--- /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
# begin
#
+"""these, because they are constants, can actually be done *as*
+ python asserts:
+ assert LINE_SIZE % ROWSIZE == 0, "line size not ...."
+"""
# assert LINE_SIZE mod ROW_SIZE = 0
# report "LINE_SIZE not multiple of ROW_SIZE" severity FAILURE;
# assert ispow2(LINE_SIZE)
# for testing purposes
from soc.experiment.testmem import TestMemory
+#from soc.scoreboard.addr_split import LDSTSplitter
+
import unittest
"""
from soc.experiment.l0_cache import L0CacheBuffer2
-from nmigen import Module
+from nmigen import Module, Signal, Mux, Elaboratable, Cat, Const
from nmigen.cli import rtlil
from soc.scoreboard.addr_split import LDSTSplitter
from soc.scoreboard.addr_match import LenExpand
from soc.config.test.test_pi2ls import pi_ld, pi_st, pi_ldst
-#cxxsim = False
-#if cxxsim:
-# from nmigen.sim.cxxsim import Simulator, Settle
-#else:
-# from nmigen.back.pysim import Simulator, Settle
+from soc.experiment.pimem import PortInterfaceBase
+
from nmigen.compat.sim import run_simulation, Settle
-def writeMulti(dut):
- for i in range(dut.n_units):
- yield dut.dports[i].is_st_i.eq(1)
- yield dut.dports[i].addr.data.eq(i)
- yield
- # TODO assert that outputs are valid
+class TestCachedMemoryPortInterface(PortInterfaceBase):
+ """TestCacheMemoryPortInterface
+
+ This is a test class for simple verification of LDSTSplitter
+ conforming to PortInterface
+ """
+
+ def __init__(self, regwid=64, addrwid=4):
+ super().__init__(regwid, addrwid)
+ self.ldst = LDSTSplitter(32, 48, 4)
+
+ # TODO implement these
+
+ def set_wr_addr(self, m, addr, mask):
+ lsbaddr, msbaddr = self.splitaddr(addr)
+ #m.d.comb += self.ldst... ### .eq(msbaddr)
+
+ def set_rd_addr(self, m, addr, mask):
+ lsbaddr, msbaddr = self.splitaddr(addr)
+ #m.d.comb += self..eq(msbaddr)
+
+ def set_wr_data(self, m, data, wen):
+ #m.d.comb += self.mem.wrport.data.eq(data) # write st to mem
+ #m.d.comb += self.mem.wrport.en.eq(wen) # enable writes
+ return Const(1, 1) #document return value
-def test_cache_run(dut):
- yield from writeMulti(dut)
+ def get_rd_data(self, m):
+ return self.ldst.ld_data_o.data, Const(1, 1)
+
+ def elaborate(self, platform):
+ m = super().elaborate(platform)
+
+ # add TestMemory as submodule
+ m.submodules.ldst = self.ldst
+
+ return m
+
+ def ports(self):
+ yield from super().ports()
+ # TODO: memory ports
def test_cache_single_run(dut):
#test single byte
data = 0xfeedface
yield from pi_st(dut.pi, addr, data, 1)
-def test_cache():
- dut = L0CacheBuffer2()
-
- #vl = rtlil.convert(dut, ports=dut.ports())
- #with open("test_data_merger.il", "w") as f:
- # f.write(vl)
-
- run_simulation(dut, test_cache_run(dut),
- vcd_name='test_cache.vcd')
-
def test_cache_single():
- dut = LDSTSplitter(8, 48, 4) #data leng in bytes, address bits, select bits
+ dut = TestCachedMemoryPortInterface()
+ #LDSTSplitter(8, 48, 4) #data leng in bytes, address bits, select bits
run_simulation(dut, test_cache_single_run(dut),
vcd_name='test_cache_single.vcd')
if __name__ == '__main__':
- #test_cache()
test_cache_single()
# convenience variables
a = dut.i.ra
b = dut.i.rb
-# ra = dut.i.ra
-# carry_in = dut.i.xer_ca[0]
-# carry_in32 = dut.i.xer_ca[1]
-# so_in = dut.i.xer_so
-# carry_out = dut.o.xer_ca
-# o = dut.o.o
+
+ abs32_a = Signal(32)
+ abs32_b = Signal(32)
+ comb += abs32_a.eq(Mux(a[31], -a[0:32], a[0:32]))
+ comb += abs32_b.eq(Mux(b[31], -b[0:32], b[0:32]))
+
+ abs64_a = Signal(64)
+ abs64_b = Signal(64)
+ comb += abs64_a.eq(Mux(a[63], -a[0:64], a[0:64]))
+ comb += abs64_b.eq(Mux(b[63], -b[0:64], b[0:64]))
# setup random inputs
comb += [a.eq(AnyConst(64)),
# Doesn't mean that the ok signal is always set though.
comb += Assert(dut.o.xer_so.data == dut.i.xer_so)
-
- # signed and signed/32 versions of input a
-# a_signed = Signal(signed(64))
-# a_signed_32 = Signal(signed(32))
-# comb += a_signed.eq(a)
-# comb += a_signed_32.eq(a[0:32])
-
# main assertion of arithmetic operations
with m.Switch(rec.insn_type):
with m.Case(MicrOp.OP_MUL_H32):
comb += Assume(rec.is_32bit) # OP_MUL_H32 is a 32-bit op
+ expected_product = Signal(64)
+ expected_o = Signal.like(expected_product)
+
# unsigned hi32 - mulhwu
with m.If(~rec.is_signed):
- expected_product = Signal(64)
- expected_o = Signal(64)
comb += expected_product.eq(a[0:32] * b[0:32])
comb += expected_o.eq(Repl(expected_product[32:64], 2))
comb += Assert(dut.o.o.data[0:64] == expected_o)
# signed hi32 - mulhw
with m.Else():
- pass
+ prod = Signal.like(expected_product) # intermediate product
+ comb += prod.eq(abs32_a * abs32_b)
+ comb += expected_product.eq(Mux(a[31] ^ b[31], -prod, prod))
+ comb += expected_o.eq(Repl(expected_product[32:64], 2))
+ comb += Assert(dut.o.o.data[0:64] == expected_o)
+
+ with m.Case(MicrOp.OP_MUL_H64):
+ comb += Assume(~rec.is_32bit)
+ expected_product = Signal(128)
+
+ # unsigned hi64 - mulhdu
+ with m.If(~rec.is_signed):
+ comb += expected_product.eq(a[0:64] * b[0:64])
+ comb += Assert(dut.o.o.data[0:64] == expected_product[64:128])
+
+ # signed hi64 - mulhd
+ with m.Else():
+ prod = Signal.like(expected_product) # intermediate product
+ comb += prod.eq(abs64_a * abs64_b)
+ comb += expected_product.eq(Mux(a[63] ^ b[63], -prod, prod))
+ comb += Assert(dut.o.o.data[0:64] == expected_product[64:128])
+
+ # mulli, mullw(o)
+ with m.Case(MicrOp.OP_MUL_L64):
+ with m.If(rec.is_32bit):
+ expected_product = Signal(64)
+# expected_o = Signal.like(expected_product)
+
+ # unsigned lo64 - mulwu
+ with m.If(~rec.is_signed):
+ comb += expected_product.eq(a[0:32] * b[0:32])
+# comb += expected_o.eq(Repl(expected_product[0:32], 2))
+ comb += Assert(dut.o.o.data[0:64] == expected_product[0:64])
+
+ # signed lo64 - mulw
+ with m.Else():
+ pass
+
+ with m.Else(): # is 64-bit
+ pass
return m
m.submodules.dut = dut = ShiftRotMainStage(pspec)
# convenience variables
- a = dut.i.rs
- b = dut.i.rb
- ra = dut.i.a
+ rs = dut.i.rs # register to shift
+ b = dut.i.rb # register containing amount to shift by
+ ra = dut.i.a # source register if masking is to be done
carry_in = dut.i.xer_ca[0]
carry_in32 = dut.i.xer_ca[1]
carry_out = dut.o.xer_ca
md_fields = dut.fields.FormMD
# setup random inputs
- comb += a.eq(AnyConst(64))
+ comb += rs.eq(AnyConst(64))
+ comb += ra.eq(AnyConst(64))
comb += b.eq(AnyConst(64))
comb += carry_in.eq(AnyConst(1))
comb += carry_in32.eq(AnyConst(1))
comb += Assert(dut.o.ctx.op == dut.i.ctx.op)
comb += Assert(dut.o.ctx.muxid == dut.i.ctx.muxid)
- # signed and signed/32 versions of input a
+ # signed and signed/32 versions of input rs
a_signed = Signal(signed(64))
a_signed_32 = Signal(signed(32))
- comb += a_signed.eq(a)
- comb += a_signed_32.eq(a[0:32])
+ comb += a_signed.eq(rs)
+ comb += a_signed_32.eq(rs[0:32])
# masks: start-left
mb = Signal(7, reset_less=True)
with m.Case(MicrOp.OP_SHL):
comb += Assume(ra == 0)
with m.If(rec.is_32bit):
- comb += Assert(o[0:32] == ((a << b[0:6]) & 0xffffffff))
+ comb += Assert(o[0:32] == ((rs << b[0:6]) & 0xffffffff))
comb += Assert(o[32:64] == 0)
with m.Else():
- comb += Assert(o == ((a << b[0:7]) & ((1 << 64)-1)))
+ comb += Assert(o == ((rs << b[0:7]) & ((1 << 64)-1)))
# right-shift: 64/32-bit / signed
with m.Case(MicrOp.OP_SHR):
comb += Assume(ra == 0)
with m.If(~rec.is_signed):
with m.If(rec.is_32bit):
- comb += Assert(o[0:32] == (a[0:32] >> b[0:6]))
+ comb += Assert(o[0:32] == (rs[0:32] >> b[0:6]))
comb += Assert(o[32:64] == 0)
with m.Else():
- comb += Assert(o == (a >> b[0:7]))
+ comb += Assert(o == (rs >> b[0:7]))
with m.Else():
with m.If(rec.is_32bit):
comb += Assert(o[0:32] == (a_signed_32 >> b[0:6]))
- comb += Assert(o[32:64] == Repl(a[31], 32))
+ comb += Assert(o[32:64] == Repl(rs[31], 32))
with m.Else():
comb += Assert(o == (a_signed >> b[0:7]))
with m.Case(MicrOp.OP_EXTSWSLI):
comb += Assume(ra == 0)
with m.If(rec.is_32bit):
- comb += Assert(o[0:32] == ((a << b[0:6]) & 0xffffffff))
+ comb += Assert(o[0:32] == ((rs << b[0:6]) & 0xffffffff))
comb += Assert(o[32:64] == 0)
with m.Else():
# sign-extend to 64 bit
a_s = Signal(64, reset_less=True)
- comb += a_s.eq(exts(a, 32, 64))
+ comb += a_s.eq(exts(rs, 32, 64))
comb += Assert(o == ((a_s << b[0:7]) & ((1 << 64)-1)))
# rlwinm, rlwnm, rlwimi
# *CAN* these even be 64-bit capable? I don't think they are.
with m.Case(MicrOp.OP_RLC):
comb += Assume(ra == 0)
+ comb += Assume(rec.is_32bit)
# Duplicate some signals so that they're much easier to find
# in gtkwave.
comb += mrl.eq(ml & mr)
ainp = Signal(64, reset_less=True, name='A_INP_FOR_RLC')
- comb += ainp.eq(field(a, 32, 63))
+ comb += ainp.eq(field(rs, 32, 63))
sh = Signal(6, reset_less=True, name='SH_FOR_RLC')
comb += sh.eq(b[0:6])
# comb += Assume(mr == 0xFFFFFFFF)
# comb += Assume(ml == 0xFFFFFFFF)
- with m.If(rec.is_32bit):
- comb += Assert(act_ol == exp_ol)
- comb += Assert(field(o, 0, 31) == 0)
+ #with m.If(rec.is_32bit):
+ # comb += Assert(act_ol == exp_ol)
+ # comb += Assert(field(o, 0, 31) == 0)
#TODO
with m.Case(MicrOp.OP_RLCR):
* http://bugs.libre-riscv.org/show_bug.cgi?id=216
"""
-from soc.experiment.pimem import PortInterface
+#from soc.experiment.pimem import PortInterface
from nmigen import Elaboratable, Module, Signal, Record, Array, Const, Cat
from nmutil.latch import SRLatch, latchregister
# cline_wid = 8<<dlen # cache line width: bytes (8) times (2^^dlen)
cline_wid = dwidth*8 # convert bytes to bits
- if(pi is None):
- self.pi = PortInterface()
- else:
- self.pi = pi
-
- self.addr_i = self.pi.addr.data #Signal(awidth, reset_less=True)
+ self.addr_i = Signal(awidth, reset_less=True)
# no match in PortInterface
self.len_i = Signal(dlen, reset_less=True)
self.valid_i = Signal(reset_less=True)
self.valid_o = Signal(reset_less=True)
- self.is_ld_i = self.pi.is_ld_i #Signal(reset_less=True)
- self.is_st_i = self.pi.is_st_i #Signal(reset_less=True)
+ self.is_ld_i = Signal(reset_less=True)
+ self.is_st_i = Signal(reset_less=True)
self.ld_data_o = LDData(dwidth*8, "ld_data_o") #port.ld
self.st_data_i = LDData(dwidth*8, "st_data_i") #port.st
m.submodules.ld2 = ld2 = LDLatch(self.dwidth*8, self.awidth-dlen, mlen)
m.submodules.lenexp = lenexp = LenExpand(self.dlen)
+ #comb += self.pi.addr_ok_o.eq(self.addr_i < 65536) #FIXME 64k limit
+ #comb += self.pi.busy_o.eq(busy)
+
+
# FIXME bytes not bits
# set up len-expander, len to mask. ld1 gets first bit, ld2 gets rest
comb += lenexp.addr_i.eq(self.addr_i)
mask1 = Signal(mlen, reset_less=True)
mask2 = Signal(mlen, reset_less=True)
comb += mask1.eq(lenexp.lexp_o[0:mlen]) # Lo bits of expanded len-mask
- comb += mask2.eq(lenexp.lexp_o[mlen:]) # Hi bits of expanded len-mask
+ comb += mask2.eq(lenexp.lexp_o[mlen:]) # Hi bits of expanded len-mask
# set up new address records: addr1 is "as-is", addr2 is +1
comb += ld1.addr_i.eq(self.addr_i[dlen:])
(ld2.ld_o.data << (ashift2*8)))
with m.If(self.is_st_i):
+ # set busy flag -- required for unit test
for i, (ld, mask) in enumerate(((ld1, mask1),
(ld2, mask2))):
valid = Signal(name="stvalid_i%d" % i, reset_less=True)