返回首页

SpinalHDL 上的 USB 1.0 控制器:实现

本文描述了在 SpinalHDL 上为 RISC-V SoC 实现 USB 1.0 主机控制器。涵盖物理层、协议、HID 描述符、硬件 FSM 和驱动程序。使用键盘、鼠标、游戏手柄的测试示例。

SpinalHDL USB 1.0:从协议到 FPGA
Advertisement 728x90

在 SpinalHDL 中为 RISC-V SoC 实现 USB 1.0 主机控制器

基于 RISC-V 开发可综合片上系统需要集成现代外围接口。USB 1.0 主机控制器支持连接 HID 输入设备:键盘、鼠标、游戏手柄。用 SpinalHDL 实现它,能在硬件层面处理协议,避免 PS/2 或模拟摇杆的软件妥协。

USB 物理层采用差分对 D+ 和 D-,以及 VBUS 和 GND 线。半双工传输模式将 USB 与 UART 区分开来:数据一次仅一个方向逐位发送,并严格分离主机与设备角色。

USB 1.0 物理层

电气特性定义信号电平:D+ 和 D- 的空闲状态决定速度。低速(1.5 Mbps)—— D- 高电平,全速(12 Mbps)—— D+ 高电平。NRZI 编码:信号跳变表示 '1',无跳变表示 '0'。同步通过位填充实现:连续六个相同位后插入反转位。

Google AdInline article slot

控制信号:

  • SE0 (Single Ended Zero):D+/D- 均低——用于总线复位。
  • J/K 状态:用于空闲和速度的差分电平。
  • Chirp:K/J 交换以检测集线器速度。

传输以 SYNC(8 位 '0')开始,后跟 PID(Packet ID)。

USB 协议:数据包和事务

PID 定义数据包类型:

Google AdInline article slot
  • 令牌:SETUP(控制请求)、IN(读)、OUT(写)、SOF(帧开始)。
  • 数据:DATA0/1(用于完整性的切换位)。
  • 握手:ACK(确认)、NAK(拒绝)、STALL(错误)。

事务:

  • 控制传输:SETUP + DATA(可选)+ IN/OUT + ACK。
  • 中断传输:IN/OUT + DATA + ACK——用于 HID 报告。
  • 批量传输:大块数据,支持重试。
  • 等时传输:无握手,用于流数据。

校验和:令牌使用 CRC5,数据使用 CRC16。公式:

// CRC5 for address/kontsa
crc5_next = crc5_reg ^ data_in;
// Polinom: x^5 + x^2 + 1 (0b001101)

描述符和设备初始化

描述符描述设备:

Google AdInline article slot
  • 设备描述符:bLength=18, bDescriptorType=1, idVendor, idProduct。
  • 配置描述符:bConfigurationValue, maxPower。
  • 接口/端点:bInterfaceClass(HID=3), bEndpointAddress, bmAttributes(中断)。
  • HID 描述符:bcdHID, bCountryCode, Report Descriptor。

初始化:

  • 总线复位(SE0 >10 μs)。
  • GET_DESCRIPTOR(设备)。
  • SET_ADDRESS。
  • GET_DESCRIPTOR(配置)。
  • SET_CONFIGURATION。

对于 HID,额外 GET_DESCRIPTOR(报告)。键盘报告示例:修饰符字节 + 保留 + 6 个键码。

在 SpinalHDL 中的硬件实现

控制器构建为有限状态机层次结构:

  • USBMain:主 FSM,管理序列。
  • USBSendSE0:生成 SE0 用于复位。
  • USBSendShortToken:短令牌(SOF)。
  • USBSendLongToken:完整令牌(地址+端点)。
  • USBSendData:NRZI 编码 + 位填充。
  • USBReceiver:NRZI 解码,CRC 检查。

接口:

import spinal.core._
case class USB_IO() extends Bundle {
  val dpp = in Bool()
  val dpn = in Bool()
  val txen = out Bool()
  val txdp = out Bool()
  val txdn = out Bool()
}

Apb3USB10Ctrl 通过 APB3 集成到 SoC 中。时钟:全速 48 MHz。

集成到 KarnixSoC 和驱动程序

连接:Karna 板上的 USB Type A,信号从 FPGA 路由。SpinalHDL 配置将模块添加到顶层。

RISC-V 的 C 驱动:

  • usb10_bus_reset():SE0 持续 20 μs。
  • usb10_in_request():IN 令牌 + DATA1 + ACK。
  • usb10_setup_request():SETUP + DATA + IN。

初始化:usb10_init() 检测设备、读取描述符、设置地址。

使用 HID 设备测试

应用程序 karnix_usb10_test:

  • 游戏手柄:模拟轴、按钮在中断端点 1。
  • 鼠标:X/Y 增量、按钮在 4 字节报告中。
  • 键盘:引导协议,8 字节报告(修饰符 + 键)。

集成到 TetRISC-V:用 USB 输入替换 PS/2。

关键点:

  • 无 MACRO-IP,用 FSM 实现 USB 1.0 全速(12 Mbps)。
  • SpinalHDL 生成 FPGA 综合 RTL。
  • HID 引导协议简化键盘/鼠标驱动。
  • NRZI + 位填充需要精确时序(48 MHz 时钟)。
  • CRC5/16 验证防止数据包错误。

— Editorial Team

Advertisement 728x90

继续阅读