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))
}