add a wishbone upconverter
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 20 Aug 2020 13:20:15 +0000 (14:20 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Thu, 20 Aug 2020 13:20:15 +0000 (14:20 +0100)
src/soc/bus/wb_upconvert.py [new file with mode: 0644]

diff --git a/src/soc/bus/wb_upconvert.py b/src/soc/bus/wb_upconvert.py
new file mode 100644 (file)
index 0000000..666e488
--- /dev/null
@@ -0,0 +1,118 @@
+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)
+
+