bugfix wishbone downconvert using wb sram 64-to-32 test
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 20 Aug 2020 14:28:12 +0000 (15:28 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 20 Aug 2020 14:28:12 +0000 (15:28 +0100)
src/soc/bus/test/test_sram_wb_downconvert.py [new file with mode: 0644]
src/soc/bus/wb_downconvert.py [new file with mode: 0644]
src/soc/bus/wb_upconvert.py [deleted file]

diff --git a/src/soc/bus/test/test_sram_wb_downconvert.py b/src/soc/bus/test/test_sram_wb_downconvert.py
new file mode 100644 (file)
index 0000000..fe87bf4
--- /dev/null
@@ -0,0 +1,132 @@
+"""demonstration of nmigen-soc SRAM behind a wishbone bus and a downconverter
+"""
+from nmigen_soc.wishbone.bus import Interface
+from nmigen_soc.wishbone.sram import SRAM
+from nmigen import Memory, Signal, Module
+from nmigen.utils import log2_int
+from soc.bus.wb_downconvert import WishboneDownConvert
+
+# memory
+memory = Memory(width=32, depth=32)
+sram = SRAM(memory=memory, granularity=16)
+
+# interface for converter
+cvtbus = Interface(addr_width=log2_int(memory.depth//2, need_pow2=False),
+                data_width=memory.width*2,
+                features={'cti'},
+                granularity=16)
+
+# actual converter
+downcvt = WishboneDownConvert(cvtbus, sram.bus)
+bus = cvtbus
+
+# valid wishbone signals include
+# sram.bus.adr
+# sram.bus.dat_w
+# sram.bus.dat_r
+# sram.bus.sel
+# sram.bus.cyc
+# sram.bus.stb
+# sram.bus.we
+# sram.bus.ack
+
+# setup simulation
+from nmigen.back.pysim import Simulator, Delay, Settle
+m = Module()
+m.submodules.sram = sram
+m.submodules.downcvt = downcvt
+sim = Simulator(m)
+sim.add_clock(1e-6)
+
+def print_sig(sig, format=None):
+    if format == None:
+        print(f"{sig.__repr__()} = {(yield sig)}")
+    if format == "h":
+        print(f"{sig.__repr__()} = {hex((yield sig))}")
+
+def process():
+
+    test_data = 0xdeadbeef12345678
+
+    # enable necessary signals for write
+    for en in range(4):
+        yield bus.sel[en].eq(1)
+    yield bus.we.eq(1)
+    yield bus.cyc.eq(1)
+    yield bus.stb.eq(1)
+
+    # put data and address on bus
+    yield bus.adr.eq(0x4)
+    yield bus.dat_w.eq(test_data)
+    yield
+
+    while True:
+        ack = yield bus.ack
+        if ack:
+            break
+        yield
+    yield bus.cyc.eq(0)
+    yield bus.stb.eq(0)
+    yield bus.adr.eq(0)
+    yield bus.dat_w.eq(0)
+
+
+    # set necessary signal to read bus
+    # at address 0
+    yield bus.we.eq(0)
+    yield bus.adr.eq(0)
+    yield bus.cyc.eq(1)
+    yield bus.stb.eq(1)
+    yield
+
+    while True:
+        ack = yield bus.ack
+        if ack:
+            break
+        yield
+
+    # see sync_behaviors.py
+    # for why we need Settle()
+    # debug print the bus address/data
+    yield Settle()
+    yield from print_sig(bus.adr)
+    yield from print_sig(bus.dat_r, "h")
+
+    # check the result
+    data = yield bus.dat_r
+    assert data == 0
+
+    # set necessary signal to read bus
+    # at address 4
+    yield bus.we.eq(0)
+    yield bus.adr.eq(0x4)
+    yield bus.cyc.eq(1)
+    yield bus.stb.eq(1)
+    yield
+
+    while True:
+        ack = yield bus.ack
+        if ack:
+            break
+        yield
+
+    data = yield bus.dat_r
+    print ("data", hex(data))
+
+    yield from print_sig(bus.adr)
+    yield from print_sig(bus.dat_r, "h")
+
+    # check the result
+    assert data == test_data, "data != %x %16x" % (test_data, data)
+
+    # disable signals
+    yield bus.adr.eq(0)
+    yield bus.cyc.eq(0)
+    yield bus.stb.eq(0)
+    yield
+
+sim_writer = sim.write_vcd(f"{__file__[:-3]}.vcd")
+
+with sim_writer:
+    sim.add_sync_process(process)
+    sim.run()
diff --git a/src/soc/bus/wb_downconvert.py b/src/soc/bus/wb_downconvert.py
new file mode 100644 (file)
index 0000000..1e4389c
--- /dev/null
@@ -0,0 +1,122 @@
+from nmigen import Elaboratable, Module, Signal, Repl, Cat, Mux
+from nmigen.utils import log2_int
+
+class WishboneDownConvert(Elaboratable):
+    """DownConverter
+
+    This module splits Wishbone accesses from a master interface to a smaller
+    slave interface.
+
+    Writes:
+        Writes from master are split N writes to the slave. Access
+        is acked when the last access is acked by the slave.
+
+    Reads:
+        Read from master are split in N reads to the the slave.
+        Read data from the slave are cached before being presented,
+        concatenated on the last access.
+
+    TODO:
+        Manage err signal? (Not implemented since we generally don't
+        use it on Migen/MiSoC modules)
+    """
+    def __init__(self, master, slave):
+        self.master = master
+        self.slave = slave
+
+    def elaborate(self, platform):
+
+        master = self.master
+        slave = self.slave
+        m = Module()
+        comb = m.d.comb
+        sync = m.d.sync
+
+        dw_from = len(master.dat_r)
+        dw_to = len(slave.dat_w)
+        ratio = dw_from//dw_to
+
+        # # #
+
+        read = Signal()
+        write = Signal()
+
+        cached_data = Signal(dw_from)
+        shift_reg = Signal(dw_from)
+
+        counter = Signal(log2_int(ratio, False))
+        counter_reset = Signal()
+        counter_ce = Signal()
+        with m.If(counter_reset):
+            sync += counter.eq(0)
+        with m.Elif(counter_ce):
+            sync += counter.eq(counter + 1)
+
+        counter_done = Signal()
+        comb += counter_done.eq(counter == ratio-1)
+
+        # Main FSM
+        with m.FSM() as fsm:
+            with m.State("IDLE"):
+                comb += counter_reset.eq(1)
+                sync += cached_data.eq(0)
+                with m.If(master.stb & master.cyc):
+                    with m.If(master.we):
+                        m.next = "WRITE"
+                    with m.Else():
+                        m.next = "READ"
+
+            with m.State("WRITE"):
+                comb += write.eq(1)
+                comb += slave.we.eq(1)
+                comb += slave.cyc.eq(1)
+                with m.If(master.stb & master.cyc):
+                    comb += slave.stb.eq(1)
+                    with m.If(slave.ack):
+                        comb += counter_ce.eq(1)
+                        with m.If(counter_done):
+                            comb += master.ack.eq(1)
+                            m.next = "IDLE"
+                with m.Elif(~master.cyc):
+                    m.next = "IDLE"
+
+            with m.State("READ"):
+                comb += read.eq(1)
+                comb += slave.cyc.eq(1)
+                with m.If(master.stb & master.cyc):
+                    comb += slave.stb.eq(1)
+                    with m.If(slave.ack):
+                        comb += counter_ce.eq(1)
+                        with m.If(counter_done):
+                            comb += master.ack.eq(1)
+                            comb += master.dat_r.eq(shift_reg)
+                            m.next = "IDLE"
+                with m.Elif(~master.cyc):
+                    m.next = "IDLE"
+
+        # Address
+        if hasattr(slave, 'cti'):
+            with m.If(counter_done):
+                comb += slave.cti.eq(7) # indicate end of burst
+            with m.Else():
+                comb += slave.cti.eq(2)
+        comb += slave.adr.eq(Cat(counter, master.adr))
+
+        # write Datapath - select fragments of data, depending on "counter"
+        with m.Switch(counter):
+            slen = slave.sel.width
+            for i in range(ratio):
+                with m.Case(i):
+                    # select fractions of dat_w and associated "sel" bits
+                    print ("sel", i, "from", i*slen, "to", (i+1)*slen)
+                    comb += slave.sel.eq(master.sel[i*slen:(i+1)*slen])
+                    comb += slave.dat_w.eq(master.dat_w[i*dw_to:(i+1)*dw_to])
+
+        # read Datapath - uses cached_data and master.dat_r as a shift-register.
+        # by the time "counter" is done (counter_done) this is complete
+        comb += shift_reg.eq(Cat(cached_data[dw_to:], slave.dat_r))
+        with m.If(read & counter_ce):
+            sync += cached_data.eq(shift_reg)
+
+
+        return m
diff --git a/src/soc/bus/wb_upconvert.py b/src/soc/bus/wb_upconvert.py
deleted file mode 100644 (file)
index 666e488..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-from nmigen import Elaboratable, Module, Signal, Repl, Cat, Mux
-from nmigen.utils import log2_int
-
-class WishboneDownConvert(Elaboratable):
-    """DownConverter
-
-    This module splits Wishbone accesses from a master interface to a smaller
-    slave interface.
-
-    Writes:
-        Writes from master are split N writes to the slave. Access
-        is acked when the last access is acked by the slave.
-
-    Reads:
-        Read from master are split in N reads to the the slave.
-        Read data from the slave are cached before being presented,
-        concatenated on the last access.
-
-    TODO:
-        Manage err signal? (Not implemented since we generally don't
-        use it on Migen/MiSoC modules)
-    """
-    def __init__(self, master, slave):
-        self.master = master
-        self.slave = slave
-
-    def elaborate(self, platform):
-
-        master = self.master
-        slave = self.slave
-        m = Module()
-        comb = m.d.comb
-        sync = m.d.sync
-
-        dw_from = len(master.dat_r)
-        dw_to = len(slave.dat_w)
-        ratio = dw_from//dw_to
-
-        # # #
-
-        read = Signal()
-        write = Signal()
-
-        cached_data = Signal(dw_from)
-        shift_reg = Signal(dw_from)
-
-        counter = Signal(log2_int(ratio, False)+1)
-        counter_reset = Signal()
-        counter_ce = Signal()
-        with m.If(counter_reset):
-            sync += counter.eq(0)
-        with m.Elif(counter_ce):
-            sync += counter.eq(counter + 1)
-
-        counter_done = Signal()
-        comb += counter_done.eq(counter == ratio-1)
-
-        # Main FSM
-        with m.FSM() as fsm:
-            with m.State("IDLE"):
-                comb += counter_reset.eq(1)
-                sync += shift_reg.eq(0)
-                with m.If(master.stb & master.cyc):
-                    with m.If(master.we):
-                        m.next = "WRITE"
-                    with m.Else():
-                        m.next = "READ"
-
-            with m.State("WRITE"):
-                comb += write.eq(1)
-                comb += slave.we.eq(1)
-                comb += slave.cyc.eq(1)
-                with m.If(master.stb & master.cyc):
-                    comb += slave.stb.eq(1)
-                    with m.If(slave.ack):
-                        comb += counter_ce.eq(1)
-                        with m.If(counter_done):
-                            comb += master.ack.eq(1)
-                            m.next = "IDLE"
-                with m.Elif(~master.cyc):
-                    m.next = "IDLE"
-
-            with m.State("READ"):
-                comb += read.eq(1)
-                comb += slave.cyc.eq(1)
-                with m.If(master.stb & master.cyc):
-                    comb += slave.stb.eq(1)
-                    with m.If(slave.ack):
-                        comb += counter_ce.eq(1)
-                        with m.If(counter_done):
-                            comb += master.ack.eq(1)
-                            comb += master.dat_r.eq(cached_data)
-                            m.next = "IDLE"
-                with m.Elif(~master.cyc):
-                    m.next = "IDLE"
-
-        # Address
-        with m.If(counter_done):
-            comb += slave.cti.eq(7) # indicate end of burst
-        with m.Else():
-            comb += slave.cti.eq(2)
-        comb += slave.adr.eq(Cat(counter, master.adr))
-
-        # write Datapath - select fragments of data, depending on "counter"
-        with m.Switch(counter):
-            for i in range(ratio):
-                with m.Case(i):
-                    # select fractions of dat_w and associated "sel" bits
-                    comb += slave.sel.eq(master.sel[i*dw_to//8:(i+1)*dw_to//8])
-                    comb += slave.dat_w.eq(master.dat_w[i*dw_to:(i+1)*dw_to])
-
-        # read Datapath - uses cached_data and master.dat_r as a shift-register.
-        # by the time "counter" is done (counter_done) this is complete
-        comb += shift_reg.eq(Cat(cached_data[dw_to:], slave.dat_r))
-        with m.If(read & counter_ce):
-            sync += cached_data.eq(shift_reg)
-
-