Merge branch 'master' of git.libre-soc.org:soc
authorCole Poirier <colepoirier@gmail.com>
Fri, 21 Aug 2020 02:46:37 +0000 (19:46 -0700)
committerCole Poirier <colepoirier@gmail.com>
Fri, 21 Aug 2020 02:46:37 +0000 (19:46 -0700)
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/experiment/dcache.py
src/soc/experiment/pimem.py
src/soc/experiment/test/test_l0_cache_buffer2.py
src/soc/fu/mul/formal/proof_main_stage.py
src/soc/fu/shift_rot/formal/proof_main_stage.py
src/soc/scoreboard/addr_split.py

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
index 7cb73e3438816cf0102e89a0c0cd41ad6b10ada5..89b5207ba54b05eebb15fb2b7ce885d5c4958c35 100644 (file)
@@ -1003,6 +1003,10 @@ class Dcache(Elaboratable):
 
 # 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)
index 6361f456793e0de6331505230135664b770ec0c5..3fac8cb649354b110779a250ee71935106065240 100644 (file)
@@ -28,6 +28,8 @@ from soc.scoreboard.addr_match import LenExpand
 
 # for testing purposes
 from soc.experiment.testmem import TestMemory
+#from soc.scoreboard.addr_split import LDSTSplitter
+
 
 import unittest
 
index ae1ae7e8872e1e4f427b21e16367526d08cb9961..354730789112f3d4cb1183091216a793c5d6a1fe 100644 (file)
@@ -3,29 +3,57 @@ test cases for LDSTSplitter and L0CacheBuffer2
 """
 
 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
@@ -33,23 +61,13 @@ def test_cache_single_run(dut):
     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()
index 0b1fe15d6743a0cae6ff8a85fb2f08fccd14207c..e9f505932bc808c52fa92322742563592b2951e1 100644 (file)
@@ -57,12 +57,16 @@ class Driver(Elaboratable):
         # 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)),
@@ -79,30 +83,63 @@ class Driver(Elaboratable):
         # 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
 
index 2f0455f48bc1ba048eef3ee444a1804e6fb9a9ee..05969d31f858f62468e4bf0e051c50f07b36249f 100644 (file)
@@ -59,9 +59,9 @@ class Driver(Elaboratable):
         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
@@ -74,7 +74,8 @@ class Driver(Elaboratable):
         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))
@@ -86,11 +87,11 @@ class Driver(Elaboratable):
         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)
@@ -137,24 +138,24 @@ class Driver(Elaboratable):
             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]))
 
@@ -162,18 +163,19 @@ class Driver(Elaboratable):
             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.
@@ -191,7 +193,7 @@ class Driver(Elaboratable):
                     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])
@@ -223,9 +225,9 @@ class Driver(Elaboratable):
 
 #               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):
index 4a9573382cdcf5bbb280a9450e4591ca8df8c795..3d197f2e9daffabccce04d9b136d0d86b01e4b90 100644 (file)
@@ -6,7 +6,7 @@ Links:
 * 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
@@ -80,19 +80,14 @@ class LDSTSplitter(Elaboratable):
         # 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
@@ -120,6 +115,10 @@ class LDSTSplitter(Elaboratable):
         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)
@@ -127,7 +126,7 @@ class LDSTSplitter(Elaboratable):
         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:])
@@ -177,6 +176,7 @@ class LDSTSplitter(Elaboratable):
                                                (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)