soc/cores: add usb_fifo with FT245 USB FIFO PHY from LiteUSB, deprecate LiteUSB
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 24 Jun 2019 08:58:36 +0000 (10:58 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 24 Jun 2019 08:58:36 +0000 (10:58 +0200)
LiteUSB was not up to date was not a real USB PHY but was just providing USB FIFO PHYs.
New true USB cores are now available: Daisho, ValentyUSB, so it's better using
then for true USB support. We only keep the FT245 FIFO PHY in LiteX that can be
useful to interface with USB2/USB3 USB FIFOs.

litex/soc/cores/usb_fifo.py [new file with mode: 0644]
litex_setup.py

diff --git a/litex/soc/cores/usb_fifo.py b/litex/soc/cores/usb_fifo.py
new file mode 100644 (file)
index 0000000..d818870
--- /dev/null
@@ -0,0 +1,338 @@
+# This file is Copyright (c) 2015-2018 Florent Kermarrec <florent@enjoy-digital.fr>
+# License: BSD
+
+import math
+
+from migen import *
+from migen.fhdl.specials import Tristate
+from migen.genlib.cdc import MultiReg
+
+from litex.soc.interconnect import stream
+
+
+def phy_description(dw):
+    payload_layout = [("data", dw)]
+    return stream.EndpointDescription(payload_layout)
+
+
+def anti_starvation(module, timeout):
+        en = Signal()
+        max_time = Signal()
+        if timeout:
+            t = timeout - 1
+            time = Signal(max=t+1)
+            module.comb += max_time.eq(time == 0)
+            module.sync += If(~en,
+                    time.eq(t)
+                ).Elif(~max_time,
+                    time.eq(time - 1)
+                )
+        else:
+            module.comb += max_time.eq(0)
+        return en, max_time
+
+
+class FT245PHYSynchronous(Module):
+    def __init__(self, pads, clk_freq,
+                 fifo_depth=32,
+                 read_time=128,
+                 write_time=128):
+        dw = len(pads.data)
+
+        # read fifo (FTDI --> SoC)
+        read_fifo = ClockDomainsRenamer({"write": "usb", "read": "sys"})(stream.AsyncFIFO(phy_description(dw), fifo_depth))
+        read_buffer = ClockDomainsRenamer("usb")(stream.SyncFIFO(phy_description(dw), 4))
+        self.comb += read_buffer.source.connect(read_fifo.sink)
+
+        # write fifo (SoC --> FTDI)
+        write_fifo = ClockDomainsRenamer({"write": "sys", "read": "usb"})(stream.AsyncFIFO(phy_description(dw), fifo_depth))
+
+        self.submodules += read_fifo, read_buffer, write_fifo
+
+        # sink / source interfaces
+        self.sink = write_fifo.sink
+        self.source = read_fifo.source
+
+        # read / write arbitration
+        wants_write = Signal()
+        wants_read = Signal()
+
+        txe_n = Signal()
+        rxf_n = Signal()
+
+        self.comb += [
+            txe_n.eq(pads.txe_n),
+            rxf_n.eq(pads.rxf_n),
+            wants_write.eq(~txe_n & write_fifo.source.valid),
+            wants_read.eq(~rxf_n & read_fifo.sink.ready),
+        ]
+
+        read_time_en, max_read_time = anti_starvation(self, read_time)
+        write_time_en, max_write_time = anti_starvation(self, write_time)
+
+        data_w_accepted = Signal(reset=1)
+
+        fsm = FSM(reset_state="READ")
+        self.submodules += ClockDomainsRenamer("usb")(fsm)
+
+        fsm.act("READ",
+            read_time_en.eq(1),
+            If(wants_write,
+                If(~wants_read | max_read_time,
+                    NextState("RTW")
+                )
+            )
+        )
+        fsm.act("RTW",
+            NextState("WRITE")
+        )
+        fsm.act("WRITE",
+            write_time_en.eq(1),
+            If(wants_read,
+                If(~wants_write | max_write_time,
+                    NextState("WTR")
+                )
+            ),
+            write_fifo.source.ready.eq(wants_write & data_w_accepted)
+        )
+        fsm.act("WTR",
+            NextState("READ")
+        )
+
+        # databus tristate
+        data_w = Signal(dw)
+        data_r = Signal(dw)
+        data_oe = Signal()
+        self.specials += Tristate(pads.data, data_w, data_oe, data_r)
+
+        # read / write actions
+        pads.oe_n.reset = 1
+        pads.rd_n.reset = 1
+        pads.wr_n.reset = 1
+
+        self.sync.usb += [
+            If(fsm.ongoing("READ"),
+                data_oe.eq(0),
+
+                pads.oe_n.eq(0),
+                pads.rd_n.eq(~wants_read),
+                pads.wr_n.eq(1)
+
+            ).Elif(fsm.ongoing("WRITE"),
+                data_oe.eq(1),
+
+                pads.oe_n.eq(1),
+                pads.rd_n.eq(1),
+                pads.wr_n.eq(~wants_write),
+
+                data_w_accepted.eq(~txe_n)
+
+            ).Else(
+                data_oe.eq(1),
+
+                pads.oe_n.eq(~fsm.ongoing("WTR")),
+                pads.rd_n.eq(1),
+                pads.wr_n.eq(1)
+            ),
+                read_buffer.sink.valid.eq(~pads.rd_n & ~rxf_n),
+                read_buffer.sink.data.eq(data_r),
+                If(~txe_n & data_w_accepted,
+                    data_w.eq(write_fifo.source.data)
+                )
+        ]
+
+
+class FT245PHYAsynchronous(Module):
+    def __init__(self, pads, clk_freq,
+                 fifo_depth=32,
+                 read_time=128,
+                 write_time=128):
+        dw = len(pads.data)
+        self.clk_freq = clk_freq
+
+        # timings
+        tRD          = self.ns(30) # RD# active pulse width (t4)
+        tRDDataSetup = self.ns(14) # RD# to DATA (t3)
+        tWRDataSetup = self.ns(5)  # DATA to WR# active setup time (t8)
+        tWR          = self.ns(30) # WR# active pulse width (t10)
+        tMultiReg    = 2
+
+        # read fifo (FTDI --> SoC)
+        read_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth)
+
+        # write fifo (SoC --> FTDI)
+        write_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth)
+
+        self.submodules += read_fifo, write_fifo
+
+        # sink / source interfaces
+        self.sink = write_fifo.sink
+        self.source = read_fifo.source
+
+        # read / write arbitration
+        wants_write = Signal()
+        wants_read = Signal()
+
+        txe_n = Signal()
+        rxf_n = Signal()
+
+        self.specials += [
+            MultiReg(pads.txe_n, txe_n),
+            MultiReg(pads.rxf_n, rxf_n)
+        ]
+
+        self.comb += [
+            wants_write.eq(~txe_n & write_fifo.source.valid),
+            wants_read.eq(~rxf_n & read_fifo.sink.ready),
+        ]
+
+        read_time_en, max_read_time = anti_starvation(self, read_time)
+        write_time_en, max_write_time = anti_starvation(self, write_time)
+
+        fsm = FSM(reset_state="READ")
+        self.submodules += fsm
+
+        read_done = Signal()
+        write_done = Signal()
+        commuting = Signal()
+
+        fsm.act("READ",
+            read_time_en.eq(1),
+            If(wants_write & read_done,
+                If(~wants_read | max_read_time,
+                    commuting.eq(1),
+                    NextState("RTW")
+                )
+            )
+        )
+        fsm.act("RTW",
+            NextState("WRITE")
+        )
+        fsm.act("WRITE",
+            write_time_en.eq(1),
+            If(wants_read & write_done,
+                If(~wants_write | max_write_time,
+                    commuting.eq(1),
+                    NextState("WTR")
+                )
+            )
+        )
+        fsm.act("WTR",
+            NextState("READ")
+        )
+
+        # databus tristate
+        data_w = Signal(dw)
+        data_r_async = Signal(dw)
+        data_r = Signal(dw)
+        data_oe = Signal()
+        self.specials += [
+            Tristate(pads.data, data_w, data_oe, data_r_async),
+            MultiReg(data_r_async, data_r)
+        ]
+
+
+        # read actions
+        pads.rd_n.reset = 1
+
+        read_fsm = FSM(reset_state="IDLE")
+        self.submodules += read_fsm
+        read_counter = Signal(8)
+        read_counter_reset = Signal()
+        read_counter_ce = Signal()
+        self.sync += \
+            If(read_counter_reset,
+                read_counter.eq(0)
+            ).Elif(read_counter_ce,
+                read_counter.eq(read_counter + 1)
+            )
+
+        read_fsm.act("IDLE",
+            read_done.eq(1),
+            read_counter_reset.eq(1),
+            If(fsm.ongoing("READ") & wants_read,
+                If(~commuting,
+                    NextState("PULSE_RD_N")
+                )
+            )
+        )
+        read_fsm.act("PULSE_RD_N",
+            pads.rd_n.eq(0),
+            read_counter_ce.eq(1),
+            If(read_counter == max((tRD-1), (tRDDataSetup + tMultiReg -1)),
+                NextState("ACQUIRE_DATA")
+            )
+        )
+        read_fsm.act("ACQUIRE_DATA",
+            read_fifo.sink.valid.eq(1),
+            read_fifo.sink.data.eq(data_r),
+            NextState("WAIT_RXF_N")
+        )
+        read_fsm.act("WAIT_RXF_N",
+            If(rxf_n,
+                NextState("IDLE")
+            )
+        )
+
+        # write actions
+        pads.wr_n.reset = 1
+
+        write_fsm = FSM(reset_state="IDLE")
+        self.submodules += write_fsm
+        write_counter = Signal(8)
+        write_counter_reset = Signal()
+        write_counter_ce = Signal()
+        self.sync += \
+            If(write_counter_reset,
+                write_counter.eq(0)
+            ).Elif(write_counter_ce,
+                write_counter.eq(write_counter + 1)
+            )
+
+        write_fsm.act("IDLE",
+            write_done.eq(1),
+            write_counter_reset.eq(1),
+            If(fsm.ongoing("WRITE") & wants_write,
+                If(~commuting,
+                    NextState("SET_DATA")
+                )
+            )
+        )
+        write_fsm.act("SET_DATA",
+            data_oe.eq(1),
+            data_w.eq(write_fifo.source.data),
+            write_counter_ce.eq(1),
+            If(write_counter == (tWRDataSetup-1),
+                write_counter_reset.eq(1),
+                NextState("PULSE_WR_N")
+            )
+        )
+        write_fsm.act("PULSE_WR_N",
+            data_oe.eq(1),
+            data_w.eq(write_fifo.source.data),
+            pads.wr_n.eq(0),
+            write_counter_ce.eq(1),
+            If(write_counter == (tWR-1),
+                NextState("WAIT_TXE_N")
+            )
+        )
+        write_fsm.act("WAIT_TXE_N",
+            If(txe_n,
+                write_fifo.source.ready.eq(1),
+                NextState("IDLE")
+            )
+        )
+
+    def ns(self, t, margin=True):
+        clk_period_ns = 1000000000/self.clk_freq
+        if margin:
+            t += clk_period_ns/2
+        return math.ceil(t/clk_period_ns)
+
+
+def FT245PHY(pads, *args, **kwargs):
+    # autodetect PHY
+    if hasattr(pads, "oe_n"):
+        return FT245PHYSynchronous(pads, *args, **kwargs)
+    else:
+        return FT245PHYAsynchronous(pads, *args, **kwargs)
index 609aa0f3ac815b61cf5c76dd6f34c42ca70da0a9..45f0ad47c4f33a5caae69a5572f18434160ef719 100755 (executable)
@@ -12,7 +12,6 @@ repos = [
     ("migen",      ("http://github.com/m-labs/",        True,  True)),
     ("litex",      ("http://github.com/enjoy-digital/", True,  True)),
     ("liteeth",    ("http://github.com/enjoy-digital/", False, True)),
-    ("liteusb",    ("http://github.com/enjoy-digital/", False, True)),
     ("litedram",   ("http://github.com/enjoy-digital/", False, True)),
     ("litepcie",   ("http://github.com/enjoy-digital/", False, True)),
     ("litesata",   ("http://github.com/enjoy-digital/", False, True)),