USB OHCI
Introduction
There is a USB OHCi controller (host) in the SpinalHDL library. In a few bullet points it can be resumed to :
It follow the OpenHCI Open Host Controller Interface Specification for USB specification (OHCI).
It is compatible with the upstream linux / uboot OHCI drivers already. (there is also a OHCI driver on tinyUSB)
This provide USB host full speed and low speed capabilities (12Mbps and 1.5Mbps)
Tested on linux and uboot
One controller can host multiple ports (up to 16)
Bmb memory interface for DMA accesses
Bmb memory interace for the configuration
Require a clock for the internal phy which is a multiple of 12 Mhz at least 48 Mhz
The controller frequency is not restricted
No external phy required
Devices tested and functional :
Mass storage (~8 Mbps on ArtyA7 linux)
Keyboard / Mouse
Audio output
Hub
Limitations :
Some USB hub (had one so far) do not like having a full speed host with low speed devices attached.
Some modern devices will not work on USB full speed (ex : Gbps ethernet adapter)
Require memory coherency with the CPU (or the cpu need to flush his data cache in the driver)
Deployments :
Usage
import spinal.core._
import spinal.core.sim._
import spinal.lib.bus.bmb._
import spinal.lib.bus.bmb.sim._
import spinal.lib.bus.misc.SizeMapping
import spinal.lib.com.usb.ohci._
import spinal.lib.com.usb.phy.UsbHubLsFs.CtrlCc
import spinal.lib.com.usb.phy._
class UsbOhciTop(val p : UsbOhciParameter) extends Component {
val ohci = UsbOhci(p, BmbParameter(
addressWidth = 12,
dataWidth = 32,
sourceWidth = 0,
contextWidth = 0,
lengthWidth = 2
))
val phyCd = ClockDomain.external("phyCd", frequency = FixedFrequency(48 MHz))
val phy = phyCd(UsbLsFsPhy(p.portCount, sim=true))
val phyCc = CtrlCc(p.portCount, ClockDomain.current, phyCd)
phyCc.input <> ohci.io.phy
phyCc.output <> phy.io.ctrl
// propagate io signals
val irq = ohci.io.interrupt.toIo
val ctrl = ohci.io.ctrl.toIo
val dma = ohci.io.dma.toIo
val usb = phy.io.usb.toIo
val management = phy.io.management.toIo
}
object UsbHostGen extends App{
val p = UsbOhciParameter(
noPowerSwitching = true,
powerSwitchingMode = true,
noOverCurrentProtection = true,
powerOnToPowerGoodTime = 10,
dataWidth = 64, //DMA data width, up to 128
portsConfig = List.fill(4)(OhciPortParameter()) //4 Ports
)
SpinalVerilog(new UsbOhciTop(p))
}