--- /dev/null
+"""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()
--- /dev/null
+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
+++ /dev/null
-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)
-
-