from enum import Enum, unique
from nmigen import Module, Signal, Elaboratable, Cat, Repl, Array, Const
-try:
- from nmigen.hdl.ast import Display
-except ImportError:
- def Display(*args):
- return []
+from nmutil.util import Display
from random import randint
from nmigen.cli import main
from nmutil.iocontrol import RecordObject
-from nmutil.util import wrap
from nmigen.utils import log2_int
from soc.experiment.mem_types import (LoadStore1ToDCacheType,
DCacheToLoadStore1Type,
WBIOMasterOut, WBIOSlaveOut)
from soc.experiment.cache_ram import CacheRam
-from soc.experiment.plru import PLRU
+#from soc.experiment.plru import PLRU
+from nmutil.plru import PLRU
# for test
from nmigen_soc.wishbone.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
# TODO: make these parameters of DCache at some point
NUM_LINES = 16 # Number of lines in a set
NUM_WAYS = 4 # Number of ways
TLB_SET_SIZE = 64 # L1 DTLB entries per set
-TLB_NUM_WAYS = 2 # L1 DTLB number of sets
+TLB_NUM_WAYS = 4 # L1 DTLB number of sets
TLB_LG_PGSZ = 12 # L1 DTLB log_2(page_size)
LOG_LENGTH = 0 # Non-zero to enable log data collection
.. --------| | 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))
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))
def TLBValidBitsArray():
- return Array(Signal(TLB_NUM_WAYS) for x in range(TLB_SET_SIZE))
+ return Array(Signal(TLB_NUM_WAYS, name="tlbvalid%d" % x) \
+ for x in range(TLB_SET_SIZE))
def TLBTagEAArray():
- return Array(Signal(TLB_EA_TAG_BITS) for x in range (TLB_NUM_WAYS))
+ return Array(Signal(TLB_EA_TAG_BITS, name="tlbtagea%d" % x) \
+ for x in range (TLB_NUM_WAYS))
def TLBTagsArray():
- return Array(Signal(TLB_TAG_WAY_BITS) for x in range (TLB_SET_SIZE))
+ return Array(Signal(TLB_TAG_WAY_BITS, name="tlbtags%d" % x) \
+ for x in range (TLB_SET_SIZE))
def TLBPtesArray():
- return Array(Signal(TLB_PTE_WAY_BITS) for x in range(TLB_SET_SIZE))
+ return Array(Signal(TLB_PTE_WAY_BITS, name="tlbptes%d" % x) \
+ for x in range(TLB_SET_SIZE))
def HitWaySet():
return Array(Signal(WAY_BITS, name="hitway_%d" % x) \
# PLRU output interface
def PLRUOut():
- return Array(Signal(WAY_BITS) for x in range(NUM_LINES))
+ return Array(Signal(WAY_BITS, name="plru_out%d" % x) \
+ for x in range(NUM_LINES))
# TLB PLRU output interface
def TLBPLRUOut():
- return Array(Signal(TLB_WAY_BITS) for x in range(TLB_SET_SIZE))
+ return Array(Signal(TLB_WAY_BITS, name="tlbplru_out%d" % x) \
+ for x in range(TLB_SET_SIZE))
# Helper functions to decode incoming requests
#
def extract_perm_attr(pte):
pa = PermAttr()
- pa.reference = pte[8]
- pa.changed = pte[7]
- pa.nocache = pte[5]
- pa.priv = pte[3]
- pa.rd_perm = pte[2]
- pa.wr_perm = pte[1]
return pa;
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)
sync += r.req.dcbz.eq(0)
sync += r.req.nc.eq(0)
sync += r.req.reserve.eq(0)
- sync += r.req.virt_mode.eq(1)
+ 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)
return
for i in range(TLB_SET_SIZE):
# TLB PLRU interface
- tlb_plru = PLRU(WAY_BITS)
+ tlb_plru = PLRU(TLB_WAY_BITS)
setattr(m.submodules, "maybe_plru_%d" % i, tlb_plru)
tlb_plru_acc_en = Signal()
comb += tlb_plru_acc_en.eq(r1.tlb_hit & (r1.tlb_hit_index == i))
comb += tlb_plru.acc_en.eq(tlb_plru_acc_en)
- comb += tlb_plru.acc.eq(r1.tlb_hit_way)
+ comb += tlb_plru.acc_i.eq(r1.tlb_hit_way)
comb += tlb_plru_victim[i].eq(tlb_plru.lru_o)
def tlb_search(self, m, tlb_req_index, r0, r0_valid,
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)
+ & (read_tlb_tag(i, tlb_tag_way) == eatag))
with m.If(is_tag_hit):
comb += hitway.eq(i)
comb += hit.eq(1)
comb += ra.eq(Cat(Const(0, ROW_OFF_BITS),
r0.req.addr[ROW_OFF_BITS:TLB_LG_PGSZ],
pte[TLB_LG_PGSZ:REAL_ADDR_BITS]))
- comb += perm_attr.eq(extract_perm_attr(pte))
+ comb += perm_attr.reference.eq(pte[8])
+ comb += perm_attr.changed.eq(pte[7])
+ comb += perm_attr.nocache.eq(pte[5])
+ comb += perm_attr.priv.eq(pte[3])
+ comb += perm_attr.rd_perm.eq(pte[2])
+ comb += perm_attr.wr_perm.eq(pte[1])
with m.Else():
comb += ra.eq(Cat(Const(0, ROW_OFF_BITS),
r0.req.addr[ROW_OFF_BITS:REAL_ADDR_BITS]))
comb += plru_acc_en.eq(r1.cache_hit & (r1.hit_index == i))
comb += plru.acc_en.eq(plru_acc_en)
- comb += plru.acc.eq(r1.hit_way)
+ comb += plru.acc_i.eq(r1.hit_way)
comb += plru_victim[i].eq(plru.lru_o)
def cache_tag_read(self, m, r0_stall, req_index, cache_tag_set, cache_tags):
sync += cache_tag_set.eq(cache_tags[index])
def dcache_request(self, m, r0, ra, req_index, req_row, req_tag,
- r0_valid, r1, cache_valid_bits, replace_way,
+ r0_valid, r1, cache_valids, replace_way,
use_forward1_next, use_forward2_next,
req_hit_way, plru_victim, rc_ok, perm_attr,
valid_ra, perm_ok, access_ok, req_op, req_go,
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_valid_bits[req_index])
+ comb += cache_valid_idx.eq(cache_valids[req_index])
m.submodules.dcache_pend = dc = DCachePendingHit(tlb_pte_way,
tlb_valid_way, tlb_hit_way,
# For a store, consider this a hit even if the row isn't
# valid since it will be by the time we perform the store.
# For a load, check the appropriate row valid bit.
- valid = r1.rows_valid[req_row[:ROW_LINE_BITS]]
+ rrow = Signal(ROW_LINE_BITS)
+ comb += rrow.eq(req_row)
+ valid = r1.rows_valid[rrow]
comb += is_hit.eq(~r0.req.load | valid)
comb += hit_way.eq(replace_way)
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
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]):
sync += Display("completing MMU load miss, data=%x",
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
wr_sel_m = Signal(ROW_SIZE)
_d_out = Signal(WB_DATA_BITS, name="dout_%d" % i)
- way = CacheRam(ROW_BITS, WB_DATA_BITS, True)
+ way = CacheRam(ROW_BITS, WB_DATA_BITS, ADD_BUF=True)
setattr(m.submodules, "cacheram_%d" % i, way)
comb += way.rd_en.eq(do_read)
# Cache hit reads
comb += do_read.eq(1)
comb += rd_addr.eq(early_req_row[:ROW_BITS])
- comb += cache_out[i].eq(_d_out)
+ with m.If(r1.hit_way == i):
+ comb += cache_out_row.eq(_d_out)
# Write mux:
#
# All wishbone requests generation is done here.
# This machine operates at stage 1.
def dcache_slow(self, m, r1, use_forward1_next, use_forward2_next,
- cache_valid_bits, r0, replace_way,
+ cache_valids, r0, replace_way,
req_hit_way, req_same_tag,
r0_valid, req_op, cache_tags, req_go, ra):
with m.Switch(r1.state):
with m.Case(State.IDLE):
- sync += r1.wb.adr.eq(req.real_addr)
+ sync += r1.real_adr.eq(req.real_addr)
sync += r1.wb.sel.eq(req.byte_sel)
sync += r1.wb.dat.eq(req.data)
sync += r1.dcbz.eq(req.dcbz)
# 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.wb.adr, r1.end_row_ix)):
+ with m.If(is_last_row_addr(r1.real_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
- rarange = Signal(LINE_OFF_BITS-ROW_OFF_BITS)
- comb += rarange.eq(r1.wb.adr[ROW_OFF_BITS:LINE_OFF_BITS]+1)
- sync += r1.wb.adr[ROW_OFF_BITS:LINE_OFF_BITS].eq(rarange)
+ 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)
# Incoming acks processing
sync += r1.forward_valid1.eq(wb_in.ack)
with m.If(wb_in.ack):
- sync += r1.rows_valid[r1.store_row[:ROW_LINE_BITS]].eq(1)
+ srow = Signal(ROW_LINE_BITS)
+ comb += srow.eq(r1.store_row)
+ sync += r1.rows_valid[srow].eq(1)
# If this is the data we were looking for,
# we can complete the request next cycle.
# Cache line is now valid
cv = Signal(INDEX_BITS)
- comb += cv.eq(cache_valid_bits[r1.store_index])
+ comb += cv.eq(cache_valids[r1.store_index])
comb += cv.bit_select(r1.store_way, 1).eq(1)
- sync += cache_valid_bits[r1.store_index].eq(cv)
+ sync += cache_valids[r1.store_index].eq(cv)
sync += r1.state.eq(State.IDLE)
# Increment store row counter
# 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.wb.adr[0:SET_SIZE_BITS].eq(ra)
+ sync += r1.real_adr[0:SET_SIZE_BITS].eq(ra)
sync += r1.wb.dat.eq(req.data)
sync += r1.wb.sel.eq(req.byte_sel)
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.adr[3:6]))
+ r1.real_adr[3:6]))
def elaborate(self, platform):
# Storage. Hopefully "cache_rows" is a BRAM, the rest is LUTs
cache_tags = CacheTagArray()
cache_tag_set = Signal(TAG_RAM_WIDTH)
- cache_valid_bits = CacheValidBitsArray()
+ cache_valids = CacheValidBitsArray()
# TODO attribute ram_style : string;
# TODO attribute ram_style of cache_tags : signal is "distributed";
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)
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)
# call sub-functions putting everything together, using shared
self.maybe_tlb_plrus(m, r1, tlb_plru_victim)
self.cache_tag_read(m, r0_stall, req_index, cache_tag_set, cache_tags)
self.dcache_request(m, r0, ra, req_index, req_row, req_tag,
- r0_valid, r1, cache_valid_bits, replace_way,
+ r0_valid, r1, cache_valids, replace_way,
use_forward1_next, use_forward2_next,
req_hit_way, plru_victim, rc_ok, perm_attr,
valid_ra, perm_ok, access_ok, req_op, req_go,
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)
self.dcache_slow(m, r1, use_forward1_next, use_forward2_next,
- cache_valid_bits, r0, replace_way,
+ cache_valids, r0, replace_way,
req_hit_way, req_same_tag,
r0_valid, req_op, cache_tags, req_go, ra)
#self.dcache_log(m, r1, valid_ra, tlb_hit_way, stall_out)
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[3:])
+ 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)
mem.append((i*2)| ((i*2+1)<<32))
test_dcache(mem, dcache_sim, "")
- #test_dcache(None, dcache_random_sim, "random")
+ test_dcache(None, dcache_random_sim, "random")