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

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from hwt.code import If, Switch, Concat
from hwt.code_utils import rename_signal
from hwt.hdl.types.bits import HBits
from hwt.hdl.types.enum import HEnum
from hwt.hwIOs.utils import propagateClkRst, addClkRst
from hwt.hwModule import HwModule
from hwt.pyUtils.typingFuture import override
from hwt.synthesizer.rtlLevel.rtlSignal import RtlSignal
from hwtLib.peripheral.usb.usb2.ulpi import Ulpi, ULPI_TX_CMD, ULPI_REG, \
    ulpi_reg_function_control_t, ulpi_reg_function_control_t_reset_default, \
    ulpi_reg_otg_control_t, ulpi_reg_otg_control_t_reset_defaults, \
    ulpi_reg_usb_interrupt_status_t_reset_default
from hwtLib.peripheral.usb.usb2.utmi import Utmi_8b, utmi_interrupt_t
from pyMathBitPrecise.bit_utils import mask


[docs] class Utmi_to_Ulpi(HwModule): """ The ULPI is an interface which reduces the number of signals for UTMI+ interface. This reduction is done using a register file which drives signals which are not used and bi-directional wiring. This component does translation of ULPI to UTMI+ by keeping copy of UTMI+ registers and synchronizing the changes and it also handles the drive of the bi-directional wires. :note: For up to UTMI+ Level 3 Based on https://raw.githubusercontent.com/ultraembedded/core_ulpi_wrapper/3c202963ac4b4ae50cadb44ce79c11463d3c6484/src_v/ulpi_wrapper.v .. hwt-autodoc:: """ @override def hwDeclr(self): addClkRst(self) # PHY is a master for UTMI/ULPI style interface self.utmi = Utmi_8b()._m() self.ulpi = Ulpi()
[docs] def ulpi_turnaround_detect(self, ulpi_dir: RtlSignal): #----------------------------------------------------------------- # Bus turnaround detect #----------------------------------------------------------------- ulpi_dir_q = self._reg("ulpi_dir_q", def_val=1) ulpi_dir_q(ulpi_dir) turnaround_w = rename_signal(self, ulpi_dir_q != ulpi_dir._eq(Ulpi.DIR.PHY), "turnaround_w") return turnaround_w
[docs] @staticmethod def parse_RX_CMD(ulpi_data, utmi_linestate_q, utmi_interrupt_q, utmi_rxactive_q, utmi_rxerror_q): return [ utmi_linestate_q(ulpi_data[2:0]), Switch(ulpi_data[4:2])\ .Case(0b00, utmi_interrupt_q.SessEnd(1), utmi_interrupt_q.SessValid(0), utmi_interrupt_q.VbusValid(0), ).Case(0b01, utmi_interrupt_q.SessEnd(0), utmi_interrupt_q.SessValid(0), utmi_interrupt_q.VbusValid(0), ).Case(0b10, utmi_interrupt_q.SessValid(1), utmi_interrupt_q.VbusValid(0), ).Case(0b11, utmi_interrupt_q.VbusValid(1), ), Switch(ulpi_data[6:4])\ .Case(0b00, utmi_rxactive_q(0), utmi_rxerror_q(0) ).Case(0b01, utmi_rxactive_q(1), utmi_rxerror_q(0) ).Case(0b10, utmi_interrupt_q.HostDisconnect(1) ).Case(0b11, utmi_rxactive_q(1), utmi_rxerror_q(1) ), utmi_interrupt_q.IdGnd(ulpi_data[6]) ]
@override def hwImpl(self): ulpi: Ulpi = self.ulpi utmi: Utmi_8b = self.utmi # Description: # - Converts from UTMI interface to reduced pin count ULPI. # - No support for low power mode. # - I/O synchronous to 60MHz ULPI clock input (from PHY) # - Tested against SMSC/Microchip USB3300 in device mode. #----------------------------------------------------------------- # States #----------------------------------------------------------------- state_t = HEnum("state_t", ["w", "idle", "cmd", "data", "reg"]) state_q = self._reg("state_q", dtype=state_t, def_val=state_t.idle) #----------------------------------------------------------------- # UTMI Mode Select #----------------------------------------------------------------- # flag which tells that the function mode register is now ready writen to PHY mode_update_q = self._reg("mode_update_q", def_val=0) function_control_q = self._reg("function_control_q", ulpi_reg_function_control_t, def_val=ulpi_reg_function_control_t_reset_default) mode_write_q = self._reg("mode_write_q", def_val=0) # Detect register write completion mode_complete_w = rename_signal(self, ((state_q._eq(state_t.reg) & mode_write_q) & ulpi.nxt) & ulpi.dir._eq(Ulpi.DIR.LINK), "mode_complete_w") #----------------------------------------------------------------- # UTMI OTG Control #----------------------------------------------------------------- otg_update_q = self._reg("otg_update_q", def_val=0) otg_control_q = self._reg("otg_control_q", ulpi_reg_otg_control_t, def_val=ulpi_reg_otg_control_t_reset_defaults) otg_write_q = self._reg("otg_write_q", def_val=0) # Detect register write completion otg_complete_w = rename_signal(self, ((state_q._eq(state_t.reg) & otg_write_q) & ulpi.nxt) & ulpi.dir._eq(Ulpi.DIR.LINK), "otg_complete_w") #----------------------------------------------------------------- # Tx Buffer - decouple UTMI Tx from PHY I/O #----------------------------------------------------------------- # tx_fifo = HandshakedFifo(Handshaked) # tx_fifo.DATA_WIDTH = 8 # tx_fifo.DEPTH = 2 # self.tx_fifo = tx_fifo #----------------------------------------------------------------- # Implementation #----------------------------------------------------------------- # Xilinx placement pragmas: # synthesis attribute IOB of ulpi_data_q is "TRUE" # synthesis attribute IOB of ulpi_stp_q is "TRUE" ulpi_data_q = self._reg("ulpi_data_q", HBits(8), def_val=0) ulpi_stp_q = self._reg("ulpi_stp_q", def_val=0) data_q = self._reg("data_q", HBits(8), def_val=0) utmi_rxvalid_q = self._reg("utmi_rxvalid_q", def_val=0) utmi_rxerror_q = self._reg("utmi_rxerror_q", def_val=0) utmi_rxactive_q = self._reg("utmi_rxactive_q", def_val=0) utmi_linestate_q = self._reg("utmi_linestate_q", HBits(2), def_val=0) utmi_data_q = self._reg("utmi_data_q", HBits(8), def_val=0) # interupts are cleared once new RX CMD is received and it does not contain the event flag utmi_interrupt_q = self._reg("utmi_interrupt_q", utmi_interrupt_t, def_val=ulpi_reg_usb_interrupt_status_t_reset_default) utmi.interrupt(utmi_interrupt_q) # Not interrupted by a Rx function_control_q(utmi.function_control, exclude=[function_control_q.Reset]) If(mode_update_q & mode_complete_w, function_control_q.Reset(0) ).Else( function_control_q.Reset(utmi.function_control.Reset) ) If(mode_update_q & mode_complete_w, mode_update_q(0), ).Elif((function_control_q != utmi.function_control) | utmi.function_control.Reset, mode_update_q(1) ) # Not interrupted by a Rx otg_control_q(utmi.otg_control) If(otg_update_q & otg_complete_w, otg_update_q(0) ).Elif(otg_control_q != utmi.otg_control, otg_update_q(1) ) turnaround_w = self.ulpi_turnaround_detect(ulpi.dir) # utmi_tx_to_ulpi_vld = tx_fifo.dataOut.vld # Push # tx_fifo.dataIn.vld(utmi.tx.vld & utmi.tx.rd) # tx_fifo.dataIn.data(utmi.tx.data) # Pop # tx_fifo.dataOut.rd(utmi_tx_to_ulpi_vld & utmi_tx_accept_w) # utmi.tx.rd(tx_fifo.dataIn.rd & tx_delay_complete_w) utmi_tx_to_ulpi_vld = utmi.tx.vld utmi_tx_data_w = utmi.tx.data utmi_tx_accept_w = rename_signal( self, ~turnaround_w & ulpi.dir._eq(Ulpi.DIR.LINK) & ( (state_q._eq(state_t.idle) & ~(mode_update_q | otg_update_q | turnaround_w)) | (state_q._eq(state_t.data) & ulpi.nxt) ), "utmi_tx_accept_w") utmi.tx.rd(utmi_tx_accept_w) ulpi_stp_q(~turnaround_w & ulpi.dir._eq(Ulpi.DIR.LINK) & ulpi.nxt & (state_q._eq(state_t.reg) | (state_q._eq(state_t.data) & ~utmi_tx_to_ulpi_vld) ) ) utmi_rxvalid_q(~turnaround_w & ulpi.dir._eq(Ulpi.DIR.PHY) & ulpi.nxt) If(turnaround_w, If(ulpi.dir._eq(Ulpi.DIR.PHY) & ulpi.nxt, # Turnaround: Input + NXT - set RX_ACTIVE utmi_rxactive_q(1), # Register write - abort If(state_q._eq(state_t.reg), state_q(state_t.idle), ulpi_data_q(0), ) ).Elif(ulpi.dir._eq(Ulpi.DIR.LINK), utmi_rxactive_q(0), # Register write - abort If(state_q._eq(state_t.reg), state_q(state_t.idle), ulpi_data_q(0), ) ) ).Else( If(ulpi.dir._eq(Ulpi.DIR.PHY), If(ulpi.nxt, #----------------------------------------------------------------- # Input: RX_DATA #----------------------------------------------------------------- utmi_rxactive_q(1), utmi_data_q(ulpi.data.i) ).Else( #----------------------------------------------------------------- # Input: RX_CMD (phy status), decode encoded status/event bits from this byte #----------------------------------------------------------------- self.parse_RX_CMD(ulpi.data.i, utmi_linestate_q, utmi_interrupt_q, utmi_rxactive_q, utmi_rxerror_q) ) ).Else( #----------------------------------------------------------------- # Output #----------------------------------------------------------------- If(state_q._eq(state_t.idle), If(mode_update_q, # IDLE: Pending mode update state_q(state_t.cmd), ulpi_data_q(ULPI_TX_CMD.REGW(ULPI_REG.Function_Control)), data_q(function_control_q._reinterpret_cast(data_q._dtype) & mask(7)), otg_write_q(0), mode_write_q(1), ).Elif(otg_update_q, # IDLE: Pending OTG control update state_q(state_t.cmd), ulpi_data_q(ULPI_TX_CMD.REGW(ULPI_REG.OTG_Control)), data_q(otg_control_q._reinterpret_cast(data_q._dtype)), otg_write_q(1), mode_write_q(0), ).Elif(utmi_tx_to_ulpi_vld, # IDLE: Pending transmit # data should have USB_PID header and this is just to be sure ulpi_data_q(ULPI_TX_CMD.USB_PID(utmi_tx_data_w[4:0])), state_q(state_t.data) ) ).Elif(ulpi.nxt, If(state_q._eq(state_t.cmd), # Command, Write Register state_q(state_t.reg), ulpi_data_q(data_q), ).Elif(state_q._eq(state_t.reg), # Data (register write) state_q(state_t.idle), ulpi_data_q(0), otg_write_q(0), mode_write_q(0), ).Elif(state_q._eq(state_t.data), # Data If(utmi_tx_to_ulpi_vld, state_q(state_t.data), ulpi_data_q(utmi_tx_data_w), ).Else( # End of packet state_q(state_t.idle), ulpi_data_q(0), ) ) ) ) ) ulpi.data.o(ulpi_data_q) ulpi.data.t(Concat(*(utmi_tx_accept_w for _ in range(8)))) ulpi.stp(ulpi_stp_q) utmi.LineState(utmi_linestate_q) utmi.rx.data(utmi_data_q) utmi.rx.error(utmi_rxerror_q) utmi.rx.active(utmi_rxactive_q) utmi.rx.valid(utmi_rxvalid_q) propagateClkRst(self)
if __name__ == "__main__": from hwt.synth import to_rtl_str m = Utmi_to_Ulpi() print(to_rtl_str(m))