Source code for hwtLib.peripheral.usb.usb2.ulpi

from hwt.code import Concat
from hwt.hdl.types.bits import Bits
from hwt.hdl.types.defs import BIT
from hwt.hdl.types.struct import HStruct
from hwt.interfaces.std import Signal
from hwt.interfaces.tristate import TristateSig
from hwt.synthesizer.interface import Interface
from hwt.synthesizer.param import Param
from hwtLib.peripheral.usb.constants import USB_PID
from hwtLib.peripheral.usb.usb2.utmi import utmi_function_control_t, \
    utmi_interface_control_t, utmi_otg_control_t, utmi_interrupt_t
from hwtSimApi.hdlSimulator import HdlSimulator
from ipCorePackager.constants import DIRECTION
from ipCorePackager.intfIpMeta import IntfIpMeta
from pyMathBitPrecise.bit_utils import mask


[docs]class ULPI_TX_CMD(): NOOP = 0b00_000000 """ :cvar NOOP: Transmit USB data that does not have a USB_PID, such as chirp and resume signalling. The PHY starts transmitting on the USB beginning with the next data byte. """ NOPID = 0b01_000000
[docs] @staticmethod def is_USB_PID(packet_first_byte): if isinstance(packet_first_byte, int): return (packet_first_byte >> 4) == 0b01_00 else: return packet_first_byte[:4]._eq(0b01_00)
[docs] @staticmethod def get_USB_PID(packet_first_byte): if isinstance(packet_first_byte, int): return packet_first_byte & mask(4) else: return packet_first_byte[4:0]
[docs] @staticmethod def USB_PID(pid: USB_PID): """ Transmit USB packet. data(3:0) indicates USB packet identifier USB_PID(3:0). """ if pid is None or isinstance(pid, int): pid = Bits(4).from_py(pid) else: assert pid._dtype.bit_length() == 4 return Concat(Bits(4).from_py(0b01_00), pid)
[docs] @staticmethod def REGW(addr): """ Register write command with 6-bit immediate address. """ if addr is None or isinstance(addr, int): addr = Bits(6).from_py(addr) else: assert addr._dtype.bit_length() == 6 return Concat(Bits(2).from_py(0b10), addr)
""" :cvar Extended reqister write command. 8-bit address available in the next cycle. """ EXTW = 0b10_101111 """ :cvar REGR: Register read command with 6-bit immediate address. """
[docs] @staticmethod def REGR(addr): if addr is None or isinstance(addr, int): addr = Bits(6).from_py(addr) else: assert addr._dtype.bit_length() == 6 return Concat(Bits(2).from_py(0b11), addr)
""" :cvar EXTR: Extended register read command. 8-bit address available in the next cycle. """ EXTR = 0b11_101111
# class ULPI_RX_CMD(): # # def build(self, LineState, vBusState, RxEvent, ID, alt_int):
[docs]class ULPI_REG: """ :note: all registers are 8b in size """ Vendor_ID_Low = 0x00 Vendor_ID_High = 0x01 Product_ID_Low = 0x02 Product_ID_High = 0x03 Function_Control = 0x04 Interface_Control = 0x07 OTG_Control = 0x0A USB_Interrupt_Enable_Rising = 0x0D USB_Interrupt_Enable_Falling = 0x10 USB_Interrupt_Status = 0x13 USB_Interrupt_Latch = 0x14 Debug = 0x15 Scratch_Register = 0x16 # optional registers Carkit_Control = 0x19 Carkit_Interrupt_Delay = 0x1C Carkit_Interrupt_Enable = 0x1D Carkit_Interrupt_Status = 0x20 Carkit_Interrupt_Latch = 0x21 Carkit_Pulse_Control = 0x22 Transmit_Positive_Width = 0x25 Transmit_Negative_Width = 0x26 Receive_Polarity_Recovery = 0x27 REGS_WITH_SET_AND_CLR = [ Function_Control, Interface_Control, OTG_Control, USB_Interrupt_Enable_Rising, USB_Interrupt_Enable_Falling, Scratch_Register, Carkit_Control, Carkit_Interrupt_Enable, Carkit_Pulse_Control, ]
[docs] @classmethod def set_of(addr): """ By write to this address the pattern on the data bus is OR’d with and written into the register. """ assert addr in ULPI_REG.REGS_WITH_SET_AND_CLR, addr return addr + 1
[docs] def clr_of(self, addr): """ By write to this address the pattern on the data bus is a mask. If a bit in the mask is set, then the corresponding register bit will be set to zero (cleared). """ assert addr in ULPI_REG.REGS_WITH_SET_AND_CLR, addr return addr + 2
ulpi_reg_function_control_t = utmi_function_control_t ulpi_reg_function_control_t_reset_default = { "XcvrSelect": 1, "TermSelect": 0, "OpMode": 0, "Reset": 0, "SuspendM": 1, } ulpi_reg_interface_control_t = utmi_interface_control_t ulpi_reg_otg_control_t = utmi_otg_control_t ulpi_reg_otg_control_t_reset_defaults = { "IdPullup": 0, "DpPulldown": 1, "DmPulldown": 1, "DischrgVbus": 0, "ChrgVbus": 0, "DrvVbus": 0, "DrvVbusExternal": 0, "UseExternalVbusIndicator": 0, } ulpi_reg_usb_interrupt_enable_rising_t = \ ulpi_reg_usb_interrupt_enable_falling_t = \ ulpi_reg_usb_interrupt_status_t = \ ulpi_reg_usb_interrupt_latch_t = utmi_interrupt_t ulpi_reg_usb_interrupt_status_t_reset_default = { "HostDisconnect": 0, "VbusValid": 0, "SessValid": 0, "SessEnd": 0, "IdGnd": 0, } ulpi_reg_debug_t = HStruct( (BIT, "LineState0"), (BIT, "LineState1"), (Bits(6), None), )
[docs]class Ulpi(Interface): """ ULPI (UTMI+ Low Pin Interface for USB2.0 PHY) * https://www.sparkfun.com/datasheets/Components/SMD/ULPI_v1_1.pdf :ivar ~.data: Bi-directional data bus, driven low by the Link during idle. Bus ownership is determined by dir. The Link and PHY initiate data transfers by driving a non-zero pattern onto the data bus. LPI defines interface timing for single-edge data transfers with respect to rising edge of clock. An implementation may optionally define double-edge data transfers with respect to both rising and falling edges of clock. :ivar ~.dir: Direction. Controls the direction of the data bus. When the PHY has data to transfer to the Link, it drives dir high to take ownership of the bus. When the PHY has no data to transfer it drives dir low and monitors the bus for Link activity. The PHY pulls dir high whenever the interface cannot accept data from the Link. For example, when the internal PHY PLL is not stable. :ivar ~.stp: Stop. The Link asserts this signal for 1 clock cycle to stop the data stream currently on the bus. If the Link is sending data to the PHY, stp indicates the last byte of data was on the bus in the previous cycle. If the PHY is sending data to the Link, stp forces the PHY to end its transfer, de-assert dir and relinquish control of the the data bus to the Link. (In the tx stp=1 is asserted after last word, data with stp=1 and after are interpreted as idle (until dir changes).) :ivar ~.nxt: Next. The PHY asserts this signal to throttle the data. When the Link is sending data to the PHY, nxt indicates when the current byte has been accepted by the PHY. The Link places the next byte on the data bus in the following clock cycle. When the PHY is sending data to the Link, nxt indicates when a new byte is available for the Link to consume. (In the rx nxt=0 means the transaction is paused and rx_cmd word is send instead of data.) :note: PHY is the master .. hwt-autodoc:: """
[docs] class DIR: """ :note: if dir == PHY the data flows to PHY """ PHY = 1 LINK = 0
def _config(self): self.DATA_WIDTH = Param(8) def _declr(self): # the "t" has to be driven from outside of PHY (from Link usually implemented in FPGA) self.data = TristateSig(masterDir=DIRECTION.IN) self.data.DATA_WIDTH = self.DATA_WIDTH self.dir = Signal() self.stp = Signal(masterDir=DIRECTION.IN) self.nxt = Signal()
[docs] def _getIpCoreIntfClass(self): return IP_Ulpi
[docs] def _initSimAgent(self, sim:HdlSimulator): from hwtLib.peripheral.usb.usb2.ulpi_agent import UlpiAgent self._ag = UlpiAgent(sim, self)
[docs]class IP_Ulpi(IntfIpMeta):
[docs] def __init__(self): super().__init__() self.name = "ulpi" self.version = "1.0" self.vendor = "xilinx.com" self.library = "interface" self.map = { # 'clk': "CLK", # [todo] need to reference associated clk/rst # 'rst': "RST", 'dir': 'DIR', "nxt": 'NEXT', 'stp': "STOP", 'data': { 'i': 'DATA_I', 'o': 'DATA_O', 't': 'DATA_T', }, }