dcache: improve debug output
[soc.git] / src / soc / experiment / dcache.py
index 488203ff9ce9ebf9153c3ba490599906bf303c69..b818347bee37ae62aa6ab1fc5c7d7c4cf8ee3aac 100644 (file)
@@ -2,14 +2,33 @@
 
 based on Anton Blanchard microwatt dcache.vhdl
 
+note that the microwatt dcache wishbone interface expects "stall".
+for simplicity at the moment this is hard-coded to cyc & ~ack.
+see WB4 spec, p84, section 5.2.1
+
+IMPORTANT: for store, the data is sampled the cycle AFTER the "valid"
+is raised.  sigh
+
+Links:
+
+* https://libre-soc.org/3d_gpu/architecture/set_associative_cache.jpg
+* https://bugs.libre-soc.org/show_bug.cgi?id=469
+
 """
 
+import sys
+
+from nmutil.gtkw import write_gtkw
+
+sys.setrecursionlimit(1000000)
+
 from enum import Enum, unique
 
 from nmigen import Module, Signal, Elaboratable, Cat, Repl, Array, Const
 from nmutil.util import Display
 
-from random import randint
+from copy import deepcopy
+from random import randint, seed
 
 from nmigen.cli import main
 from nmutil.iocontrol import RecordObject
@@ -30,13 +49,14 @@ from soc.experiment.cache_ram import CacheRam
 from nmutil.plru import PLRU
 
 # for test
-from nmigen_soc.wishbone.sram import SRAM
+from soc.bus.sram import SRAM
 from nmigen import Memory
 from nmigen.cli import rtlil
-if True:
-    from nmigen.back.pysim import Simulator, Delay, Settle
-else:
-    from nmigen.sim.cxxsim import Simulator, Delay, Settle
+
+# NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell
+# Also, check out the cxxsim nmigen branch, and latest yosys from git
+from nmutil.sim_tmp_alternative import Simulator
+
 from nmutil.util import wrap
 
 
@@ -121,7 +141,7 @@ layout = """\
   .. --------|              | TAG_BITS      (45)
 """
 print (layout)
-print ("Dcache TAG %d IDX %d ROW %d ROFF %d LOFF %d RLB %d" % \
+print ("Dcache TAG %d IDX %d ROW_BITS %d ROFF %d LOFF %d RLB %d" % \
             (TAG_BITS, INDEX_BITS, ROW_BITS,
              ROW_OFF_BITS, LINE_OFF_BITS, ROW_LINE_BITS))
 print ("index @: %d-%d" % (LINE_OFF_BITS, SET_SIZE_BITS))
@@ -130,6 +150,8 @@ print ("tag @: %d-%d width %d" % (SET_SIZE_BITS, REAL_ADDR_BITS, TAG_WIDTH))
 
 TAG_RAM_WIDTH = TAG_WIDTH * NUM_WAYS
 
+print ("TAG_RAM_WIDTH", TAG_RAM_WIDTH)
+
 def CacheTagArray():
     return Array(Signal(TAG_RAM_WIDTH, name="cachetag_%d" % x) \
                         for x in range(NUM_LINES))
@@ -317,10 +339,11 @@ class RegStage0(RecordObject):
     def __init__(self, name=None):
         super().__init__(name=name)
         self.req     = LoadStore1ToDCacheType(name="lsmem")
-        self.tlbie   = Signal()
-        self.doall   = Signal()
-        self.tlbld   = Signal()
+        self.tlbie   = Signal() # indicates a tlbie request (from MMU)
+        self.doall   = Signal() # with tlbie, indicates flush whole TLB
+        self.tlbld   = Signal() # indicates a TLB load request (from MMU)
         self.mmu_req = Signal() # indicates source of request
+        self.d_valid = Signal() # indicates req.data is valid now
 
 
 class MemAccessRequest(RecordObject):
@@ -374,7 +397,6 @@ class RegStage1(RecordObject):
         self.write_bram       = Signal()
         self.write_tag        = Signal()
         self.slow_valid       = Signal()
-        self.real_adr         = Signal(REAL_ADDR_BITS)
         self.wb               = WBMasterOut("wb")
         self.reload_tag       = Signal(TAG_BITS)
         self.store_way        = Signal(WAY_BITS)
@@ -422,11 +444,11 @@ class DTLBUpdate(Elaboratable):
         self.eatag           = Signal(TLB_EA_TAG_BITS)
         self.pte_data        = Signal(TLB_PTE_BITS)
 
-        self.dv = Signal(TLB_PTE_WAY_BITS)
+        self.dv = Signal(TLB_NUM_WAYS) # tlb_way_valids_t
 
-        self.tb_out = Signal(TLB_TAG_WAY_BITS)
-        self.pb_out = Signal(TLB_NUM_WAYS)
-        self.db_out = Signal(TLB_PTE_WAY_BITS)
+        self.tb_out = Signal(TLB_TAG_WAY_BITS) # tlb_way_tags_t
+        self.db_out = Signal(TLB_NUM_WAYS)     # tlb_way_valids_t
+        self.pb_out = Signal(TLB_PTE_WAY_BITS) # tlb_way_ptes_t
 
     def elaborate(self, platform):
         m = Module()
@@ -437,13 +459,13 @@ class DTLBUpdate(Elaboratable):
         pteset   = Signal(TLB_PTE_WAY_BITS)
 
         tb_out, pb_out, db_out = self.tb_out, self.pb_out, self.db_out
+        comb += db_out.eq(self.dv)
 
         with m.If(self.tlbie & self.doall):
             pass # clear all back in parent
         with m.Elif(self.tlbie):
             with m.If(self.tlb_hit):
-                comb += db_out.eq(self.dv)
-                comb += db_out.bit_select(self.tlb_hit_way, 1).eq(1)
+                comb += db_out.bit_select(self.tlb_hit_way, 1).eq(0)
                 comb += self.v_updated.eq(1)
 
         with m.Elif(self.tlbwe):
@@ -520,7 +542,7 @@ class DCachePendingHit(Elaboratable):
         # the TLB, and then decide later which match to use.
 
         with m.If(virt_mode):
-            for j in range(TLB_NUM_WAYS):
+            for j in range(TLB_NUM_WAYS): # tlb_num_way_t
                 s_tag       = Signal(TAG_BITS, name="s_tag%d" % j)
                 s_hit       = Signal()
                 s_pte       = Signal(TLB_PTE_BITS)
@@ -530,7 +552,7 @@ class DCachePendingHit(Elaboratable):
                                     s_pte[TLB_LG_PGSZ:REAL_ADDR_BITS]))
                 comb += s_tag.eq(get_tag(s_ra))
 
-                for i in range(NUM_WAYS):
+                for i in range(NUM_WAYS): # way_t
                     is_tag_hit = Signal(name="is_tag_hit_%d_%d" % (j, i))
                     comb += is_tag_hit.eq(go & cache_valid_idx[i] &
                                   (read_tag(i, cache_tag_set) == s_tag)
@@ -548,7 +570,7 @@ class DCachePendingHit(Elaboratable):
         with m.Else():
             s_tag       = Signal(TAG_BITS)
             comb += s_tag.eq(get_tag(req_addr))
-            for i in range(NUM_WAYS):
+            for i in range(NUM_WAYS): # way_t
                 is_tag_hit = Signal(name="is_tag_hit_%d" % i)
                 comb += is_tag_hit.eq(go & cache_valid_idx[i] &
                           (read_tag(i, cache_tag_set) == s_tag))
@@ -563,6 +585,7 @@ class DCachePendingHit(Elaboratable):
 
 class DCache(Elaboratable):
     """Set associative dcache write-through
+
     TODO (in no specific order):
     * See list in icache.vhdl
     * Complete load misses on the cycle when WB data comes instead of
@@ -578,8 +601,8 @@ class DCache(Elaboratable):
 
         self.stall_out = Signal()
 
-        self.wb_out    = WBMasterOut()
-        self.wb_in     = WBSlaveOut()
+        self.wb_out    = WBMasterOut("wb_out")
+        self.wb_in     = WBSlaveOut("wb_in")
 
         self.log_out   = Signal(20)
 
@@ -597,29 +620,45 @@ class DCache(Elaboratable):
             sync += Display("request collision loadstore vs MMU")
 
         with m.If(m_in.valid):
-            sync += r.req.valid.eq(1)
-            sync += r.req.load.eq(~(m_in.tlbie | m_in.tlbld))
-            sync += r.req.dcbz.eq(0)
-            sync += r.req.nc.eq(0)
-            sync += r.req.reserve.eq(0)
-            sync += r.req.virt_mode.eq(0)
-            sync += r.req.priv_mode.eq(1)
-            sync += r.req.addr.eq(m_in.addr)
-            sync += r.req.data.eq(m_in.pte)
-            sync += r.req.byte_sel.eq(~0) # Const -1 sets all to 0b111....
-            sync += r.tlbie.eq(m_in.tlbie)
-            sync += r.doall.eq(m_in.doall)
-            sync += r.tlbld.eq(m_in.tlbld)
-            sync += r.mmu_req.eq(1)
+            comb += r.req.valid.eq(1)
+            comb += r.req.load.eq(~(m_in.tlbie | m_in.tlbld))# no invalidate
+            comb += r.req.dcbz.eq(0)
+            comb += r.req.nc.eq(0)
+            comb += r.req.reserve.eq(0)
+            comb += r.req.virt_mode.eq(0)
+            comb += r.req.priv_mode.eq(1)
+            comb += r.req.addr.eq(m_in.addr)
+            comb += r.req.data.eq(m_in.pte)
+            comb += r.req.byte_sel.eq(~0) # Const -1 sets all to 0b111....
+            comb += r.tlbie.eq(m_in.tlbie)
+            comb += r.doall.eq(m_in.doall)
+            comb += r.tlbld.eq(m_in.tlbld)
+            comb += r.mmu_req.eq(1)
+            m.d.sync += Display("    DCACHE req mmu addr %x pte %x ld %d",
+                                 m_in.addr, m_in.pte, r.req.load)
+
         with m.Else():
-            sync += r.req.eq(d_in)
-            sync += r.tlbie.eq(0)
-            sync += r.doall.eq(0)
-            sync += r.tlbld.eq(0)
-            sync += r.mmu_req.eq(0)
-            with m.If(~(r1.full & r0_full)):
-                sync += r0.eq(r)
-                sync += r0_full.eq(r.req.valid)
+            comb += r.req.eq(d_in)
+            comb += r.req.data.eq(0)
+            comb += r.tlbie.eq(0)
+            comb += r.doall.eq(0)
+            comb += r.tlbld.eq(0)
+            comb += r.mmu_req.eq(0)
+        with m.If((~r1.full & ~d_in.hold) | ~r0_full):
+            sync += r0.eq(r)
+            sync += r0_full.eq(r.req.valid)
+            # Sample data the cycle after a request comes in from loadstore1.
+            # If another request has come in already then the data will get
+            # put directly into req.data below.
+            with m.If(r0.req.valid & ~r.req.valid & ~r0.d_valid &
+                     ~r0.mmu_req):
+                sync += r0.req.data.eq(d_in.data)
+                sync += r0.d_valid.eq(1)
+        with m.If(d_in.valid):
+            m.d.sync += Display("    DCACHE req cache "
+                                "virt %d addr %x data %x ld %d",
+                                 r.req.virt_mode, r.req.addr,
+                                 r.req.data, r.req.load)
 
     def tlb_read(self, m, r0_stall, tlb_valid_way,
                  tlb_tag_way, tlb_pte_way, dtlb_valid_bits,
@@ -675,7 +714,6 @@ class DCache(Elaboratable):
                    tlb_pte_way, pte, tlb_hit, valid_ra, perm_attr, ra):
 
         comb = m.d.comb
-        sync = m.d.sync
 
         hitway = Signal(TLB_WAY_BITS)
         hit    = Signal()
@@ -686,9 +724,10 @@ class DCache(Elaboratable):
         comb += eatag.eq(r0.req.addr[TLB_LG_END : 64 ])
 
         for i in range(TLB_NUM_WAYS):
-            is_tag_hit = Signal()
-            comb += is_tag_hit.eq(tlb_valid_way[i]
-                                  & (read_tlb_tag(i, tlb_tag_way) == eatag))
+            is_tag_hit = Signal(name="is_tag_hit%d" % i)
+            tlb_tag = Signal(TLB_EA_TAG_BITS, name="tlb_tag%d" % i)
+            comb += tlb_tag.eq(read_tlb_tag(i, tlb_tag_way))
+            comb += is_tag_hit.eq(tlb_valid_way[i] & (tlb_tag == eatag))
             with m.If(is_tag_hit):
                 comb += hitway.eq(i)
                 comb += hit.eq(1)
@@ -698,9 +737,8 @@ class DCache(Elaboratable):
 
         with m.If(tlb_hit):
             comb += pte.eq(read_tlb_pte(hitway, tlb_pte_way))
-        with m.Else():
-            comb += pte.eq(0)
         comb += valid_ra.eq(tlb_hit | ~r0.req.virt_mode)
+
         with m.If(r0.req.virt_mode):
             comb += ra.eq(Cat(Const(0, ROW_OFF_BITS),
                               r0.req.addr[ROW_OFF_BITS:TLB_LG_PGSZ],
@@ -714,7 +752,6 @@ class DCache(Elaboratable):
         with m.Else():
             comb += ra.eq(Cat(Const(0, ROW_OFF_BITS),
                               r0.req.addr[ROW_OFF_BITS:REAL_ADDR_BITS]))
-
             comb += perm_attr.reference.eq(1)
             comb += perm_attr.changed.eq(1)
             comb += perm_attr.nocache.eq(0)
@@ -722,10 +759,22 @@ class DCache(Elaboratable):
             comb += perm_attr.rd_perm.eq(1)
             comb += perm_attr.wr_perm.eq(1)
 
+        with m.If(valid_ra):
+            m.d.sync += Display("DCACHE virt mode %d hit %d ra %x pte %x",
+                                r0.req.virt_mode, tlb_hit, ra, pte)
+            m.d.sync += Display("       perm ref=%d", perm_attr.reference)
+            m.d.sync += Display("       perm chg=%d", perm_attr.changed)
+            m.d.sync += Display("       perm noc=%d", perm_attr.nocache)
+            m.d.sync += Display("       perm prv=%d", perm_attr.priv)
+            m.d.sync += Display("       perm rdp=%d", perm_attr.rd_perm)
+            m.d.sync += Display("       perm wrp=%d", perm_attr.wr_perm)
+
     def tlb_update(self, m, r0_valid, r0, dtlb_valid_bits, tlb_req_index,
                     tlb_hit_way, tlb_hit, tlb_plru_victim, tlb_tag_way,
                     dtlb_tags, tlb_pte_way, dtlb_ptes):
 
+        dtlb_valids = TLBValidBitsArray()
+
         comb = m.d.comb
         sync = m.d.sync
 
@@ -813,7 +862,6 @@ class DCache(Elaboratable):
         """
 
         comb = m.d.comb
-        sync = m.d.sync
         m_in, d_in = self.m_in, self.d_in
 
         is_hit      = Signal()
@@ -862,7 +910,7 @@ class DCache(Elaboratable):
             rrow = Signal(ROW_LINE_BITS)
             comb += rrow.eq(req_row)
             valid = r1.rows_valid[rrow]
-            comb += is_hit.eq(~r0.req.load | valid)
+            comb += is_hit.eq((~r0.req.load) | valid)
             comb += hit_way.eq(replace_way)
 
         # Whether to use forwarded data for a load or not
@@ -892,9 +940,8 @@ class DCache(Elaboratable):
         # work out whether we have permission for this access
         # NB we don't yet implement AMR, thus no KUAP
         comb += rc_ok.eq(perm_attr.reference
-                         & (r0.req.load | perm_attr.changed)
-                )
-        comb += perm_ok.eq((r0.req.priv_mode | ~perm_attr.priv) &
+                         & (r0.req.load | perm_attr.changed))
+        comb += perm_ok.eq((r0.req.priv_mode | (~perm_attr.priv)) &
                            (perm_attr.wr_perm |
                               (r0.req.load & perm_attr.rd_perm)))
         comb += access_ok.eq(valid_ra & perm_ok & rc_ok)
@@ -904,10 +951,15 @@ class DCache(Elaboratable):
         comb += op.eq(Op.OP_NONE)
         with m.If(go):
             with m.If(~access_ok):
+                m.d.sync += Display("DCACHE access fail valid_ra=%d p=%d rc=%d",
+                                 valid_ra, perm_ok, rc_ok)
                 comb += op.eq(Op.OP_BAD)
             with m.Elif(cancel_store):
+                m.d.sync += Display("DCACHE cancel store")
                 comb += op.eq(Op.OP_STCX_FAIL)
             with m.Else():
+                m.d.sync += Display("DCACHE valid_ra=%d nc=%d ld=%d",
+                                 valid_ra, nc, r0.req.load)
                 comb += opsel.eq(Cat(is_hit, nc, r0.req.load))
                 with m.Switch(opsel):
                     with m.Case(0b101): comb += op.eq(Op.OP_LOAD_HIT)
@@ -938,16 +990,15 @@ class DCache(Elaboratable):
         """Handle load-with-reservation and store-conditional instructions
         """
         comb = m.d.comb
-        sync = m.d.sync
 
         with m.If(r0_valid & r0.req.reserve):
             # XXX generate alignment interrupt if address
             # is not aligned XXX or if r0.req.nc = '1'
             with m.If(r0.req.load):
-                comb += set_rsrv.eq(1) # load with reservation
+                comb += set_rsrv.eq(r0.req.atomic_last) # load with reservation
             with m.Else():
-                comb += clear_rsrv.eq(1) # store conditional
-                with m.If(~reservation.valid |
+                comb += clear_rsrv.eq(r0.req.atomic_last) # store conditional
+                with m.If((~reservation.valid) |
                          (r0.req.addr[LINE_OFF_BITS:64] != reservation.addr)):
                     comb += cancel_store.eq(1)
 
@@ -964,7 +1015,7 @@ class DCache(Elaboratable):
                 sync += reservation.valid.eq(1)
                 sync += reservation.addr.eq(r0.req.addr[LINE_OFF_BITS:64])
 
-    def writeback_control(self, m, r1, cache_out):
+    def writeback_control(self, m, r1, cache_out_row):
         """Return data for loads & completion control logic
         """
         comb = m.d.comb
@@ -983,7 +1034,7 @@ class DCache(Elaboratable):
         with m.Else():
             comb += data_fwd.eq(r1.forward_data2)
 
-        comb += data_out.eq(cache_out[r1.hit_way])
+        comb += data_out.eq(cache_out_row)
 
         for i in range(8):
             with m.If(r1.forward_sel[i]):
@@ -1028,12 +1079,15 @@ class DCache(Elaboratable):
 
             # error cases complete without stalling
             with m.If(r1.ls_error):
-                sync += Display("completing ld/st with error")
+                with m.If(r1.dcbz):
+                    sync += Display("completing dcbz with error")
+                with m.Else():
+                    sync += Display("completing ld/st with error")
 
             # Slow ops (load miss, NC, stores)
             with m.If(r1.slow_valid):
-                sync += Display("completing store or load miss data=%x",
-                                data_out)
+                sync += Display("completing store or load miss adr=%x data=%x",
+                                r1.req.real_addr, data_out)
 
         with m.Else():
             # Request came from MMU
@@ -1046,10 +1100,10 @@ class DCache(Elaboratable):
 
             # Slow ops (i.e. load miss)
             with m.If(r1.slow_valid):
-                sync += Display("completing MMU load miss, data=%x",
-                                m_out.data)
+                sync += Display("completing MMU load miss, adr=%x data=%x",
+                                r1.req.real_addr, m_out.data)
 
-    def rams(self, m, r1, early_req_row, cache_out, replace_way):
+    def rams(self, m, r1, early_req_row, cache_out_row, replace_way):
         """rams
         Generate a cache RAM for each way. This handles the normal
         reads, writes from reloads and the special store-hit update
@@ -1065,15 +1119,15 @@ class DCache(Elaboratable):
 
         for i in range(NUM_WAYS):
             do_read  = Signal(name="do_rd%d" % i)
-            rd_addr  = Signal(ROW_BITS)
+            rd_addr  = Signal(ROW_BITS, name="rd_addr_%d" % i)
             do_write = Signal(name="do_wr%d" % i)
-            wr_addr  = Signal(ROW_BITS)
-            wr_data  = Signal(WB_DATA_BITS)
+            wr_addr  = Signal(ROW_BITS, name="wr_addr_%d" % i)
+            wr_data  = Signal(WB_DATA_BITS, name="din_%d" % i)
             wr_sel   = Signal(ROW_SIZE)
             wr_sel_m = Signal(ROW_SIZE)
-            _d_out   = Signal(WB_DATA_BITS, name="dout_%d" % i)
+            _d_out   = Signal(WB_DATA_BITS, name="dout_%d" % i) # cache_row_t
 
-            way = CacheRam(ROW_BITS, WB_DATA_BITS, True)
+            way = CacheRam(ROW_BITS, WB_DATA_BITS, ADD_BUF=True, ram_num=i)
             setattr(m.submodules, "cacheram_%d" % i, way)
 
             comb += way.rd_en.eq(do_read)
@@ -1085,8 +1139,9 @@ class DCache(Elaboratable):
 
             # Cache hit reads
             comb += do_read.eq(1)
-            comb += rd_addr.eq(early_req_row[:ROW_BITS])
-            comb += cache_out[i].eq(_d_out)
+            comb += rd_addr.eq(early_req_row)
+            with m.If(r1.hit_way == i):
+                comb += cache_out_row.eq(_d_out)
 
             # Write mux:
             #
@@ -1113,9 +1168,9 @@ class DCache(Elaboratable):
                 comb += wr_addr.eq(r1.store_row)
                 comb += wr_sel.eq(~0) # all 1s
 
-            with m.If((r1.state == State.RELOAD_WAIT_ACK)
-                      & wb_in.ack & (replace_way == i)):
-                comb += do_write.eq(1)
+                with m.If((r1.state == State.RELOAD_WAIT_ACK)
+                          & wb_in.ack & (replace_way == i)):
+                    comb += do_write.eq(1)
 
             # Mask write selects with do_write since BRAM
             # doesn't have a global write-enable
@@ -1156,19 +1211,20 @@ class DCache(Elaboratable):
             sync += r1.cache_hit.eq(0)
 
         with m.If(req_op == Op.OP_BAD):
-            # Display(f"Signalling ld/st error valid_ra={valid_ra}"
-            #      f"rc_ok={rc_ok} perm_ok={perm_ok}"
+            sync += Display("Signalling ld/st error "
+                            "ls_error=%i mmu_error=%i cache_paradox=%i",
+                            ~r0.mmu_req,r0.mmu_req,access_ok)
             sync += r1.ls_error.eq(~r0.mmu_req)
             sync += r1.mmu_error.eq(r0.mmu_req)
             sync += r1.cache_paradox.eq(access_ok)
 
-            with m.Else():
-                sync += r1.ls_error.eq(0)
-                sync += r1.mmu_error.eq(0)
-                sync += r1.cache_paradox.eq(0)
+        with m.Else():
+            sync += r1.ls_error.eq(0)
+            sync += r1.mmu_error.eq(0)
+            sync += r1.cache_paradox.eq(0)
 
         with m.If(req_op == Op.OP_STCX_FAIL):
-            r1.stcx_fail.eq(1)
+            sync += r1.stcx_fail.eq(1)
         with m.Else():
             sync += r1.stcx_fail.eq(0)
 
@@ -1193,10 +1249,9 @@ class DCache(Elaboratable):
         comb = m.d.comb
         sync = m.d.sync
         wb_in = self.wb_in
+        d_in = self.d_in
 
         req         = MemAccessRequest("mreq_ds")
-        acks        = Signal(3)
-        adjust_acks = Signal(3)
 
         req_row = Signal(ROW_BITS)
         req_idx = Signal(INDEX_BITS)
@@ -1240,8 +1295,7 @@ class DCache(Elaboratable):
         # complete tlbies and TLB loads in the third cycle
         sync += r1.mmu_done.eq(r0_valid & (r0.tlbie | r0.tlbld))
 
-        with m.If((req_op == Op.OP_LOAD_HIT)
-                  | (req_op == Op.OP_STCX_FAIL)):
+        with m.If((req_op == Op.OP_LOAD_HIT) | (req_op == Op.OP_STCX_FAIL)):
             with m.If(~r0.mmu_req):
                 sync += r1.ls_valid.eq(1)
             with m.Else():
@@ -1253,6 +1307,11 @@ class DCache(Elaboratable):
                 with m.If(i == replace_way):
                     ct = Signal(TAG_RAM_WIDTH)
                     comb += ct.eq(cache_tags[r1.store_index])
+                    """
+TODO: check this
+cache_tags(r1.store_index)((i + 1) * TAG_WIDTH - 1 downto i * TAG_WIDTH) <=
+                    (TAG_WIDTH - 1 downto TAG_BITS => '0') & r1.reload_tag;
+                    """
                     comb += ct.word_select(i, TAG_WIDTH).eq(r1.reload_tag)
                     sync += cache_tags[r1.store_index].eq(ct)
             sync += r1.store_way.eq(replace_way)
@@ -1269,10 +1328,13 @@ class DCache(Elaboratable):
             comb += req.dcbz.eq(r0.req.dcbz)
             comb += req.real_addr.eq(ra)
 
-            with m.If(~r0.req.dcbz):
+            with m.If(r0.req.dcbz):
+                # force data to 0 for dcbz
+                comb += req.data.eq(0)
+            with m.Elif(r0.d_valid):
                 comb += req.data.eq(r0.req.data)
             with m.Else():
-                comb += req.data.eq(0)
+                comb += req.data.eq(d_in.data)
 
             # Select all bytes for dcbz
             # and for cacheable loads
@@ -1297,7 +1359,7 @@ class DCache(Elaboratable):
         with m.Switch(r1.state):
 
             with m.Case(State.IDLE):
-                sync += r1.real_adr.eq(req.real_addr)
+                sync += r1.wb.adr.eq(req.real_addr[ROW_LINE_BITS:])
                 sync += r1.wb.sel.eq(req.byte_sel)
                 sync += r1.wb.dat.eq(req.data)
                 sync += r1.dcbz.eq(req.dcbz)
@@ -1306,7 +1368,7 @@ class DCache(Elaboratable):
                 # for subsequent stores.
                 sync += r1.store_index.eq(req_idx)
                 sync += r1.store_row.eq(req_row)
-                sync += r1.end_row_ix.eq(get_row_of_line(req_row))
+                sync += r1.end_row_ix.eq(get_row_of_line(req_row)-1)
                 sync += r1.reload_tag.eq(req_tag)
                 sync += r1.req.same_tag.eq(1)
 
@@ -1387,20 +1449,21 @@ class DCache(Elaboratable):
                 # Requests are all sent if stb is 0
                 comb += ld_stbs_done.eq(~r1.wb.stb)
 
+                # If we are still sending requests, was one accepted?
                 with m.If((~wb_in.stall) & r1.wb.stb):
-                    # That was the last word?
-                    # We are done sending.
-                    # Clear stb and set ld_stbs_done
-                    # so we can handle an eventual
-                    # last ack on the same cycle.
-                    with m.If(is_last_row_addr(r1.real_adr, r1.end_row_ix)):
+                    # That was the last word?  We are done sending.
+                    # Clear stb and set ld_stbs_done so we can handle an
+                    # eventual last ack on the same cycle.
+                    # sigh - reconstruct wb adr with 3 extra 0s at front
+                    wb_adr = Cat(Const(0, ROW_OFF_BITS), r1.wb.adr)
+                    with m.If(is_last_row_addr(wb_adr, r1.end_row_ix)):
                         sync += r1.wb.stb.eq(0)
                         comb += ld_stbs_done.eq(1)
 
                     # Calculate the next row address in the current cache line
                     row = Signal(LINE_OFF_BITS-ROW_OFF_BITS)
-                    comb += row.eq(r1.real_adr[ROW_OFF_BITS:])
-                    sync += r1.real_adr[ROW_OFF_BITS:LINE_OFF_BITS].eq(row+1)
+                    comb += row.eq(r1.wb.adr)
+                    sync += r1.wb.adr[:LINE_OFF_BITS-ROW_OFF_BITS].eq(row+1)
 
                 # Incoming acks processing
                 sync += r1.forward_valid1.eq(wb_in.ack)
@@ -1414,10 +1477,10 @@ class DCache(Elaboratable):
                     # Compare the whole address in case the
                     # request in r1.req is not the one that
                     # started this refill.
-                    with m.If(r1.full & r1.req.same_tag &
+                    with m.If(req.valid & r1.req.same_tag &
                               ((r1.dcbz & r1.req.dcbz) |
                                (~r1.dcbz & (r1.req.op == Op.OP_LOAD_MISS))) &
-                                (r1.store_row == get_row(r1.req.real_addr))):
+                                (r1.store_row == get_row(req.real_addr))):
                         sync += r1.full.eq(0)
                         sync += r1.slow_valid.eq(1)
                         with m.If(~r1.mmu_req):
@@ -1438,13 +1501,20 @@ class DCache(Elaboratable):
                         comb += cv.eq(cache_valids[r1.store_index])
                         comb += cv.bit_select(r1.store_way, 1).eq(1)
                         sync += cache_valids[r1.store_index].eq(cv)
+
                         sync += r1.state.eq(State.IDLE)
+                        sync += Display("cache valid set %x "
+                                        "idx %d way %d",
+                                         cv, r1.store_index, r1.store_way)
 
                     # Increment store row counter
                     sync += r1.store_row.eq(next_row(r1.store_row))
 
             with m.Case(State.STORE_WAIT_ACK):
                 st_stbs_done = Signal()
+                acks        = Signal(3)
+                adjust_acks = Signal(3)
+
                 comb += st_stbs_done.eq(~r1.wb.stb)
                 comb += acks.eq(r1.acks_pending)
 
@@ -1463,12 +1533,12 @@ class DCache(Elaboratable):
                     # See if there is another store waiting
                     # to be done which is in the same real page.
                     with m.If(req.valid):
-                        ra = req.real_addr[0:SET_SIZE_BITS]
-                        sync += r1.real_adr[0:SET_SIZE_BITS].eq(ra)
+                        _ra = req.real_addr[ROW_LINE_BITS:SET_SIZE_BITS]
+                        sync += r1.wb.adr[0:SET_SIZE_BITS].eq(_ra)
                         sync += r1.wb.dat.eq(req.data)
                         sync += r1.wb.sel.eq(req.byte_sel)
 
-                    with m.Elif((adjust_acks < 7) & req.same_tag &
+                    with m.If((adjust_acks < 7) & req.same_tag &
                                 ((req.op == Op.OP_STORE_MISS)
                                  | (req.op == Op.OP_STORE_HIT))):
                         sync += r1.wb.stb.eq(1)
@@ -1530,6 +1600,7 @@ class DCache(Elaboratable):
 
         m = Module()
         comb = m.d.comb
+        d_in = self.d_in
 
         # Storage. Hopefully "cache_rows" is a BRAM, the rest is LUTs
         cache_tags       = CacheTagArray()
@@ -1579,7 +1650,7 @@ class DCache(Elaboratable):
         use_forward1_next = Signal()
         use_forward2_next = Signal()
 
-        cache_out         = CacheRamOut()
+        cache_out_row     = Signal(WB_DATA_BITS)
 
         plru_victim       = PLRUOut()
         replace_way       = Signal(WAY_BITS)
@@ -1609,14 +1680,18 @@ class DCache(Elaboratable):
         comb += self.m_out.stall.eq(0)
 
         # Hold off the request in r0 when r1 has an uncompleted request
-        comb += r0_stall.eq(r0_full & r1.full)
-        comb += r0_valid.eq(r0_full & ~r1.full)
+        comb += r0_stall.eq(r0_full & (r1.full | d_in.hold))
+        comb += r0_valid.eq(r0_full & ~r1.full & ~d_in.hold)
         comb += self.stall_out.eq(r0_stall)
 
         # Wire up wishbone request latch out of stage 1
-        comb += r1.wb.adr.eq(r1.real_adr[ROW_OFF_BITS:]) # truncate LSBs
         comb += self.wb_out.eq(r1.wb)
 
+        # deal with litex not doing wishbone pipeline mode
+        # XXX in wrong way.  FIFOs are needed in the SRAM test
+        # so that stb/ack match up
+        comb += self.wb_in.stall.eq(self.wb_out.cyc & ~self.wb_in.ack)
+
         # call sub-functions putting everything together, using shared
         # signals established above
         self.stage_0(m, r0, r1, r0_full)
@@ -1644,8 +1719,8 @@ class DCache(Elaboratable):
                            r0_valid, r0, reservation)
         self.reservation_reg(m, r0_valid, access_ok, set_rsrv, clear_rsrv,
                            reservation, r0)
-        self.writeback_control(m, r1, cache_out)
-        self.rams(m, r1, early_req_row, cache_out, replace_way)
+        self.writeback_control(m, r1, cache_out_row)
+        self.rams(m, r1, early_req_row, cache_out_row, replace_way)
         self.dcache_fast_hit(m, req_op, r0_valid, r0, r1,
                         req_hit_way, req_index, req_tag, access_ok,
                         tlb_hit, tlb_hit_way, tlb_req_index)
@@ -1657,193 +1732,9 @@ class DCache(Elaboratable):
 
         return m
 
-def dcache_load(dut, addr, nc=0):
-    yield dut.d_in.load.eq(1)
-    yield dut.d_in.nc.eq(nc)
-    yield dut.d_in.addr.eq(addr)
-    yield dut.d_in.byte_sel.eq(~0)
-    yield dut.d_in.valid.eq(1)
-    yield
-    yield dut.d_in.valid.eq(0)
-    yield dut.d_in.byte_sel.eq(0)
-    yield
-    while not (yield dut.d_out.valid):
-        yield
-    data = yield dut.d_out.data
-    return data
-
-
-def dcache_store(dut, addr, data, nc=0):
-    yield dut.d_in.load.eq(0)
-    yield dut.d_in.nc.eq(nc)
-    yield dut.d_in.data.eq(data)
-    yield dut.d_in.byte_sel.eq(~0)
-    yield dut.d_in.addr.eq(addr)
-    yield dut.d_in.valid.eq(1)
-    yield
-    yield dut.d_in.valid.eq(0)
-    yield dut.d_in.byte_sel.eq(0)
-    yield
-    while not (yield dut.d_out.valid):
-        yield
-
-
-def dcache_random_sim(dut):
-
-    # start with stack of zeros
-    sim_mem = [0] * 512
-
-    # clear stuff
-    yield dut.d_in.valid.eq(0)
-    yield dut.d_in.load.eq(0)
-    yield dut.d_in.priv_mode.eq(1)
-    yield dut.d_in.nc.eq(0)
-    yield dut.d_in.addr.eq(0)
-    yield dut.d_in.data.eq(0)
-    yield dut.m_in.valid.eq(0)
-    yield dut.m_in.addr.eq(0)
-    yield dut.m_in.pte.eq(0)
-    # wait 4 * clk_period
-    yield
-    yield
-    yield
-    yield
-
-    print ()
-
-    for i in range(256):
-        addr = randint(0, 255)
-        data = randint(0, (1<<64)-1)
-        sim_mem[addr] = data
-        addr *= 8
-
-        print ("testing %x data %x" % (addr, data))
-
-        yield from dcache_load(dut, addr)
-        yield from dcache_store(dut, addr, data)
-
-        addr = randint(0, 255)
-        sim_data = sim_mem[addr]
-        addr *= 8
-
-        data = yield from dcache_load(dut, addr)
-        assert data == sim_data, \
-            "check %x data %x != %x" % (addr, data, sim_data)
-
-    for addr in range(256):
-        data = yield from dcache_load(dut, addr*8)
-        assert data == sim_mem[addr], \
-            "final check %x data %x != %x" % (addr*8, data, sim_mem[addr])
-
-def dcache_sim(dut):
-    # clear stuff
-    yield dut.d_in.valid.eq(0)
-    yield dut.d_in.load.eq(0)
-    yield dut.d_in.priv_mode.eq(1)
-    yield dut.d_in.nc.eq(0)
-    yield dut.d_in.addr.eq(0)
-    yield dut.d_in.data.eq(0)
-    yield dut.m_in.valid.eq(0)
-    yield dut.m_in.addr.eq(0)
-    yield dut.m_in.pte.eq(0)
-    # wait 4 * clk_period
-    yield
-    yield
-    yield
-    yield
-
-    # Cacheable read of address 4
-    data = yield from dcache_load(dut, 0x58)
-    addr = yield dut.d_in.addr
-    assert data == 0x0000001700000016, \
-        f"data @%x=%x expected 0x0000001700000016" % (addr, data)
-
-    # Cacheable read of address 20
-    data = yield from dcache_load(dut, 0x20)
-    addr = yield dut.d_in.addr
-    assert data == 0x0000000900000008, \
-        f"data @%x=%x expected 0x0000000900000008" % (addr, data)
-
-    # Cacheable read of address 30
-    data = yield from dcache_load(dut, 0x530)
-    addr = yield dut.d_in.addr
-    assert data == 0x0000014D0000014C, \
-        f"data @%x=%x expected 0000014D0000014C" % (addr, data)
-
-    # 2nd Cacheable read of address 30
-    data = yield from dcache_load(dut, 0x530)
-    addr = yield dut.d_in.addr
-    assert data == 0x0000014D0000014C, \
-        f"data @%x=%x expected 0000014D0000014C" % (addr, data)
-
-    # Non-cacheable read of address 100
-    data = yield from dcache_load(dut, 0x100, nc=1)
-    addr = yield dut.d_in.addr
-    assert data == 0x0000004100000040, \
-        f"data @%x=%x expected 0000004100000040" % (addr, data)
-
-    # Store at address 530
-    yield from dcache_store(dut, 0x530, 0x121)
-
-    # Store at address 30
-    yield from dcache_store(dut, 0x530, 0x12345678)
-
-    # 3nd Cacheable read of address 530
-    data = yield from dcache_load(dut, 0x530)
-    addr = yield dut.d_in.addr
-    assert data == 0x12345678, \
-        f"data @%x=%x expected 0x12345678" % (addr, data)
-
-    # 4th Cacheable read of address 20
-    data = yield from dcache_load(dut, 0x20)
-    addr = yield dut.d_in.addr
-    assert data == 0x0000000900000008, \
-        f"data @%x=%x expected 0x0000000900000008" % (addr, data)
-
-    yield
-    yield
-    yield
-    yield
-
-
-def test_dcache(mem, test_fn, test_name):
-    dut = DCache()
-
-    memory = Memory(width=64, depth=16*64, init=mem)
-    sram = SRAM(memory=memory, granularity=8)
-
-    m = Module()
-    m.submodules.dcache = dut
-    m.submodules.sram = sram
-
-    m.d.comb += sram.bus.cyc.eq(dut.wb_out.cyc)
-    m.d.comb += sram.bus.stb.eq(dut.wb_out.stb)
-    m.d.comb += sram.bus.we.eq(dut.wb_out.we)
-    m.d.comb += sram.bus.sel.eq(dut.wb_out.sel)
-    m.d.comb += sram.bus.adr.eq(dut.wb_out.adr)
-    m.d.comb += sram.bus.dat_w.eq(dut.wb_out.dat)
-
-    m.d.comb += dut.wb_in.ack.eq(sram.bus.ack)
-    m.d.comb += dut.wb_in.dat.eq(sram.bus.dat_r)
-
-    # nmigen Simulation
-    sim = Simulator(m)
-    sim.add_clock(1e-6)
-
-    sim.add_sync_process(wrap(test_fn(dut)))
-    with sim.write_vcd('test_dcache%s.vcd' % test_name):
-        sim.run()
 
 if __name__ == '__main__':
     dut = DCache()
     vl = rtlil.convert(dut, ports=[])
     with open("test_dcache.il", "w") as f:
         f.write(vl)
-
-    mem = []
-    for i in range(0,512):
-        mem.append((i*2)| ((i*2+1)<<32))
-
-    test_dcache(mem, dcache_sim, "")
-    test_dcache(None, dcache_random_sim, "random")
-