liteusb: begin refactoring and simplification (wip)
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 27 Apr 2015 13:19:54 +0000 (15:19 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Mon, 27 Apr 2015 13:22:49 +0000 (15:22 +0200)
17 files changed:
misoclib/com/litepcie/test/model/__init__.py [new file with mode: 0644]
misoclib/com/liteusb/__init__.py [new file with mode: 0644]
misoclib/com/liteusb/common.py
misoclib/com/liteusb/core/crc.py
misoclib/com/liteusb/core/depacketizer.py
misoclib/com/liteusb/core/packetizer.py
misoclib/com/liteusb/frontend/crossbar.py
misoclib/com/liteusb/frontend/dma.py
misoclib/com/liteusb/frontend/uart.py
misoclib/com/liteusb/phy/__init__.py [new file with mode: 0644]
misoclib/com/liteusb/phy/ft2232h.py
misoclib/com/liteusb/software/.keep_me [deleted file]
misoclib/com/liteusb/test/.keep_me [deleted file]
misoclib/com/liteusb/test/Makefile [new file with mode: 0644]
misoclib/com/liteusb/test/__init__.py [new file with mode: 0644]
misoclib/com/liteusb/test/common.py [new file with mode: 0644]
misoclib/com/liteusb/test/ft2232h_tb.py [new file with mode: 0644]

diff --git a/misoclib/com/litepcie/test/model/__init__.py b/misoclib/com/litepcie/test/model/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/misoclib/com/liteusb/__init__.py b/misoclib/com/liteusb/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
index eaa9f162cd5b06e1f78d00d7ff0975580da7e982..f9e4b267421bd9c3f571257184396a4df4db4c59 100644 (file)
@@ -1,20 +1,19 @@
-import random
-
 from migen.fhdl.std import *
 from migen.genlib.fsm import *
 from migen.actorlib.fifo import *
 from migen.flow.actor import EndpointDescription
+from migen.actorlib.packet import Arbiter, Dispatcher
 
 user_layout = EndpointDescription(
     [("dst", 8),
      ("length", 4*8),
      ("error", 1),
-     ("d", 8)
+     ("data", 8)
     ],
     packetized=True
 )
 
-phy_layout = [("d", 8)]
+phy_layout = [("data", 8)]
 
 
 class LiteUSBPipe:
@@ -22,39 +21,3 @@ class LiteUSBPipe:
         self.sink = Sink(layout)
         self.source = Source(layout)
 
-
-class LiteUSBTimeout(Module):
-    def __init__(self, clk_freq, length):
-        cnt_max = int(clk_freq*length)
-        width = bits_for(cnt_max)
-
-        self.clear = Signal()
-        self.done = Signal()
-
-        cnt = Signal(width)
-        self.sync += \
-            If(self.clear,
-                cnt.eq(0)
-            ).Elif(~self.done,
-                cnt.eq(cnt+1)
-            )
-        self.comb += self.done.eq(cnt == cnt_max)
-
-
-#
-# TB
-#
-def randn(max_n):
-    return random.randint(0, max_n-1)
-
-
-class RandRun:
-    def __init__(self, level=0):
-        self.run = True
-        self.level = level
-
-    def do_simulation(self, selfp):
-        self.run = True
-        n = randn(100)
-        if n < self.level:
-            self.run = False
index bfea13f6a405aa0e9a419609064feb6c66405ce2..9d9d2a1d981520783840b018a63cdcb604f214ba 100644 (file)
@@ -34,7 +34,7 @@ class CRCEngine(Module):
         next CRC value.
     """
     def __init__(self, dat_width, width, polynom):
-        self.d = Signal(dat_width)
+        self.data = Signal(dat_width)
         self.last = Signal(width)
         self.next = Signal(width)
 
@@ -74,7 +74,7 @@ class CRCEngine(Module):
                 if t == "state":
                     xors += [self.last[n]]
                 elif t == "din":
-                    xors += [self.d[n]]
+                    xors += [self.data[n]]
             self.comb += self.next[i].eq(optree("^", xors))
 
 
@@ -105,7 +105,7 @@ class CRC32(Module):
     check = 0xC704DD7B
 
     def __init__(self, dat_width):
-        self.d = Signal(dat_width)
+        self.data = Signal(dat_width)
         self.value = Signal(self.width)
         self.error = Signal()
 
@@ -115,7 +115,7 @@ class CRC32(Module):
         reg = Signal(self.width, reset=self.init)
         self.sync += reg.eq(self.engine.next)
         self.comb += [
-            self.engine.d.eq(self.d),
+            self.engine.data.eq(self.data),
             self.engine.last.eq(reg),
 
             self.value.eq(~reg[::-1]),
@@ -147,7 +147,7 @@ class CRCInserter(Module):
 
         # # #
 
-        dw = flen(sink.d)
+        dw = flen(sink.data)
         crc = crc_class(dw)
         fsm = FSM(reset_state="IDLE")
         self.submodules += crc, fsm
@@ -162,7 +162,7 @@ class CRCInserter(Module):
         )
         fsm.act("COPY",
             crc.ce.eq(sink.stb & source.ack),
-            crc.d.eq(sink.d),
+            crc.data.eq(sink.data),
             Record.connect(sink, source),
             source.eop.eq(0),
             If(sink.stb & sink.eop & source.ack,
@@ -175,7 +175,7 @@ class CRCInserter(Module):
             cnt_done = Signal()
             fsm.act("INSERT",
                 source.stb.eq(1),
-                chooser(crc.value, cnt, source.d, reverse=True),
+                chooser(crc.value, cnt, source.data, reverse=True),
                 If(cnt_done,
                     source.eop.eq(1),
                     If(source.ack, NextState("IDLE"))
@@ -192,7 +192,7 @@ class CRCInserter(Module):
             fsm.act("INSERT",
                 source.stb.eq(1),
                 source.eop.eq(1),
-                source.d.eq(crc.value),
+                source.data.eq(crc.value),
                 If(source.ack, NextState("IDLE"))
             )
         self.comb += self.busy.eq(~fsm.ongoing("IDLE"))
@@ -228,7 +228,7 @@ class CRCChecker(Module):
 
         # # #
 
-        dw = flen(sink.d)
+        dw = flen(sink.data)
         crc = crc_class(dw)
         self.submodules += crc
         ratio = crc.width//dw
@@ -268,14 +268,14 @@ class CRCChecker(Module):
             NextState("IDLE"),
         )
         fsm.act("IDLE",
-            crc.d.eq(sink.d),
+            crc.data.eq(sink.data),
             If(sink.stb & sink.sop & sink.ack,
                 crc.ce.eq(1),
                 NextState("COPY")
             )
         )
         fsm.act("COPY",
-            crc.d.eq(sink.d),
+            crc.data.eq(sink.data),
             If(sink.stb & sink.ack,
                 crc.ce.eq(1),
                 If(sink.eop,
index aa308b010fa151ea1e4fa4d716528683e145929c..27b58da22b110ad6c70d433484965655f54cdb50 100644 (file)
@@ -1,6 +1,7 @@
 from migen.fhdl.std import *
 from migen.actorlib.structuring import *
 from migen.genlib.fsm import FSM, NextState
+from migen.genlib.misc import Timeout
 
 from misoclib.com.liteusb.common import *
 
@@ -32,12 +33,12 @@ class LiteUSBDepacketizer(Module):
 
         for i, byte in enumerate(header):
             chunk = getattr(header_pack.source.payload, "chunk" + str(i))
-            self.comb += byte.eq(chunk.d)
+            self.comb += byte.eq(chunk.data)
 
         fsm = FSM()
         self.submodules += fsm
 
-        self.comb += preamble[0].eq(sink.d)
+        self.comb += preamble[0].eq(sink.data)
         for i in range(1, 4):
             self.sync += If(sink.stb & sink.ack,
                     preamble[i].eq(preamble[i-1])
@@ -54,18 +55,21 @@ class LiteUSBDepacketizer(Module):
             header_pack.source.ack.eq(1),
         )
 
-        self.submodules.timeout = LiteUSBTimeout(60000000, timeout)
-        self.comb += self.timeout.clear.eq(fsm.ongoing("WAIT_SOP"))
+        self.submodules.timeout = Timeout(60000000*timeout)
+        self.comb += [
+            self.timeout.reset.eq(fsm.ongoing("WAIT_SOP")),
+            self.timeout.ce.eq(1)
+        ]
 
         fsm.act("RECEIVE_HEADER",
             header_pack.sink.stb.eq(sink.stb),
             header_pack.sink.payload.eq(sink.payload),
-            If(self.timeout.done, NextState("WAIT_SOP"))
+            If(self.timeout.reached, NextState("WAIT_SOP"))
             .Elif(header_pack.source.stb, NextState("RECEIVE_PAYLOAD"))
             .Else(sink.ack.eq(1))
         )
 
-        self.comb += header_pack.reset.eq(self.timeout.done)
+        self.comb += header_pack.reset.eq(self.timeout.reached)
 
         sop = Signal()
         eop = Signal()
@@ -75,9 +79,9 @@ class LiteUSBDepacketizer(Module):
             source.stb.eq(sink.stb),
             source.sop.eq(sop),
             source.eop.eq(eop),
-            source.d.eq(sink.d),
+            source.data.eq(sink.data),
             sink.ack.eq(source.ack),
-            If((eop & sink.stb & source.ack) | self.timeout.done,
+            If((eop & sink.stb & source.ack) | self.timeout.reached,
                 NextState("WAIT_SOP")
             )
         )
@@ -120,7 +124,7 @@ class DepacketizerSourceModel(Module, Source, RandRun):
             self._cnt += 1
 
         selfp.stb = self._stb
-        selfp.d = self.data[self._cnt]
+        selfp.data = self.data[self._cnt]
 
         if self._cnt == len(self.data)-1:
             raise StopSimulation
index ebd5bf1fefb986ddd728cdb6e5c0a42c1baa6959..5843cf5cb007af8d09e9f9a74b313dc259808ae9 100644 (file)
@@ -35,7 +35,7 @@ class LiteUSBPacketizer(Module):
 
         for i, byte in enumerate(header):
             chunk = getattr(header_unpack.sink.payload, "chunk" + str(i))
-            self.comb += chunk.d.eq(byte)
+            self.comb += chunk.data.eq(byte)
 
         fsm = FSM()
         self.submodules += fsm
@@ -47,14 +47,14 @@ class LiteUSBPacketizer(Module):
         fsm.act("SEND_HEADER",
             header_unpack.sink.stb.eq(1),
             source.stb.eq(1),
-            source.d.eq(header_unpack.source.d),
+            source.data.eq(header_unpack.source.data),
             header_unpack.source.ack.eq(source.ack),
             If(header_unpack.sink.ack, NextState("SEND_DATA"))
         )
 
         fsm.act("SEND_DATA",
             source.stb.eq(sink.stb),
-            source.d.eq(sink.d),
+            source.data.eq(sink.data),
             sink.ack.eq(source.ack),
             If(source.ack & sink.eop, NextState("WAIT_SOP"))
         )
@@ -113,7 +113,7 @@ class PacketizerSourceModel(Module, Source, RandRun):
         selfp.eop = self._eop & self._stb
         selfp.dst = dst
         selfp.length = length
-        selfp.d = payload[self._payload_cnt]
+        selfp.data = payload[self._payload_cnt]
 
         if self._frame_cnt == len(self.data):
             raise StopSimulation
index 85ddefbf2e433d459d6a1df94f3a028288eeb48a..0ed2d57638f656e4f71995bf1961cd6a530094a1 100644 (file)
@@ -12,21 +12,8 @@ class LiteUSBCrossbar(Module):
             self.slave = slave
 
         # masters --> slave arbitration
-        self.submodules.rr = RoundRobin(len(masters))
-        cases = {}
-        for i, m in enumerate(masters):
-            sop = Signal()
-            eop = Signal()
-            ongoing = Signal()
-            self.comb += [
-                sop.eq(m.source.stb & m.source.sop),
-                eop.eq(m.source.stb & m.source.eop & m.source.ack),
-            ]
-            self.sync += ongoing.eq((sop | ongoing) & ~eop)
-            self.comb += self.rr.request[i].eq(sop | ongoing)
-
-            cases[i] = [Record.connect(masters[i].source, slave.source)]
-        self.comb += Case(self.rr.grant, cases)
+        sources = [master.source for master in masters]
+        self.submodules.arbiter = Arbiter(sources, slave.source)
 
         # slave --> master demux
         cases = {}
index 7cccccde30d9c2cea070d8d36f021e07e9b6085c..7b7d4eb645169106db2f1c7889d1ec40a1a0c419 100644 (file)
@@ -79,7 +79,7 @@ class LiteUSBDMAReader(Module, AutoCSR):
             source.sop.eq(cnt == 0),
             source.eop.eq(cnt == (self.dma.length*pack_factor-1)),
             source.length.eq(self.dma.length*pack_factor+4),
-            source.d.eq(unpack.source.d),
+            source.data.eq(unpack.source.data),
             source.dst.eq(tag),
             unpack.source.ack.eq(source.ack)
         ]
index 68a032ec54847787d64f7797ce43f19f4539c77d..49b04959cd0bf45aea1172f471e6bc973805eb49 100644 (file)
@@ -29,7 +29,7 @@ class LiteUSBUART(Module, AutoCSR):
         self.sync += \
             If(tx_start,
                 source.stb.eq(1),
-                source.d.eq(self._rxtx.r),
+                source.data.eq(self._rxtx.r),
             ).Elif(tx_done,
                 source.stb.eq(0)
             )
@@ -50,7 +50,7 @@ class LiteUSBUART(Module, AutoCSR):
         self.comb += [
             rx_fifo.we.eq(sink.stb),
             sink.ack.eq(sink.stb & rx_fifo.writable),
-            rx_fifo.din.eq(sink.d),
+            rx_fifo.din.eq(sink.data),
 
             rx_available.eq(rx_fifo.readable),
             rx_fifo.re.eq(self.ev.rx.clear),
diff --git a/misoclib/com/liteusb/phy/__init__.py b/misoclib/com/liteusb/phy/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
index 349dc60838d4167c960acd3068942fbb8cf276b1..5a325656c940ee89c8a8af2fbf5d09695e644ea8 100644 (file)
@@ -6,7 +6,7 @@ from migen.fhdl.specials import *
 from misoclib.com.liteusb.common import *
 
 
-class FT2232HPHY(Module):
+class FT2232HPHYSynchronous(Module):
     def __init__(self, pads, fifo_depth=32, read_time=16, write_time=16):
         dw = flen(pads.data)
 
@@ -70,7 +70,7 @@ class FT2232HPHY(Module):
 
         data_w_accepted = Signal(reset=1)
 
-        fsm = FSM()
+        fsm = FSM(reset_state="READ")
         self.submodules += RenameClockDomains(fsm, {"sys": "ftdi"})
 
         fsm.act("READ",
@@ -135,9 +135,9 @@ class FT2232HPHY(Module):
                 pads.wr_n.eq(1)
             ),
                 read_buffer.sink.stb.eq(~pads.rd_n & ~rxf_n),
-                read_buffer.sink.d.eq(data_r),
+                read_buffer.sink.data.eq(data_r),
                 If(~txe_n & data_w_accepted,
-                    data_w.eq(write_fifo.source.d)
+                    data_w.eq(write_fifo.source.data)
                 )
         ]
 
@@ -148,169 +148,3 @@ class FT2232HPHY(Module):
 
         self.debug = Signal(8)
         self.comb += self.debug.eq(data_r)
-
-
-#
-# TB
-#
-class FT2232HModel(Module, RandRun):
-    def __init__(self, rd_data):
-        RandRun.__init__(self, 50)
-        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
-
-    def wr_sim(self, selfp):
-        if not selfp.wr_n and not selfp.txe_n:
-            self.wr_data.append(selfp.data)
-            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 = 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)
-
-
-class UserModel(Module, RandRun):
-    def __init__(self, wr_data):
-        RandRun.__init__(self, 50)
-        self.wr_data = wr_data
-        self.wr_data_idx = 0
-
-        self.sink = Sink(phy_layout)
-        self.source = Source(phy_layout)
-
-        self.rd_data = []
-
-    def wr_sim(self, selfp):
-        auth = True
-        if selfp.source.stb and not selfp.source.ack:
-            auth = False
-        if auth:
-            if self.wr_data_idx < len(self.wr_data):
-                if self.run:
-                    selfp.source.d = self.wr_data[self.wr_data_idx]
-                    selfp.source.stb = 1
-                    self.wr_data_idx += 1
-                else:
-                    selfp.source.stb = 0
-            else:
-                self.source.stb = 0
-
-    def rd_sim(self, selfp):
-        if self.run:
-            selfp.sink.ack = 1
-        else:
-            selfp.sink.ack = 0
-        if selfp.sink.stb & selfp.sink.ack:
-            self.rd_data.append(selfp.sink.d)
-
-    def do_simulation(self, selfp):
-        RandRun.do_simulation(self, selfp)
-        self.wr_sim(selfp)
-        self.rd_sim(selfp)
-
-
-LENGTH = 512
-model_rd_data = [i%256 for i in range(LENGTH)][::-1]
-user_wr_data = [i%256 for i in range(LENGTH)]
-
-
-class TB(Module):
-    def __init__(self):
-        self.submodules.model = FT2232HModel(model_rd_data)
-        self.submodules.phy = FT2232HPHY(self.model)
-
-        self.submodules.user = UserModel(user_wr_data)
-
-        self.comb += [
-            self.user.source.connect(self.phy.sink),
-            self.phy.source.connect(self.user.sink)
-        ]
-
-        # Use sys_clk as ftdi_clk in simulation
-        self.comb += [
-            ClockSignal("ftdi").eq(ClockSignal()),
-            ResetSignal("ftdi").eq(ResetSignal())
-        ]
-
-
-def print_results(s, l1, l2):
-    def comp(l1, l2):
-        r = True
-        try:
-            for i, val in enumerate(l1):
-                if val != l2[i]:
-                    print(s + " : val : {:02X}, exp : {:02X}".format(val, l2[i]))
-                    r = False
-        except:
-            return r
-        return r
-
-    c = comp(l1, l2)
-    r = s + " "
-    if c:
-        r += "[OK]"
-    else:
-        r += "[KO]"
-    print(r)
-
-
-def main():
-    from migen.sim.generic import run_simulation
-    tb = TB()
-    run_simulation(tb, ncycles=8000, vcd_name="tb_phy.vcd")
-
-    # print(tb.user.rd_data)
-    # print(tb.model.wr_data)
-    # print(len(tb.user.rd_data))
-    # print(len(tb.model.wr_data))
-
-    print_results("F2232HModel --> UserModel", model_rd_data, tb.user.rd_data)
-    print_results("UserModel --> FT2232HModel", user_wr_data,  tb.model.wr_data)
-
-if __name__ == "__main__":
-    main()
diff --git a/misoclib/com/liteusb/software/.keep_me b/misoclib/com/liteusb/software/.keep_me
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/misoclib/com/liteusb/test/.keep_me b/misoclib/com/liteusb/test/.keep_me
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/misoclib/com/liteusb/test/Makefile b/misoclib/com/liteusb/test/Makefile
new file mode 100644 (file)
index 0000000..6b0ad57
--- /dev/null
@@ -0,0 +1,7 @@
+MSCDIR = ../../
+PYTHON = python3
+
+CMD = PYTHONPATH=$(MSCDIR) $(PYTHON)
+
+ft2232h_tb:
+       $(CMD) ft2232h_tb.py
diff --git a/misoclib/com/liteusb/test/__init__.py b/misoclib/com/liteusb/test/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/misoclib/com/liteusb/test/common.py b/misoclib/com/liteusb/test/common.py
new file mode 100644 (file)
index 0000000..3355987
--- /dev/null
@@ -0,0 +1,16 @@
+import random
+
+def randn(max_n):
+    return random.randint(0, max_n-1)
+
+
+class RandRun:
+    def __init__(self, level=0):
+        self.run = True
+        self.level = level
+
+    def do_simulation(self, selfp):
+        self.run = True
+        n = randn(100)
+        if n < self.level:
+            self.run = False
diff --git a/misoclib/com/liteusb/test/ft2232h_tb.py b/misoclib/com/liteusb/test/ft2232h_tb.py
new file mode 100644 (file)
index 0000000..1dd6029
--- /dev/null
@@ -0,0 +1,133 @@
+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 *
+
+
+def phy_description():
+    payload_layout = [("data", 8)]
+    return EndpointDescription(payload_layout, packetized=True)
+
+
+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())
+        self.submodules.streamer_randomizer = AckRandomizer(phy_description(), level=10)
+
+        self.submodules.logger_randomizer = AckRandomizer(phy_description(), level=10)
+        self.submodules.logger = PacketLogger(phy_description())
+
+        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