liteusb: continue refactoring (virtual UART and DMA working on minispartan6)
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Fri, 1 May 2015 14:11:15 +0000 (16:11 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Fri, 1 May 2015 14:11:15 +0000 (16:11 +0200)
- rename ft2232h phy to ft245.
- make crc optional
- fix depacketizer
- refactor uart (it's now only a wrapper around standard UART)
- fix and update dma

13 files changed:
misoclib/com/liteusb/common.py
misoclib/com/liteusb/core/__init__.py
misoclib/com/liteusb/core/crossbar.py
misoclib/com/liteusb/core/packet.py
misoclib/com/liteusb/frontend/dma.py
misoclib/com/liteusb/frontend/uart.py
misoclib/com/liteusb/phy/ft2232h.py [deleted file]
misoclib/com/liteusb/phy/ft245.py [new file with mode: 0644]
misoclib/com/liteusb/test/Makefile
misoclib/com/liteusb/test/ft2232h_async_tb.py [deleted file]
misoclib/com/liteusb/test/ft2232h_sync_tb.py [deleted file]
misoclib/com/liteusb/test/ft245_async_tb.py [new file with mode: 0644]
misoclib/com/liteusb/test/ft245_sync_tb.py [new file with mode: 0644]

index df3d748291e0728a07c774e44dce70417def1f57..2cbf00e62bfe001453f601d1508e835f9f4e4369 100644 (file)
@@ -3,6 +3,7 @@ from migen.genlib.fsm import *
 from migen.actorlib.fifo import *
 from migen.flow.actor import EndpointDescription
 from migen.actorlib.packet import *
+from migen.actorlib.structuring import Pipeline
 
 
 packet_header_length = 9
@@ -49,11 +50,12 @@ class LiteUSBMasterPort:
 
 
 class LiteUSBSlavePort:
-    def __init__(self, dw):
+    def __init__(self, dw, tag):
         self.sink = Sink(user_description(dw))
         self.source = Source(user_description(dw))
+        self.tag = tag
 
 
 class LiteUSBUserPort(LiteUSBSlavePort):
-    def __init__(self, dw):
-        LiteUSBSlavePort.__init__(self, dw)
+    def __init__(self, dw, tag):
+        LiteUSBSlavePort.__init__(self, dw, tag)
index b416a983b606034f41e287d8f2a995d4fdaada4b..9ecd835cbda9d5985353866b3ac316221cf03b60 100644 (file)
@@ -3,29 +3,29 @@ from misoclib.com.liteusb.core.packet import LiteUSBPacketizer, LiteUSBDepacketi
 from misoclib.com.liteusb.core.crc import LiteUSBCRC32Inserter, LiteUSBCRC32Checker
 from misoclib.com.liteusb.core.crossbar import LiteUSBCrossbar
 
-# XXX Header should be protected by CRC
-
 class LiteUSBCore(Module):
-    def __init__(self, phy):
+    def __init__(self, phy, clk_freq, with_crc=True):
+        rx_pipeline = [phy]
+        tx_pipeline = [phy]
+
         # depacketizer / packetizer
-        self.submodules.depacketizer = LiteUSBDepacketizer()
+        self.submodules.depacketizer = LiteUSBDepacketizer(clk_freq)
         self.submodules.packetizer = LiteUSBPacketizer()
-        self.comb += [
-            Record.connect(phy.source, self.depacketizer.sink),
-            Record.connect(self.packetizer.source, phy.sink)
-        ]
+        rx_pipeline += [self.depacketizer]
+        tx_pipeline += [self.packetizer]
 
-        # crc checker / inserter
-        self.submodules.crc_rx = LiteUSBCRC32Checker()
-        self.submodules.crc_tx = LiteUSBCRC32Inserter()
-        self.comb += [
-            Record.connect(self.depacketizer.source, self.crc_rx.sink),
-            Record.connect(self.crc_tx.source, self.packetizer.sink)
-        ]
+        if with_crc:
+            # crc checker / inserter
+            self.submodules.crc_rx = LiteUSBCRC32Checker()
+            self.submodules.crc_tx = LiteUSBCRC32Inserter()
+            rx_pipeline += [self.crc_rx]
+            tx_pipeline += [self.crc_tx]
 
-       # crossbar
+        # crossbar
         self.submodules.crossbar = LiteUSBCrossbar()
-        self.comb += [
-            Record.connect(self.crossbar.master.source, self.crc_tx.sink),
-            Record.connect(self.crc_rx.source, self.crossbar.master.sink)
-        ]
+        rx_pipeline += [self.crossbar.master]
+        tx_pipeline += [self.crossbar.master]
+
+        # graph
+        self.submodules.rx_pipeline = Pipeline(*rx_pipeline)
+        self.submodules.tx_pipeline = Pipeline(*reversed(tx_pipeline))
index e342712eba2df5a556c723386dae25bf149c7b5b..59440df5b77a22f5aca4e90fb4f19d023fe45fc5 100644 (file)
@@ -9,7 +9,7 @@ class LiteUSBCrossbar(Module):
         self.dispatch_param = "dst"
 
     def get_port(self, dst):
-        port = LiteUSBUserPort(8)
+        port = LiteUSBUserPort(8, dst)
         if dst in self.users.keys():
             raise ValueError("Destination {0:#x} already assigned".format(dst))
         self.users[dst] = port
index bb4c28ad4f104f2c07b315f93b3b61ac2462d7c9..43c97b014ca6d9670b5e92204b65fc9e15948287 100644 (file)
@@ -66,7 +66,7 @@ class LiteUSBPacketizer(Module):
 
 
 class LiteUSBDepacketizer(Module):
-    def __init__(self, timeout=10):
+    def __init__(self, clk_freq, timeout=10):
         self.sink = sink = Sink(phy_description(8))
         self.source = source = Source(user_description(8))
 
@@ -116,9 +116,9 @@ class LiteUSBDepacketizer(Module):
             header_pack.source.ack.eq(1),
         )
 
-        self.submodules.timeout = Timeout(60000000*timeout) #XXX use clk_freq
+        self.submodules.timeout = Timeout(clk_freq*timeout)
         self.comb += [
-            self.timeout.reset.eq(fsm.ongoing("WAIT_SOP")),
+            self.timeout.reset.eq(fsm.ongoing("IDLE")),
             self.timeout.ce.eq(1)
         ]
 
index a9a967012e0b5b1ec8a742e6d16520ab04f7ec63..79f7ab67fb65b0b87b7d0577a6fa9a46c2c5216e 100644 (file)
@@ -16,7 +16,7 @@ class LiteUSBDMAWriter(Module, AutoCSR):
 
         # Pack data
         pack_factor = lasmim.dw//8
-        pack = structuring.Pack(phy_layout, pack_factor, reverse=True)
+        pack = structuring.Pack(phy_description(8), pack_factor, reverse=True)
         cast = structuring.Cast(pack.source.payload.layout, lasmim.dw)
 
         # DMA
@@ -61,12 +61,12 @@ class LiteUSBDMAReader(Module, AutoCSR):
         pack_factor = lasmim.dw//8
         packed_dat = structuring.pack_layout(8, pack_factor)
         cast = structuring.Cast(lasmim.dw, packed_dat)
-        unpack = structuring.Unpack(pack_factor, phy_layout, reverse=True)
+        unpack = structuring.Unpack(pack_factor, phy_description(8), reverse=True)
 
         # Graph
         cnt = Signal(32)
         self.sync += \
-            If(self.dma.generator._r_shoot.re,
+            If(self.dma.generator._shoot.re,
                 cnt.eq(0)
             ).Elif(source.stb & source.ack,
                 cnt.eq(cnt + 1)
@@ -92,12 +92,11 @@ class LiteUSBDMAReader(Module, AutoCSR):
 
 
 class LiteUSBDMA(Module, AutoCSR):
-    def __init__(self, lasmim_dma_wr, lasmim_dma_rd, tag):
-        self.tag = tag
-
+    def __init__(self, port, lasmim_dma_wr, lasmim_dma_rd):
         self.submodules.writer = LiteUSBDMAWriter(lasmim_dma_wr)
-        self.submodules.reader = LiteUSBDMAReader(lasmim_dma_rd, self.tag)
+        self.submodules.reader = LiteUSBDMAReader(lasmim_dma_rd, port.tag)
         self.submodules.ev = SharedIRQ(self.writer.ev, self.reader.ev)
-
-        self.sink = self.writer.sink
-        self.source = self.reader.source
+        self.comb += [
+            Record.connect(port.source, self.writer.sink),
+            Record.connect(self.reader.source, port.sink),
+        ]
index 6e551f00e7f6cd8ec0838ba3497c1d076b8a5546..648d676398ea0b8bfc6d05e6bdd3eb4b3991b0c6 100644 (file)
@@ -1,59 +1,35 @@
 from migen.fhdl.std import *
-from migen.bank.description import *
-from migen.bank.eventmanager import *
-from migen.actorlib.fifo import SyncFIFO
 
 from misoclib.com.liteusb.common import *
+from misoclib.com.uart import UART
 
+class LiteUSBUARTPHY:
+    def __init__(self):
+        self.sink = Sink([("data", 8)])
+        self.source = Source([("data", 8)])
 
-class LiteUSBUART(Module, AutoCSR):
-    def __init__(self, tag, fifo_depth=64):
-        self.tag = tag
+class LiteUSBUART(UART):
+    def __init__(self, port,
+                 tx_fifo_depth=16,
+                 rx_fifo_depth=16):
 
-        self._rxtx = CSR(8)
-
-        self.submodules.ev = EventManager()
-        self.ev.tx = EventSourcePulse()
-        self.ev.rx = EventSourceLevel()
-        self.ev.finalize()
-
-        self.source = source = Source(user_description(8))
-        self.sink = sink = Sink(user_description(8))
-
-        # # #
+        phy = LiteUSBUARTPHY()
+        UART.__init__(self, phy, tx_fifo_depth, rx_fifo_depth)
 
         # TX
-        tx_start = self._rxtx.re
-        tx_done = self.ev.tx.trigger
-
-        self.sync += \
-            If(tx_start,
-                source.stb.eq(1),
-                source.data.eq(self._rxtx.r),
-            ).Elif(tx_done,
-                source.stb.eq(0)
-            )
-
         self.comb += [
-            source.sop.eq(1),
-            source.eop.eq(1),
-            source.length.eq(1),
-            source.dst.eq(self.tag),
-            tx_done.eq(source.stb & source.ack),
+            port.sink.stb.eq(phy.sink.stb),
+            port.sink.sop.eq(1),
+            port.sink.eop.eq(1),
+            port.sink.length.eq(1),
+            port.sink.dst.eq(port.tag),
+            port.sink.data.eq(phy.sink.data),
+            phy.sink.ack.eq(port.sink.ack)
         ]
 
         # RX
-        rx_available = self.ev.rx.trigger
-
-        rx_fifo = SyncFIFO(8, fifo_depth)
-        self.submodules += rx_fifo
         self.comb += [
-            Record.connect(sink, rx_fifo.sink),
-
-            rx_fifo.we.eq(sink.stb),
-            sink.ack.eq(sink.stb & rx_fifo.writable),
-            rx_fifo.din.eq(sink.data),
-            rx_available.eq(rx_fifo.stb),
-            rx_fifo.ack.eq(self.ev.rx.clear),
-            self._rxtx.w.eq(rx_fifo.dout)
+            phy.source.stb.eq(port.source.stb),
+            phy.source.data.eq(port.source.data),
+            port.source.ack.eq(phy.source.ack)
         ]
diff --git a/misoclib/com/liteusb/phy/ft2232h.py b/misoclib/com/liteusb/phy/ft2232h.py
deleted file mode 100644 (file)
index 92b4219..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-from migen.fhdl.std import *
-from migen.flow.actor import *
-from migen.actorlib.fifo import SyncFIFO, AsyncFIFO
-from migen.fhdl.specials import *
-from migen.genlib.cdc import MultiReg
-
-from misoclib.com.liteusb.common import *
-
-
-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 FT2232HPHYSynchronous(Module):
-    def __init__(self, pads,
-                 fifo_depth=32,
-                 read_time=16,
-                 write_time=16):
-        dw = flen(pads.data)
-
-        #
-        # Read / Write Fifos
-        #
-
-        # Read Fifo (Ftdi --> SoC)
-        read_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
-            {"write": "ftdi", "read": "sys"})
-        read_buffer = RenameClockDomains(SyncFIFO(phy_description(8), 4),
-            {"sys": "ftdi"})
-        self.comb += read_buffer.source.connect(read_fifo.sink)
-
-        # Write Fifo (SoC --> Ftdi)
-        write_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
-            {"write": "sys", "read": "ftdi"})
-
-        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.stb),
-            wants_read.eq(~rxf_n & read_fifo.sink.ack),
-        ]
-
-        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 += RenameClockDomains(fsm, {"sys": "ftdi"})
-
-        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.ack.eq(wants_write & data_w_accepted)
-        )
-        fsm.act("WTR",
-            NextState("READ")
-        )
-
-        #
-        # Read / Write Actions
-        #
-
-        data_w = Signal(dw)
-        data_r = Signal(dw)
-        data_oe = Signal()
-
-        pads.oe_n.reset = 1
-        pads.rd_n.reset = 1
-        pads.wr_n.reset = 1
-
-        self.sync.ftdi += [
-            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.stb.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)
-                )
-        ]
-
-        #
-        # Databus Tristate
-        #
-        self.specials += Tristate(pads.data, data_w, data_oe, data_r)
-
-        self.debug = Signal(8)
-        self.comb += self.debug.eq(data_r)
-
-
-class FT2232HPHYAsynchronous(Module):
-    def __init__(self, pads, clk_freq,
-                 fifo_depth=32,
-                 read_time=16,
-                 write_time=16):
-        dw = flen(pads.data)
-
-        #
-        # Read / Write Fifos
-        #
-
-        # Read Fifo (Ftdi --> SoC)
-        read_fifo = SyncFIFO(phy_description(8), fifo_depth)
-
-        # Write Fifo (SoC --> Ftdi)
-        write_fifo = SyncFIFO(phy_description(8), 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.stb),
-            wants_read.eq(~rxf_n & read_fifo.sink.ack),
-        ]
-
-        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 / Write Actions
-        #
-        pads.wr_n.reset = 1
-        pads.rd_n.reset = 1
-
-        read_fsm = FSM(reset_state="IDLE")
-        read_counter = Counter(8)
-        self.submodules += read_fsm, read_counter
-
-        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.value == 15, # XXX Compute exact value
-                NextState("ACQUIRE_DATA")
-            )
-        )
-        read_fsm.act("ACQUIRE_DATA",
-            read_fifo.sink.stb.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_fsm = FSM(reset_state="IDLE")
-        write_counter = Counter(8)
-        self.submodules += write_fsm, write_counter
-
-        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.value == 5, # XXX Compute exact value
-                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.value == 15, # XXX Compute exact value
-                NextState("WAIT_TXE_N")
-            )
-        )
-        write_fsm.act("WAIT_TXE_N",
-            If(txe_n,
-                write_fifo.source.ack.eq(1),
-                NextState("IDLE")
-            )
-        )
diff --git a/misoclib/com/liteusb/phy/ft245.py b/misoclib/com/liteusb/phy/ft245.py
new file mode 100644 (file)
index 0000000..bed5f5d
--- /dev/null
@@ -0,0 +1,319 @@
+import math
+
+from migen.fhdl.std import *
+from migen.flow.actor import *
+from migen.actorlib.fifo import SyncFIFO, AsyncFIFO
+from migen.fhdl.specials import *
+from migen.genlib.cdc import MultiReg
+
+from misoclib.com.liteusb.common import *
+
+
+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 = flen(pads.data)
+
+        # read fifo (FTDI --> SoC)
+        read_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
+            {"write": "ftdi", "read": "sys"})
+        read_buffer = RenameClockDomains(SyncFIFO(phy_description(8), 4),
+            {"sys": "ftdi"})
+        self.comb += read_buffer.source.connect(read_fifo.sink)
+
+        # write fifo (SoC --> FTDI)
+        write_fifo = RenameClockDomains(AsyncFIFO(phy_description(8), fifo_depth),
+            {"write": "sys", "read": "ftdi"})
+
+        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.stb),
+            wants_read.eq(~rxf_n & read_fifo.sink.ack),
+        ]
+
+        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 += RenameClockDomains(fsm, {"sys": "ftdi"})
+
+        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.ack.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.ftdi += [
+            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.stb.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 = flen(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 = SyncFIFO(phy_description(8), fifo_depth)
+
+        # write fifo (SoC --> FTDI)
+        write_fifo = SyncFIFO(phy_description(8), 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.stb),
+            wants_read.eq(~rxf_n & read_fifo.sink.ack),
+        ]
+
+        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")
+        read_counter = Counter(8)
+        self.submodules += read_fsm, read_counter
+
+        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.value == max((tRD-1), (tRDDataSetup + tMultiReg -1)),
+                NextState("ACQUIRE_DATA")
+            )
+        )
+        read_fsm.act("ACQUIRE_DATA",
+            read_fifo.sink.stb.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")
+        write_counter = Counter(8)
+        self.submodules += write_fsm, write_counter
+
+        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.value == (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.value == (tWR-1),
+                NextState("WAIT_TXE_N")
+            )
+        )
+        write_fsm.act("WAIT_TXE_N",
+            If(txe_n,
+                write_fifo.source.ack.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 8224e80985f8a47ae164067ee304b3d8aff07ee7..bf90b0741d9ee095407bdbe07af709b6770eb9bc 100644 (file)
@@ -3,11 +3,11 @@ PYTHON = python3
 
 CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
 
-ft2232h_sync_tb:
-       $(CMD) ft2232h_sync_tb.py
+ft245_sync_tb:
+       $(CMD) ft245_sync_tb.py
 
-ft2232h_async_tb:
-       $(CMD) ft2232h_async_tb.py
+ft245_async_tb:
+       $(CMD) ft245_async_tb.py
 
 core_tb:
        $(CMD) core_tb.py
diff --git a/misoclib/com/liteusb/test/ft2232h_async_tb.py b/misoclib/com/liteusb/test/ft2232h_async_tb.py
deleted file mode 100644 (file)
index 4af0a6c..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-from migen.fhdl.std import *
-from migen.flow.actor import *
-from migen.fhdl.specials import *
-
-from migen.sim.generic import run_simulation
-
-from misoclib.com.liteusb.common import *
-from misoclib.com.liteusb.phy.ft2232h import FT2232HPHYAsynchronous
-from misoclib.com.liteusb.test.common import *
-
-# XXX for now use it from liteeth to avoid duplication
-from misoclib.com.liteeth.test.common import *
-
-class FT2232HAsynchronousModel(Module, RandRun):
-    def __init__(self, rd_data):
-        RandRun.__init__(self, 10)
-        self.rd_data = [0] + rd_data
-        self.rd_idx = 0
-
-        # pads
-        self.data = Signal(8)
-        self.rxf_n = Signal(reset=1)
-        self.txe_n = Signal(reset=1)
-        self.rd_n = Signal(reset=1)
-        self.wr_n = Signal(reset=1)
-
-        self.init = True
-        self.wr_data = []
-        self.wait_wr_n = False
-        self.rd_done = 0
-
-
-        self.data_w = Signal(8)
-        self.data_r = Signal(8)
-
-        self.specials += Tristate(self.data, self.data_r, ~self.rd_n, self.data_w)
-
-        self.last_wr_n = 1
-        self.last_rd_n = 1
-
-        self.wr_delay = 0
-        self.rd_delay = 0
-
-    def wr_sim(self, selfp):
-        if self.wr_delay:
-            selfp.txe_n = 1
-            self.wr_delay = self.wr_delay - 1
-        else:
-            if (not selfp.wr_n and self.last_wr_n) and not selfp.txe_n:
-                self.wr_data.append(selfp.data_w)
-                self.wr_delay = 20 # XXX FIXME
-            self.last_wr_n = selfp.wr_n
-
-            if self.run:
-                selfp.txe_n = 0
-
-    def rd_sim(self, selfp):
-        if self.rd_delay:
-            selfp.rxf_n = 1
-            self.rd_delay = self.rd_delay - 1
-        else:
-            rxf_n = selfp.rxf_n
-            if self.run:
-                if self.rd_idx < len(self.rd_data)-1:
-                    self.rd_done = selfp.rxf_n
-                    selfp.rxf_n = 0
-                else:
-                    selfp.rxf_n = self.rd_done
-            else:
-                selfp.rxf_n = self.rd_done
-
-            if not selfp.rd_n and self.last_rd_n:
-                if self.rd_idx < len(self.rd_data)-1:
-                    self.rd_idx += not rxf_n
-                selfp.data_r = self.rd_data[self.rd_idx]
-                self.rd_done = 1
-            if selfp.rd_n and not self.last_rd_n:
-                self.rd_delay = 4 # XXX FIXME
-
-        self.last_rd_n = selfp.rd_n
-
-    def do_simulation(self, selfp):
-        RandRun.do_simulation(self, selfp)
-        if self.init:
-            selfp.rxf_n = 0
-            self.wr_data = []
-            self.init = False
-        self.wr_sim(selfp)
-        self.rd_sim(selfp)
-
-test_packet = [i%256 for i in range(128)]
-
-
-class TB(Module):
-    def __init__(self):
-        self.submodules.model = FT2232HAsynchronousModel(test_packet)
-        self.submodules.phy = FT2232HPHYAsynchronous(self.model, 50000000)
-
-        self.submodules.streamer = PacketStreamer(phy_description(8))
-        self.submodules.streamer_randomizer = AckRandomizer(phy_description(8), level=10)
-
-        self.submodules.logger_randomizer = AckRandomizer(phy_description(8), level=10)
-        self.submodules.logger = PacketLogger(phy_description(8))
-
-        self.comb += [
-            Record.connect(self.streamer.source, self.streamer_randomizer.sink),
-            self.phy.sink.stb.eq(self.streamer_randomizer.source.stb),
-            self.phy.sink.data.eq(self.streamer_randomizer.source.data),
-            self.streamer_randomizer.source.ack.eq(self.phy.sink.ack),
-
-            self.logger_randomizer.sink.stb.eq(self.phy.source.stb),
-            self.logger_randomizer.sink.data.eq(self.phy.source.data),
-            self.phy.source.ack.eq(self.logger_randomizer.sink.ack),
-            Record.connect(self.logger_randomizer.source, self.logger.sink)
-        ]
-
-    def gen_simulation(self, selfp):
-        yield from self.streamer.send(Packet(test_packet))
-        for i in range(4000):
-            yield
-        s, l, e = check(test_packet, self.model.wr_data)
-        print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
-
-        s, l, e = check(test_packet, self.logger.packet)
-        print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
-
-
-def main():
-    run_simulation(TB(), ncycles=8000, vcd_name="my.vcd", keep_files=True)
-
-if __name__ == "__main__":
-    main()
\ No newline at end of file
diff --git a/misoclib/com/liteusb/test/ft2232h_sync_tb.py b/misoclib/com/liteusb/test/ft2232h_sync_tb.py
deleted file mode 100644 (file)
index a17e808..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-from migen.fhdl.std import *
-from migen.flow.actor import *
-from migen.fhdl.specials import *
-
-from migen.sim.generic import run_simulation
-
-from misoclib.com.liteusb.common import *
-from misoclib.com.liteusb.phy.ft2232h import FT2232HPHYSynchronous
-from misoclib.com.liteusb.test.common import *
-
-# XXX for now use it from liteeth to avoid duplication
-from misoclib.com.liteeth.test.common import *
-
-class FT2232HSynchronousModel(Module, RandRun):
-    def __init__(self, rd_data):
-        RandRun.__init__(self, 10)
-        self.rd_data = [0] + rd_data
-        self.rd_idx = 0
-
-        # pads
-        self.data = Signal(8)
-        self.rxf_n = Signal(reset=1)
-        self.txe_n = Signal(reset=1)
-        self.rd_n = Signal(reset=1)
-        self.wr_n = Signal(reset=1)
-        self.oe_n = Signal(reset=1)
-        self.siwua = Signal()
-        self.pwren_n = Signal(reset=1)
-
-        self.init = True
-        self.wr_data = []
-        self.wait_wr_n = False
-        self.rd_done = 0
-
-
-        self.data_w = Signal(8)
-        self.data_r = Signal(8)
-
-        self.specials += Tristate(self.data, self.data_r, ~self.oe_n, self.data_w)
-
-    def wr_sim(self, selfp):
-        if not selfp.wr_n and not selfp.txe_n:
-            self.wr_data.append(selfp.data_w)
-            self.wait_wr_n = False
-
-        if not self.wait_wr_n:
-            if self.run:
-                selfp.txe_n = 1
-            else:
-                if selfp.txe_n:
-                    self.wait_wr_n = True
-                selfp.txe_n = 0
-
-    def rd_sim(self, selfp):
-        rxf_n = selfp.rxf_n
-        if self.run:
-            if self.rd_idx < len(self.rd_data)-1:
-                self.rd_done = selfp.rxf_n
-                selfp.rxf_n = 0
-            else:
-                selfp.rxf_n = self.rd_done
-        else:
-            selfp.rxf_n = self.rd_done
-
-        if not selfp.rd_n and not selfp.oe_n:
-            if self.rd_idx < len(self.rd_data)-1:
-                self.rd_idx += not rxf_n
-            selfp.data_r = self.rd_data[self.rd_idx]
-            self.rd_done = 1
-
-    def do_simulation(self, selfp):
-        RandRun.do_simulation(self, selfp)
-        if self.init:
-            selfp.rxf_n = 0
-            self.wr_data = []
-            self.init = False
-        self.wr_sim(selfp)
-        self.rd_sim(selfp)
-
-test_packet = [i%256 for i in range(512)]
-
-
-class TB(Module):
-    def __init__(self):
-        self.submodules.model = FT2232HSynchronousModel(test_packet)
-        self.submodules.phy = FT2232HPHYSynchronous(self.model)
-
-        self.submodules.streamer = PacketStreamer(phy_description(8))
-        self.submodules.streamer_randomizer = AckRandomizer(phy_description(8), level=10)
-
-        self.submodules.logger_randomizer = AckRandomizer(phy_description(8), level=10)
-        self.submodules.logger = PacketLogger(phy_description(8))
-
-        self.comb += [
-            Record.connect(self.streamer.source, self.streamer_randomizer.sink),
-            self.phy.sink.stb.eq(self.streamer_randomizer.source.stb),
-            self.phy.sink.data.eq(self.streamer_randomizer.source.data),
-            self.streamer_randomizer.source.ack.eq(self.phy.sink.ack),
-
-            self.logger_randomizer.sink.stb.eq(self.phy.source.stb),
-            self.logger_randomizer.sink.data.eq(self.phy.source.data),
-            self.phy.source.ack.eq(self.logger_randomizer.sink.ack),
-            Record.connect(self.logger_randomizer.source, self.logger.sink)
-        ]
-
-        # Use sys_clk as ftdi_clk in simulation
-        self.comb += [
-            ClockSignal("ftdi").eq(ClockSignal()),
-            ResetSignal("ftdi").eq(ResetSignal())
-        ]
-
-    def gen_simulation(self, selfp):
-        yield from self.streamer.send(Packet(test_packet))
-        for i in range(2000):
-            yield
-        s, l, e = check(test_packet, self.model.wr_data)
-        print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
-
-        s, l, e = check(test_packet, self.logger.packet[1:])
-        print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
-
-
-def main():
-    run_simulation(TB(), ncycles=8000, vcd_name="my.vcd", keep_files=True)
-
-if __name__ == "__main__":
-    main()
\ No newline at end of file
diff --git a/misoclib/com/liteusb/test/ft245_async_tb.py b/misoclib/com/liteusb/test/ft245_async_tb.py
new file mode 100644 (file)
index 0000000..dcc2599
--- /dev/null
@@ -0,0 +1,139 @@
+from migen.fhdl.std import *
+from migen.flow.actor import *
+from migen.fhdl.specials import *
+
+from migen.sim.generic import run_simulation
+
+from misoclib.com.liteusb.common import *
+from misoclib.com.liteusb.phy.ft245 import FT245PHYAsynchronous
+from misoclib.com.liteusb.test.common import *
+
+# XXX for now use it from liteeth to avoid duplication
+from misoclib.com.liteeth.test.common import *
+
+class FT245AsynchronousModel(Module):
+    def __init__(self, clk_freq, rd_data):
+        self.clk_freq = clk_freq
+        self.rd_data = [0] + rd_data
+        self.rd_idx = 0
+
+        # timings
+        self.tRDInactive = self.ns(49) # RXF# inactive after RD# cycle
+        self.tWRInactive = self.ns(49) # TXE# inactive after WR# cycle
+
+        # pads
+        self.data = Signal(8)
+        self.rxf_n = Signal(reset=1)
+        self.txe_n = Signal(reset=1)
+        self.rd_n = Signal(reset=1)
+        self.wr_n = Signal(reset=1)
+
+        self.init = True
+        self.wr_data = []
+        self.wait_wr_n = False
+        self.rd_done = 0
+
+
+        self.data_w = Signal(8)
+        self.data_r = Signal(8)
+
+        self.specials += Tristate(self.data, self.data_r, ~self.rd_n, self.data_w)
+
+        self.last_wr_n = 1
+        self.last_rd_n = 1
+
+        self.wr_delay = 0
+        self.rd_delay = 0
+
+    def wr_sim(self, selfp):
+        if self.wr_delay:
+            selfp.txe_n = 1
+            self.wr_delay = self.wr_delay - 1
+        else:
+            if (not selfp.wr_n and self.last_wr_n) and not selfp.txe_n:
+                self.wr_data.append(selfp.data_w)
+                self.wr_delay = self.tWRInactive
+            self.last_wr_n = selfp.wr_n
+
+            selfp.txe_n = 0
+
+    def rd_sim(self, selfp):
+        if self.rd_delay:
+            selfp.rxf_n = 1
+            self.rd_delay = self.rd_delay - 1
+        else:
+            rxf_n = selfp.rxf_n
+            if self.rd_idx < len(self.rd_data)-1:
+                self.rd_done = selfp.rxf_n
+                selfp.rxf_n = 0
+            else:
+                selfp.rxf_n = self.rd_done
+
+            if not selfp.rd_n and self.last_rd_n:
+                if self.rd_idx < len(self.rd_data)-1:
+                    self.rd_idx += not rxf_n
+                selfp.data_r = self.rd_data[self.rd_idx]
+                self.rd_done = 1
+            if selfp.rd_n and not self.last_rd_n:
+                self.rd_delay = self.tRDInactive
+
+        self.last_rd_n = selfp.rd_n
+
+    def do_simulation(self, selfp):
+        if self.init:
+            selfp.rxf_n = 0
+            self.wr_data = []
+            self.init = False
+        self.wr_sim(selfp)
+        self.rd_sim(selfp)
+
+    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)
+
+
+test_packet = [i%256 for i in range(128)]
+
+
+class TB(Module):
+    def __init__(self):
+        clk_freq = 50*1000000
+        self.submodules.model = FT245AsynchronousModel(clk_freq, test_packet)
+        self.submodules.phy = FT245PHYAsynchronous(self.model, clk_freq)
+
+        self.submodules.streamer = PacketStreamer(phy_description(8))
+        self.submodules.streamer_randomizer = AckRandomizer(phy_description(8), level=10)
+
+        self.submodules.logger_randomizer = AckRandomizer(phy_description(8), level=10)
+        self.submodules.logger = PacketLogger(phy_description(8))
+
+        self.comb += [
+            Record.connect(self.streamer.source, self.streamer_randomizer.sink),
+            self.phy.sink.stb.eq(self.streamer_randomizer.source.stb),
+            self.phy.sink.data.eq(self.streamer_randomizer.source.data),
+            self.streamer_randomizer.source.ack.eq(self.phy.sink.ack),
+
+            self.logger_randomizer.sink.stb.eq(self.phy.source.stb),
+            self.logger_randomizer.sink.data.eq(self.phy.source.data),
+            self.phy.source.ack.eq(self.logger_randomizer.sink.ack),
+            Record.connect(self.logger_randomizer.source, self.logger.sink)
+        ]
+
+    def gen_simulation(self, selfp):
+        yield from self.streamer.send(Packet(test_packet))
+        for i in range(4000):
+            yield
+        s, l, e = check(test_packet, self.model.wr_data)
+        print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
+
+        s, l, e = check(test_packet, self.logger.packet)
+        print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
+
+
+def main():
+    run_simulation(TB(), ncycles=8000, vcd_name="my.vcd", keep_files=True)
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/misoclib/com/liteusb/test/ft245_sync_tb.py b/misoclib/com/liteusb/test/ft245_sync_tb.py
new file mode 100644 (file)
index 0000000..ffa2faf
--- /dev/null
@@ -0,0 +1,127 @@
+from migen.fhdl.std import *
+from migen.flow.actor import *
+from migen.fhdl.specials import *
+
+from migen.sim.generic import run_simulation
+
+from misoclib.com.liteusb.common import *
+from misoclib.com.liteusb.phy.ft245 import FT245PHYSynchronous
+from misoclib.com.liteusb.test.common import *
+
+# XXX for now use it from liteeth to avoid duplication
+from misoclib.com.liteeth.test.common import *
+
+class FT245SynchronousModel(Module, RandRun):
+    def __init__(self, rd_data):
+        RandRun.__init__(self, 10)
+        self.rd_data = [0] + rd_data
+        self.rd_idx = 0
+
+        # pads
+        self.data = Signal(8)
+        self.rxf_n = Signal(reset=1)
+        self.txe_n = Signal(reset=1)
+        self.rd_n = Signal(reset=1)
+        self.wr_n = Signal(reset=1)
+        self.oe_n = Signal(reset=1)
+        self.siwua = Signal()
+        self.pwren_n = Signal(reset=1)
+
+        self.init = True
+        self.wr_data = []
+        self.wait_wr_n = False
+        self.rd_done = 0
+
+
+        self.data_w = Signal(8)
+        self.data_r = Signal(8)
+
+        self.specials += Tristate(self.data, self.data_r, ~self.oe_n, self.data_w)
+
+    def wr_sim(self, selfp):
+        if not selfp.wr_n and not selfp.txe_n:
+            self.wr_data.append(selfp.data_w)
+            self.wait_wr_n = False
+
+        if not self.wait_wr_n:
+            if self.run:
+                selfp.txe_n = 1
+            else:
+                if selfp.txe_n:
+                    self.wait_wr_n = True
+                selfp.txe_n = 0
+
+    def rd_sim(self, selfp):
+        rxf_n = selfp.rxf_n
+        if self.run:
+            if self.rd_idx < len(self.rd_data)-1:
+                self.rd_done = selfp.rxf_n
+                selfp.rxf_n = 0
+            else:
+                selfp.rxf_n = self.rd_done
+        else:
+            selfp.rxf_n = self.rd_done
+
+        if not selfp.rd_n and not selfp.oe_n:
+            if self.rd_idx < len(self.rd_data)-1:
+                self.rd_idx += not rxf_n
+            selfp.data_r = self.rd_data[self.rd_idx]
+            self.rd_done = 1
+
+    def do_simulation(self, selfp):
+        RandRun.do_simulation(self, selfp)
+        if self.init:
+            selfp.rxf_n = 0
+            self.wr_data = []
+            self.init = False
+        self.wr_sim(selfp)
+        self.rd_sim(selfp)
+
+test_packet = [i%256 for i in range(512)]
+
+
+class TB(Module):
+    def __init__(self):
+        self.submodules.model = FT245SynchronousModel(test_packet)
+        self.submodules.phy = FT245PHYSynchronous(self.model)
+
+        self.submodules.streamer = PacketStreamer(phy_description(8))
+        self.submodules.streamer_randomizer = AckRandomizer(phy_description(8), level=10)
+
+        self.submodules.logger_randomizer = AckRandomizer(phy_description(8), level=10)
+        self.submodules.logger = PacketLogger(phy_description(8))
+
+        self.comb += [
+            Record.connect(self.streamer.source, self.streamer_randomizer.sink),
+            self.phy.sink.stb.eq(self.streamer_randomizer.source.stb),
+            self.phy.sink.data.eq(self.streamer_randomizer.source.data),
+            self.streamer_randomizer.source.ack.eq(self.phy.sink.ack),
+
+            self.logger_randomizer.sink.stb.eq(self.phy.source.stb),
+            self.logger_randomizer.sink.data.eq(self.phy.source.data),
+            self.phy.source.ack.eq(self.logger_randomizer.sink.ack),
+            Record.connect(self.logger_randomizer.source, self.logger.sink)
+        ]
+
+        # Use sys_clk as ftdi_clk in simulation
+        self.comb += [
+            ClockSignal("ftdi").eq(ClockSignal()),
+            ResetSignal("ftdi").eq(ResetSignal())
+        ]
+
+    def gen_simulation(self, selfp):
+        yield from self.streamer.send(Packet(test_packet))
+        for i in range(2000):
+            yield
+        s, l, e = check(test_packet, self.model.wr_data)
+        print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
+
+        s, l, e = check(test_packet, self.logger.packet[1:])
+        print("shift " + str(s) + " / length " + str(l) + " / errors " + str(e))
+
+
+def main():
+    run_simulation(TB(), ncycles=8000, vcd_name="my.vcd", keep_files=True)
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file