from copy import deepcopy
from random import randint, seed
+from nmigen_soc.wishbone.bus import Interface
+
from nmigen.cli import main
from nmutil.iocontrol import RecordObject
from nmigen.utils import log2_int
# BRAM organisation: We never access more than
# -- WB_DATA_BITS at a time so to save
# -- resources we make the array only that wide, and
-# -- use consecutive indices for to make a cache "line"
+# -- use consecutive indices to make a cache "line"
# --
# -- ROW_SIZE is the width in bytes of the BRAM
# -- (based on WB, so 64-bits)
self.dv = Signal(TLB_NUM_WAYS) # tlb_way_valids_t
self.tb_out = Signal(TLB_TAG_WAY_BITS) # tlb_way_tags_t
- self.pb_out = Signal(TLB_NUM_WAYS) # tlb_way_valids_t
- self.db_out = Signal(TLB_PTE_WAY_BITS) # tlb_way_ptes_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()
pass # clear all back in parent
with m.Elif(self.tlbie):
with m.If(self.tlb_hit):
- 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):
class DCachePendingHit(Elaboratable):
def __init__(self, tlb_pte_way, tlb_valid_way, tlb_hit_way,
- cache_valid_idx, cache_tag_set,
+ cache_i_validdx, cache_tag_set,
req_addr,
hit_set):
self.tlb_hit_way = tlb_hit_way
self.tlb_pte_way = tlb_pte_way
self.tlb_valid_way = tlb_valid_way
- self.cache_valid_idx = cache_valid_idx
+ self.cache_i_validdx = cache_i_validdx
self.cache_tag_set = cache_tag_set
self.req_addr = req_addr
self.hit_set = hit_set
is_hit = self.is_hit
tlb_pte_way = self.tlb_pte_way
tlb_valid_way = self.tlb_valid_way
- cache_valid_idx = self.cache_valid_idx
+ cache_i_validdx = self.cache_i_validdx
cache_tag_set = self.cache_tag_set
req_addr = self.req_addr
tlb_hit_way = self.tlb_hit_way
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] &
+ comb += is_tag_hit.eq(go & cache_i_validdx[i] &
(read_tag(i, cache_tag_set) == s_tag)
& tlb_valid_way[j])
with m.If(is_tag_hit):
comb += s_tag.eq(get_tag(req_addr))
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] &
+ comb += is_tag_hit.eq(go & cache_i_validdx[i] &
(read_tag(i, cache_tag_set) == s_tag))
with m.If(is_tag_hit):
comb += hit_way.eq(i)
self.stall_out = Signal()
- self.wb_out = WBMasterOut("wb_out")
- self.wb_in = WBSlaveOut("wb_in")
+ # standard naming (wired to non-standard for compatibility)
+ self.bus = Interface(addr_width=32,
+ data_width=64,
+ granularity=8,
+ features={'stall'},
+ alignment=0,
+ name="dcache")
self.log_out = Signal(20)
with m.If(m_in.valid):
comb += r.req.valid.eq(1)
- comb += r.req.load.eq(~(m_in.tlbie | m_in.tlbld))
+ 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.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():
comb += r.req.eq(d_in)
comb += r.req.data.eq(0)
~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,
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)
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):
nc = Signal()
hit_set = Array(Signal(name="hit_set_%d" % i) \
for i in range(TLB_NUM_WAYS))
- cache_valid_idx = Signal(NUM_WAYS)
+ cache_i_validdx = Signal(NUM_WAYS)
# Extract line, row and tag from request
comb += req_index.eq(get_index(r0.req.addr))
r0.req.addr, ra, req_index, req_tag, req_row)
comb += go.eq(r0_valid & ~(r0.tlbie | r0.tlbld) & ~r1.ls_error)
- comb += cache_valid_idx.eq(cache_valids[req_index])
+ comb += cache_i_validdx.eq(cache_valids[req_index])
m.submodules.dcache_pend = dc = DCachePendingHit(tlb_pte_way,
tlb_valid_way, tlb_hit_way,
- cache_valid_idx, cache_tag_set,
+ cache_i_validdx, cache_tag_set,
r0.req.addr,
hit_set)
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)
# 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):
# 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_row, replace_way):
"""rams
account by using 1-cycle delayed signals for load hits.
"""
comb = m.d.comb
- wb_in = self.wb_in
+ bus = self.bus
for i in range(NUM_WAYS):
do_read = Signal(name="do_rd%d" % i)
wr_sel_m = Signal(ROW_SIZE)
_d_out = Signal(WB_DATA_BITS, name="dout_%d" % i) # cache_row_t
- way = CacheRam(ROW_BITS, WB_DATA_BITS, ADD_BUF=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)
with m.If(r1.dcbz):
comb += wr_data.eq(0)
with m.Else():
- comb += wr_data.eq(wb_in.dat)
+ comb += wr_data.eq(bus.dat_r)
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)):
+ & bus.ack & (replace_way == i)):
comb += do_write.eq(1)
# Mask write selects with do_write since BRAM
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)
comb = m.d.comb
sync = m.d.sync
- wb_in = self.wb_in
+ bus = self.bus
d_in = self.d_in
req = MemAccessRequest("mreq_ds")
with m.If(r1.dcbz):
sync += r1.forward_data1.eq(0)
with m.Else():
- sync += r1.forward_data1.eq(wb_in.dat)
+ sync += r1.forward_data1.eq(bus.dat_r)
sync += r1.forward_sel1.eq(~0) # all 1s
sync += r1.forward_way1.eq(replace_way)
sync += r1.forward_row1.eq(r1.store_row)
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):
+ with m.If((~bus.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.
sync += r1.wb.adr[:LINE_OFF_BITS-ROW_OFF_BITS].eq(row+1)
# Incoming acks processing
- sync += r1.forward_valid1.eq(wb_in.ack)
- with m.If(wb_in.ack):
+ sync += r1.forward_valid1.eq(bus.ack)
+ with m.If(bus.ack):
srow = Signal(ROW_LINE_BITS)
comb += srow.eq(r1.store_row)
sync += r1.rows_valid[srow].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))
sync += r1.acks_pending.eq(adjust_acks)
# Clear stb when slave accepted request
- with m.If(~wb_in.stall):
+ with m.If(~bus.stall):
# See if there is another store waiting
# to be done which is in the same real page.
with m.If(req.valid):
comb += st_stbs_done.eq(1)
# Got ack ? See if complete.
- with m.If(wb_in.ack):
+ with m.If(bus.ack):
with m.If(st_stbs_done & (adjust_acks == 1)):
sync += r1.state.eq(State.IDLE)
sync += r1.wb.cyc.eq(0)
with m.Case(State.NC_LOAD_WAIT_ACK):
# Clear stb when slave accepted request
- with m.If(~wb_in.stall):
+ with m.If(~bus.stall):
sync += r1.wb.stb.eq(0)
# Got ack ? complete.
- with m.If(wb_in.ack):
+ with m.If(bus.ack):
sync += r1.state.eq(State.IDLE)
sync += r1.full.eq(0)
sync += r1.slow_valid.eq(1)
def dcache_log(self, m, r1, valid_ra, tlb_hit_way, stall_out):
sync = m.d.sync
- d_out, wb_in, log_out = self.d_out, self.wb_in, self.log_out
+ d_out, bus, log_out = self.d_out, self.bus, self.log_out
sync += log_out.eq(Cat(r1.state[:3], valid_ra, tlb_hit_way[:3],
stall_out, req_op[:3], d_out.valid, d_out.error,
- r1.wb.cyc, r1.wb.stb, wb_in.ack, wb_in.stall,
+ r1.wb.cyc, r1.wb.stb, bus.ack, bus.stall,
r1.real_adr[3:6]))
def elaborate(self, platform):
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 += 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)
+ # so that stb/ack match up. same thing done in icache.py
+ comb += self.bus.stall.eq(self.bus.cyc & ~self.bus.ack)
+
+ # Wire up wishbone request latch out of stage 1
+ comb += self.bus.we.eq(r1.wb.we)
+ comb += self.bus.adr.eq(r1.wb.adr)
+ comb += self.bus.sel.eq(r1.wb.sel)
+ comb += self.bus.stb.eq(r1.wb.stb)
+ comb += self.bus.dat_w.eq(r1.wb.dat)
+ comb += self.bus.cyc.eq(r1.wb.cyc)
# call sub-functions putting everything together, using shared
# signals established above
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)
- while not (yield dut.d_out.valid):
- yield
- # yield # data is valid one cycle AFTER valid goes hi? (no it isn't)
- 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.byte_sel.eq(~0)
- yield dut.d_in.addr.eq(addr)
- yield dut.d_in.valid.eq(1)
- yield
- yield dut.d_in.data.eq(data) # leave set, but the cycle AFTER
- yield dut.d_in.valid.eq(0)
- yield dut.d_in.byte_sel.eq(0)
- while not (yield dut.d_out.valid):
- yield
-
-
-def dcache_random_sim(dut, mem, nc=0):
-
- # start copy of mem
- sim_mem = deepcopy(mem)
- memsize = len(sim_mem)
- print ("mem len", memsize)
-
- # 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(1024):
- # sim_mem[i] = i
-
- for i in range(1024):
- addr = randint(0, memsize-1)
- data = randint(0, (1<<64)-1)
- sim_mem[addr] = data
- row = addr
- addr *= 8
-
- print ("random testing %d 0x%x row %d data 0x%x" % (i, addr, row, data))
-
- yield from dcache_load(dut, addr, nc)
- yield from dcache_store(dut, addr, data, nc)
-
- addr = randint(0, memsize-1)
- sim_data = sim_mem[addr]
- row = addr
- addr *= 8
-
- print (" load 0x%x row %d expect data 0x%x" % (addr, row, sim_data))
- data = yield from dcache_load(dut, addr, nc)
- assert data == sim_data, \
- "check addr 0x%x row %d data %x != %x" % (addr, row, data, sim_data)
-
- for addr in range(memsize):
- data = yield from dcache_load(dut, addr*8, nc)
- assert data == sim_mem[addr], \
- "final check %x data %x != %x" % (addr*8, data, sim_mem[addr])
-
-
-def dcache_regression_sim(dut, mem, nc=0):
-
- # start copy of mem
- sim_mem = deepcopy(mem)
- memsize = len(sim_mem)
- print ("mem len", memsize)
-
- # 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
-
- addr = 0
- row = addr
- addr *= 8
-
- print ("random testing %d 0x%x row %d" % (i, addr, row))
-
- yield from dcache_load(dut, addr, nc)
-
- addr = 2
- sim_data = sim_mem[addr]
- row = addr
- addr *= 8
-
- print (" load 0x%x row %d expect data 0x%x" % (addr, row, sim_data))
- data = yield from dcache_load(dut, addr, nc)
- assert data == sim_data, \
- "check addr 0x%x row %d data %x != %x" % (addr, row, data, sim_data)
-
-
-
-def dcache_sim(dut, mem):
- # 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=len(mem), init=mem, simulate=True)
- 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)
-
- dcache_write_gtkw(test_name)
-
- # nmigen Simulation
- sim = Simulator(m)
- sim.add_clock(1e-6)
-
- sim.add_sync_process(wrap(test_fn(dut, mem)))
- with sim.write_vcd('test_dcache%s.vcd' % test_name):
- sim.run()
-
-
-def dcache_write_gtkw(test_name):
- traces = [
- 'clk',
- ('d_in', [
- 'd_in_load', 'd_in_nc', 'd_in_addr[63:0]', 'd_in_data[63:0]',
- 'd_in_byte_sel[7:0]', 'd_in_valid'
- ]),
- ('d_out', [
- 'd_out_valid', 'd_out_data[63:0]'
- ]),
- ('wb_out', [
- 'wb_out_cyc', 'wb_out_stb', 'wb_out_we',
- 'wb_out_adr[31:0]', 'wb_out_sel[7:0]', 'wb_out_dat[63:0]'
- ]),
- ('wb_in', [
- 'wb_in_stall', 'wb_in_ack', 'wb_in_dat[63:0]'
- ])
- ]
- write_gtkw('test_dcache%s.gtkw' % test_name,
- 'test_dcache%s.vcd' % test_name,
- traces, module='top.dcache')
-
if __name__ == '__main__':
- seed(0)
dut = DCache()
vl = rtlil.convert(dut, ports=[])
with open("test_dcache.il", "w") as f:
f.write(vl)
-
- mem = []
- memsize = 16
- for i in range(memsize):
- mem.append(i)
-
- test_dcache(mem, dcache_regression_sim, "simpleregression")
-
- mem = []
- memsize = 256
- for i in range(memsize):
- mem.append(i)
-
- test_dcache(mem, dcache_random_sim, "random")
-
- mem = []
- for i in range(1024):
- mem.append((i*2)| ((i*2+1)<<32))
-
- test_dcache(mem, dcache_sim, "")
-