diff --git a/ut_mem_block/dtlb/ld_tlb/__init__.py b/ut_mem_block/dtlb/agent/__init__.py similarity index 100% rename from ut_mem_block/dtlb/ld_tlb/__init__.py rename to ut_mem_block/dtlb/agent/__init__.py diff --git a/ut_mem_block/dtlb/agent/dtlb_agent.py b/ut_mem_block/dtlb/agent/dtlb_agent.py new file mode 100644 index 00000000..6a46f32b --- /dev/null +++ b/ut_mem_block/dtlb/agent/dtlb_agent.py @@ -0,0 +1,295 @@ +from toffee.agent import Agent, driver_method, monitor_method +from toffee import Value +from ..bundle.dtlb_bundle import DTLBBundle, RequestorBundle, CSRBundle, PTWBundle, PMPBundle + +def _canon_sv39_vaddr(va: int) -> int: + """把 64 位 VA 规范化为 Sv39 canonical 形式(按 bit[38] 符号扩展)""" + va = int(va) & ((1 << 64) - 1) + low39 = va & ((1 << 39) - 1) + sign = (low39 >> 38) & 1 + upper = ((-sign) & ((1 << (64 - 39)) - 1)) << 39 + return (upper | low39) & ((1 << 64) - 1) + +class DTLBAgent(Agent): + def __init__(self, dtlb_bundle): + super().__init__(dtlb_bundle) + self.bundle = dtlb_bundle + self.requestor = dtlb_bundle.requestor + self.ptw_resp = dtlb_bundle.ptw.resp + + @driver_method() + async def drive_request( + self, + port: int, + vaddr: int, + cmd: int, + *, + hyperinst: bool = False, + pmp_addr: int | None = None, + hlvx: bool = False, + kill: bool = False, + is_prefetch: bool = False, + no_translate: bool = False, + check_fullva: bool = False, + debug_robIdx_flag: int = 0, + debug_robIdx_value: int = 0, + debug_isFirstIssue: int = 0, + ): + if not 0 <= port < 4: + raise ValueError("Port must be 0..N-1") + + fullva = int(vaddr) & ((1 << 64) - 1) # 完整 64 位 + canon = _canon_sv39_vaddr(fullva) # Sv39 规范化(再按端口位宽截取) + pmp = fullva if pmp_addr is None else int(pmp_addr) + + + # === 驱动 req === + req = self.requestor[port].req + + req.valid.value = 0 + req.bits_vaddr.value = canon & ((1 << 39) - 1) # 你的接口是 50 位,这里保守取低 50 位 + req.bits_fullva.value = fullva # 完整 64 位 + req.bits_checkfullva.value = 1 if check_fullva else 0 + + req.bits_cmd.value = int(cmd) + req.bits_hyperinst.value = 1 if hyperinst else 0 + req.bits_hlvx.value = 1 if hlvx else 0 + req.bits_kill.value = 1 if kill else 0 + req.bits_isPrefetch.value = 1 if is_prefetch else 0 + req.bits_no_translate.value = 1 if no_translate else 0 + + req.bits_pmp_addr.value = pmp & ((1 << 48) - 1) + + req.bits_debug_robIdx_flag.value = 1 if debug_robIdx_flag else 0 + req.bits_debug_robIdx_value.value = int(debug_robIdx_value) & 0xFF + req.bits_debug_isFirstIssue.value = 1 if debug_isFirstIssue else 0 + + while (self.requestor[port].resp.valid.value == 1): + await self.bundle.step() + + req.valid.value = 1 + await Value(self.requestor[port].resp.valid, 1) + req.valid.value = 0 + + resp = self.requestor[port].resp + + miss = int(resp.bits_miss.value) + if miss == 1: + return None + + # print("resp_excp_0_pf_ld : ",resp.bits_excp_0_pf_ld.value, flush=True) + # print("resp_excp_0_pf_st : ",resp.bits_excp_0_pf_st.value, flush=True) + # print("resp_excp_0_af_ld : ",resp.bits_excp_0_af_ld.value, flush=True) + # print("resp_excp_0_af_st : ",resp.bits_excp_0_af_st.value, flush=True) + # print("resp_excp_0_gpf_ld: ",resp.bits_excp_0_gpf_ld.value, flush=True) + # print("resp_excp_0_gpf_st: ",resp.bits_excp_0_gpf_st.value, flush=True) + + gpf = resp.bits_excp_0_gpf_ld.value == 1 or resp.bits_excp_0_gpf_st.value == 1 + pf = resp.bits_excp_0_pf_ld.value == 1 or resp.bits_excp_0_pf_st.value == 1 + af = resp.bits_excp_0_af_ld.value == 1 or resp.bits_excp_0_af_st.value == 1 + if gpf or pf or af: + return -1 # 有异常 + + return int(resp.bits_paddr_0.value) + + + @driver_method() + async def set_ptw_resp(self, vaddr, paddr, level, *, + valid: bool = True, + s2xlate: int = 0, + getGpa: bool = False, + # ---------- S1 entry ---------- + s1_asid: int = 0, + s1_vmid: int = 0, + s1_n: bool = False, + s1_pbmt: int = 0, + s1_perm_d: bool = False, s1_perm_a: bool = True, s1_perm_g: bool = False, + s1_perm_u: bool = True, s1_perm_x: bool = False, s1_perm_w: bool = False, s1_perm_r: bool = True, + s1_v: bool = True, + s1_ppn_low: list[int] | None = None, + s1_valididx: list[int] | None = None, + s1_pteidx: list[int] | None = None, + s1_pf: bool = False, s1_af: bool = False, + # ---------- S2 entry ---------- + s2_tag: int = 0, + s2_vmid: int = 0, + s2_n: bool = False, + s2_pbmt: int = 0, + s2_ppn: int = 0, + s2_perm_d: bool = False, s2_perm_a: bool = True, s2_perm_g: bool = False, + s2_perm_u: bool = False, s2_perm_x: bool = False, s2_perm_w: bool = False, s2_perm_r: bool = True, + s2_level: int = 0, + s2_gpf: bool = False, s2_gaf: bool = False, + ): + vaddr = int(vaddr) & ((1 << 64) - 1) + paddr = int(paddr) & ((1 << 56) - 1) + vpn = (vaddr >> 12) & ((1 << 27) - 1) + ppn = (paddr >> 12) + addr_low = vpn & 0b111 + entry_tag = vpn >> 3 + if s2xlate == 0: + entry_ppn = (ppn >> 3) & ((1 << 21) - 1) + else: + entry_ppn = (ppn >> 3) & ((1 << 26) - 1) + ppn_low_val = ppn & 0b111 + + # -------- (valid 暂时保持 0)-------- + self.ptw_resp.valid.value = 0 + self.ptw_resp.bits_s2xlate.value = s2xlate + self.ptw_resp.bits_getGpa.value = 1 if getGpa else 0 + + # S1 基本字段 + self.ptw_resp.bits_s1_entry_tag.value = entry_tag + self.ptw_resp.bits_s1_entry_asid.value = s1_asid + self.ptw_resp.bits_s1_entry_vmid.value = s1_vmid + self.ptw_resp.bits_s1_entry_n.value = 1 if s1_n else 0 + self.ptw_resp.bits_s1_entry_pbmt.value = s1_pbmt + + self.ptw_resp.bits_s1_entry_perm_d.value = 1 if s1_perm_d else 0 + self.ptw_resp.bits_s1_entry_perm_a.value = 1 if s1_perm_a else 0 + self.ptw_resp.bits_s1_entry_perm_g.value = 1 if s1_perm_g else 0 + self.ptw_resp.bits_s1_entry_perm_u.value = 1 if s1_perm_u else 0 + self.ptw_resp.bits_s1_entry_perm_x.value = 1 if s1_perm_x else 0 + self.ptw_resp.bits_s1_entry_perm_w.value = 1 if s1_perm_w else 0 + self.ptw_resp.bits_s1_entry_perm_r.value = 1 if s1_perm_r else 0 + + self.ptw_resp.bits_s1_entry_level.value = int(level) + self.ptw_resp.bits_s1_entry_v.value = 1 if s1_v else 0 + self.ptw_resp.bits_s1_entry_ppn.value = entry_ppn + self.ptw_resp.bits_s1_addr_low.value = addr_low + + # 8 路列表:清零后只在 addr_low 位置写有效/索引/ppn_low + if s1_ppn_low is None and s1_valididx is None and s1_pteidx is None: + for i in range(8): + self.ptw_resp.bits_s1_ppn_low[i].value = 0 + self.ptw_resp.bits_s1_valididx[i].value = 0 + self.ptw_resp.bits_s1_pteidx[i].value = 0 + self.ptw_resp.bits_s1_ppn_low[addr_low].value = ppn_low_val + self.ptw_resp.bits_s1_valididx[addr_low].value = 1 + self.ptw_resp.bits_s1_pteidx[addr_low].value = 1 + else: + for i in range(8): + self.ptw_resp.bits_s1_ppn_low[i].value = s1_ppn_low[i] & 0b111 + self.ptw_resp.bits_s1_valididx[i].value = 1 if s1_valididx[i] else 0 + self.ptw_resp.bits_s1_pteidx[i].value = 1 if s1_pteidx[i] else 0 + + self.ptw_resp.bits_s1_pf.value = 1 if s1_pf else 0 + self.ptw_resp.bits_s1_af.value = 1 if s1_af else 0 + + # S2 + self.ptw_resp.bits_s2_entry_tag.value = s2_tag + self.ptw_resp.bits_s2_entry_vmid.value = s2_vmid + self.ptw_resp.bits_s2_entry_n.value = 1 if s2_n else 0 + self.ptw_resp.bits_s2_entry_pbmt.value = s2_pbmt + self.ptw_resp.bits_s2_entry_ppn.value = s2_ppn + + self.ptw_resp.bits_s2_entry_perm_d.value = 1 if s2_perm_d else 0 + self.ptw_resp.bits_s2_entry_perm_a.value = 1 if s2_perm_a else 0 + self.ptw_resp.bits_s2_entry_perm_g.value = 1 if s2_perm_g else 0 + self.ptw_resp.bits_s2_entry_perm_u.value = 1 if s2_perm_u else 0 + self.ptw_resp.bits_s2_entry_perm_x.value = 1 if s2_perm_x else 0 + self.ptw_resp.bits_s2_entry_perm_w.value = 1 if s2_perm_w else 0 + self.ptw_resp.bits_s2_entry_perm_r.value = 1 if s2_perm_r else 0 + + self.ptw_resp.bits_s2_entry_level.value = s2_level + self.ptw_resp.bits_s2_gpf.value = 1 if s2_gpf else 0 + self.ptw_resp.bits_s2_gaf.value = 1 if s2_gaf else 0 + + # -------- 对拍:见到某一路 ptw.req 匹配就打一拍 resp -------- + self.ptw_resp.valid.value = 1 + await self.bundle.step() + self.bundle.ptw.resp.valid.value = 0 + + + @monitor_method() + async def monitor_ptw_req(self): + """ + 监测 PTW 请求。 + """ + for port in range(4): + req = self.bundle.ptw.req[port] + if req.valid.value == 1: + return { + "port": port, + "vpn": req.bits_vpn.value, + "tag": req.bits_vpn.value >> 3, + "s2xlate": req.bits_s2xlate.value, + "getGpa": req.bits_getGpa.value + } + return None + + @monitor_method() + async def monitor_ptw_resp(self): + ptw_resp = self.ptw_resp + if ptw_resp.valid.value == 1: + return { + "s2xlate": ptw_resp.bits_s2xlate.value, + "s1_asid": ptw_resp.bits_s1_entry_asid.value, + "s1_entry_tag": ptw_resp.bits_s1_entry_tag.value, + "s1_level": ptw_resp.bits_s1_entry_level.value, + "s1_ppn_low[0]": ptw_resp.bits_s1_ppn_low[0].value, + "s1_ppn_low[1]": ptw_resp.bits_s1_ppn_low[1].value, + "s1_ppn_low[2]": ptw_resp.bits_s1_ppn_low[2].value, + "s1_ppn_low[3]": ptw_resp.bits_s1_ppn_low[3].value, + "s1_ppn_low[4]": ptw_resp.bits_s1_ppn_low[4].value, + "s1_ppn_low[5]": ptw_resp.bits_s1_ppn_low[5].value, + "s1_ppn_low[6]": ptw_resp.bits_s1_ppn_low[6].value, + "s1_ppn_low[7]": ptw_resp.bits_s1_ppn_low[7].value, + "s1_valididx[0]": ptw_resp.bits_s1_valididx[0].value, + "s1_valididx[1]": ptw_resp.bits_s1_valididx[1].value, + "s1_valididx[2]": ptw_resp.bits_s1_valididx[2].value, + "s1_valididx[3]": ptw_resp.bits_s1_valididx[3].value, + "s1_valididx[4]": ptw_resp.bits_s1_valididx[4].value, + "s1_valididx[5]": ptw_resp.bits_s1_valididx[5].value, + "s1_valididx[6]": ptw_resp.bits_s1_valididx[6].value, + "s1_valididx[7]": ptw_resp.bits_s1_valididx[7].value, + "s1_pteidx[0]": ptw_resp.bits_s1_pteidx[0].value, + "s1_pteidx[1]": ptw_resp.bits_s1_pteidx[1].value, + "s1_pteidx[2]": ptw_resp.bits_s1_pteidx[2].value, + "s1_pteidx[3]": ptw_resp.bits_s1_pteidx[3].value, + "s1_pteidx[4]": ptw_resp.bits_s1_pteidx[4].value, + "s1_pteidx[5]": ptw_resp.bits_s1_pteidx[5].value, + "s1_pteidx[6]": ptw_resp.bits_s1_pteidx[6].value, + "s1_pteidx[7]": ptw_resp.bits_s1_pteidx[7].value + } + return None + + @monitor_method() + async def monitor_req(self): + """ + 监测请求。 + """ + for port in range(4): + req = self.requestor[port].req + if req.valid.value == 1: + return { + "port": port, + "vaddr": req.bits_vaddr.value, + "fullva": req.bits_fullva.value, + "cmd": req.bits_cmd.value, + "hyperinst": req.bits_hyperinst.value, + "hlvx": req.bits_hlvx.value, + "kill": req.bits_kill.value, + "isPrefetch": req.bits_isPrefetch.value, + "no_translate": req.bits_no_translate.value, + "pmp_addr": req.bits_pmp_addr.value + } + return None + + @monitor_method() + async def monitor_resp(self): + """ + 监测响应。 + """ + for port in range(4): + resp = self.requestor[port].resp + if resp.valid.value == 1: + return { + "port": port, + "miss": resp.bits_miss.value, + "paddr": resp.bits_paddr_0.value, + "fullva": resp.bits_fullva.value + } + return None + + diff --git a/ut_mem_block/dtlb/ld_tlb/agent/__init__.py b/ut_mem_block/dtlb/bundle/__init__.py similarity index 100% rename from ut_mem_block/dtlb/ld_tlb/agent/__init__.py rename to ut_mem_block/dtlb/bundle/__init__.py diff --git a/ut_mem_block/dtlb/bundle/dtlb_bundle.py b/ut_mem_block/dtlb/bundle/dtlb_bundle.py new file mode 100644 index 00000000..fca3f57c --- /dev/null +++ b/ut_mem_block/dtlb/bundle/dtlb_bundle.py @@ -0,0 +1,149 @@ +from toffee import Bundle, Signal, Signals, SignalList, BundleList + +#------------------------------------------------------------------------ +class SfenceBundle(Bundle): + valid, \ + bits_rs1, \ + bits_rs2, \ + bits_flushPipe, \ + bits_hv, \ + bits_hg = Signals(6) + bits_addr = Signal() + bits_id = Signal() +#------------------------------------------------------------------------ +class ATPBundle(Bundle): + mode = Signal() + asid = Signal() + ppn = Signal() + changed = Signal() + +class HGATPBundle(Bundle): + mode = Signal() + vmid = Signal() + ppn = Signal() + changed = Signal() + +class PMMBundle(Bundle): + mseccfg, menvcfg, henvcfg, hstatus, senvcfg = Signals(5) + +class CSRBundle(Bundle): + priv_mxr, priv_sum, priv_vmxr, priv_vsum, priv_virt, priv_spvp = Signals(6) + priv_imode, priv_dmode = Signals(2) + + Satp = ATPBundle.from_prefix('satp_') + Vsatp = ATPBundle.from_prefix('vsatp_') + HGatp = HGATPBundle.from_prefix('hgatp_') + PMM = PMMBundle.from_prefix('pmm_') +#------------------------------------------------------------------------ +class RequestorReqBundle(Bundle): + valid = Signal() + bits_vaddr = Signal() + bits_fullva = Signal() + bits_checkfullva = Signal() + bits_cmd = Signal() + bits_hyperinst, \ + bits_hlvx, \ + bits_kill, \ + bits_isPrefetch, \ + bits_no_translate = Signals(5) + bits_pmp_addr = Signal() + bits_debug_robIdx_flag = Signal() + bits_debug_robIdx_value = Signal() + bits_debug_isFirstIssue = Signal() + +class RequestorRespBundle(Bundle): + valid = Signal() + bits_paddr_0 = Signal() + bits_paddr_1 = Signal() + bits_gpaddr_0 = Signal() + bits_fullva = Signal() + bits_pbmt_0 = Signal() + bits_miss = Signal() + bits_isForVSnonLeafPTE = Signal() + bits_excp_0_vaNeedExt, \ + bits_excp_0_isHyper, \ + bits_excp_0_gpf_ld, \ + bits_excp_0_gpf_st, \ + bits_excp_0_pf_ld, \ + bits_excp_0_pf_st, \ + bits_excp_0_af_ld, \ + bits_excp_0_af_st = Signals(8) + bits_ptwBack = Signal() + +class RequestorBundle(Bundle): + req = RequestorReqBundle.from_prefix('req_') + resp = RequestorRespBundle.from_prefix('resp_') +#------------------------------------------------------------------------ +class RedirectBundle(Bundle): + valid = Signal() + bits_robIdx_flag = Signal() + bits_robIdx_value = Signal() + bits_level = Signal() +#------------------------------------------------------------------------ +class PTWReqBundle(Bundle): + valid = Signal() + bits_vpn = Signal() + bits_s2xlate = Signal() + bits_getGpa = Signal() +class PTWRespBundle(Bundle): + valid = Signal() + bits_s2xlate = Signal() + # S1 entry + bits_s1_entry_tag = Signal() + bits_s1_entry_asid = Signal() + bits_s1_entry_vmid = Signal() + bits_s1_entry_n = Signal() + bits_s1_entry_pbmt = Signal() + bits_s1_entry_perm_d, \ + bits_s1_entry_perm_a, \ + bits_s1_entry_perm_g, \ + bits_s1_entry_perm_u, \ + bits_s1_entry_perm_x, \ + bits_s1_entry_perm_w, \ + bits_s1_entry_perm_r = Signals(7) + bits_s1_entry_level = Signal() + bits_s1_entry_v = Signal() + bits_s1_entry_ppn = Signal() + bits_s1_addr_low = Signal() + # 列表信号 + bits_s1_ppn_low = SignalList("bits_s1_ppn_low_#", 8) + bits_s1_valididx = SignalList("bits_s1_valididx_#", 8) + bits_s1_pteidx = SignalList("bits_s1_pteidx_#", 8) + bits_s1_pf, bits_s1_af = Signals(2) + # S2 entry + bits_s2_entry_tag = Signal() + bits_s2_entry_vmid = Signal() + bits_s2_entry_n = Signal() + bits_s2_entry_pbmt = Signal() + bits_s2_entry_ppn = Signal() + bits_s2_entry_perm_d, \ + bits_s2_entry_perm_a, \ + bits_s2_entry_perm_g, \ + bits_s2_entry_perm_u, \ + bits_s2_entry_perm_x, \ + bits_s2_entry_perm_w, \ + bits_s2_entry_perm_r = Signals(7) + bits_s2_entry_level = Signal() + bits_s2_gpf, \ + bits_s2_gaf = Signals(2) + + bits_getGpa = Signal() + +class PTWBundle(Bundle): + req = BundleList(PTWReqBundle, "req_#_", 4) + resp = PTWRespBundle.from_prefix('resp_') +#------------------------------------------------------------------------ +class PMPBundle(Bundle): + valid = Signal() + bits_addr = Signal() + bits_cmd = Signal() +#------------------------------------------------------------------------ +class DTLBBundle(Bundle): + sfence = SfenceBundle.from_prefix('sfence_') + csr = CSRBundle.from_prefix('csr_') + hartId = Signal() + requestor = BundleList(RequestorBundle, "requestor_#_", 4) + redirect = RedirectBundle.from_prefix('redirect_') + ptw = PTWBundle.from_prefix('ptw_') + pmp = BundleList(PMPBundle, "pmp_#_", 4) + tlbreplay = SignalList("tlbreplay_#", 4) \ No newline at end of file diff --git a/ut_mem_block/dtlb/ld_tlb/bundle/__init__.py b/ut_mem_block/dtlb/env/__init__.py similarity index 100% rename from ut_mem_block/dtlb/ld_tlb/bundle/__init__.py rename to ut_mem_block/dtlb/env/__init__.py diff --git a/ut_mem_block/dtlb/env/dtlb_env.py b/ut_mem_block/dtlb/env/dtlb_env.py new file mode 100644 index 00000000..17340e72 --- /dev/null +++ b/ut_mem_block/dtlb/env/dtlb_env.py @@ -0,0 +1,75 @@ +# env/dtlb_env.py +from toffee import Env +from ..bundle.dtlb_bundle import DTLBBundle +from ..agent.dtlb_agent import DTLBAgent +from .dtlb_mdl import DTLBPLRURefModel + +class DTLBEnv_PLRU(Env): + def __init__(self, dut): + super().__init__() + self.dut = dut + self.bundle = DTLBBundle.from_prefix("io_").bind(dut) + self.agent = DTLBAgent(self.bundle) + self.mdl = DTLBPLRURefModel() + self.attach(self.mdl) + + async def set_sv39_defaults(self): + csr = self.bundle.csr + csr.priv_mxr.value = 0; csr.priv_sum.value = 0 + csr.priv_vmxr.value = 0; csr.priv_vsum.value = 0 + csr.priv_virt.value = 0; csr.priv_spvp.value = 0 + csr.priv_imode.value = 0; csr.priv_dmode.value = 0 + + csr.Satp.mode.value = 8 # Sv39 常见编码 + csr.Satp.asid.value = 0 + csr.Satp.ppn.value = 0 + csr.Satp.changed.value = 0 + + csr.Vsatp.mode.value = 0 + csr.Vsatp.asid.value = 0 + csr.Vsatp.ppn.value = 0 + csr.Vsatp.changed.value = 0 + + csr.HGatp.mode.value = 0 + csr.HGatp.vmid.value = 0 + csr.HGatp.ppn.value = 0 + csr.HGatp.changed.value = 0 + + sf = self.bundle.sfence + sf.valid.value = 0; sf.bits_rs1.value = 0; sf.bits_rs2.value = 0 + sf.bits_flushPipe.value = 0; sf.bits_hv.value = 0; sf.bits_hg.value = 0 + sf.bits_addr.value = 0; sf.bits_id.value = 0 + +class DTLBEnv(Env): + def __init__(self, dut): + super().__init__() + self.dut = dut + self.bundle = DTLBBundle.from_prefix("io_").bind(dut) + self.agent = DTLBAgent(self.bundle) + + async def set_sv39_defaults(self): + csr = self.bundle.csr + csr.priv_mxr.value = 0; csr.priv_sum.value = 0 + csr.priv_vmxr.value = 0; csr.priv_vsum.value = 0 + csr.priv_virt.value = 0; csr.priv_spvp.value = 0 + csr.priv_imode.value = 0; csr.priv_dmode.value = 0 + + csr.Satp.mode.value = 8 # Sv39 常见编码 + csr.Satp.asid.value = 0 + csr.Satp.ppn.value = 0 + csr.Satp.changed.value = 0 + + csr.Vsatp.mode.value = 0 + csr.Vsatp.asid.value = 0 + csr.Vsatp.ppn.value = 0 + csr.Vsatp.changed.value = 0 + + csr.HGatp.mode.value = 0 + csr.HGatp.vmid.value = 0 + csr.HGatp.ppn.value = 0 + csr.HGatp.changed.value = 0 + + sf = self.bundle.sfence + sf.valid.value = 0; sf.bits_rs1.value = 0; sf.bits_rs2.value = 0 + sf.bits_flushPipe.value = 0; sf.bits_hv.value = 0; sf.bits_hg.value = 0 + sf.bits_addr.value = 0; sf.bits_id.value = 0 \ No newline at end of file diff --git a/ut_mem_block/dtlb/env/dtlb_mdl.py b/ut_mem_block/dtlb/env/dtlb_mdl.py new file mode 100644 index 00000000..e22b94f2 --- /dev/null +++ b/ut_mem_block/dtlb/env/dtlb_mdl.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +from __future__ import annotations +from toffee.model import Model, driver_hook + +def _canon_sv39_vaddr(va: int) -> int: + va = int(va) & ((1 << 64) - 1) + low39 = va & ((1 << 39) - 1) + sign = (low39 >> 38) & 1 + upper = ((-sign) & ((1 << (64 - 39)) - 1)) << 39 + return (upper | low39) & ((1 << 64) - 1) + +def _page_key_from_va(va: int) -> int: + canon = _canon_sv39_vaddr(va) + va39 = canon & ((1 << 39) - 1) + return va39 & ~0xFFF + +# ---------------------- 不平衡 48-way Tree-PLRU(根 32/16) ---------------------- +class TreePLRU48: + class Node: + __slots__ = ("L", "R", "bit", "lch", "rch") + def __init__(self, L, R): + self.L, self.R = L, R + self.bit = 0 + self.lch = None + self.rch = None + + def __init__(self): + self.root = self.Node(32, 16) + def build(n): + if n.L > 1: + n.lch = self.Node(n.L // 2, n.L - n.L // 2) + build(n.lch) + if n.R > 1: + n.rch = self.Node(n.R // 2, n.R - n.R // 2) + build(n.rch) + build(self.root) + + def pick_victim(self) -> int: + n = self.root + idx = 0 + while True: + if n.L + n.R == 1: + break + if n.bit == 1: + idx += n.L + if n.R == 1: + break # 右侧是叶,不再往下 + n = n.rch + else: + if n.L == 1: + break # 左侧是叶,不再往下 + n = n.lch + return idx + + def touch(self, i: int) -> None: + n = self.root + idx = i + while n and (n.L + n.R > 1): + if idx < n.L: + # 走左:把右标冷(bit=1) + n.bit = 1 + if n.L == 1: + break # 左侧是叶,不再向下 + n = n.lch + else: + # 走右:把左标冷(bit=0) + idx -= n.L + n.bit = 0 + if n.R == 1: + break # 右侧是叶,不再向下 + n = n.rch + + def reset(self): + def walk(n): + if not n: return + n.bit = 0 + walk(n.lch); walk(n.rch) + walk(self.root) + + +# ---------------------- 参考模型:48 项全相联 ---------------------- +class DTLBPLRURefModel(Model): + CAP = 48 + + def __init__(self): + super().__init__() + self.entries = [{"valid": False, "va": 0, "pa": 0} for _ in range(self.CAP)] + self.plru = TreePLRU48() + + @driver_hook(agent_name="agent", driver_name="drive_request") + def drive_request( + self, + port: int, + vaddr: int, + cmd: int, + *, + hyperinst: bool = False, + pmp_addr: int | None = None, + hlvx: bool = False, + kill: bool = False, + is_prefetch: bool = False, + no_translate: bool = False, + check_fullva: bool = False, + debug_robIdx_flag: int = 0, + debug_robIdx_value: int = 0, + debug_isFirstIssue: int = 0, + return_on_miss: bool = False, + ): + key = _page_key_from_va(vaddr) + for idx, e in enumerate(self.entries): + if e["valid"] and e["va"] == key: + self.plru.touch(idx) # 命中也要更新 PLRU + return int(e["pa"] | (int(vaddr) & 0xFFF)) + return None + + @driver_hook(agent_name="agent", driver_name="set_ptw_resp") + def set_ptw_resp(self, vaddr, paddr, level, *, + # S1/S2 选择 + valid: bool = True, + s2xlate: int = 0, + getGpa: bool = False, + # ---------- S1 entry ---------- + s1_asid: int = 0, + s1_vmid: int = 0, + s1_n: bool = False, + s1_pbmt: int = 0, + s1_perm_d: bool = False, s1_perm_a: bool = True, s1_perm_g: bool = False, + s1_perm_u: bool = True, s1_perm_x: bool = False, s1_perm_w: bool = False, s1_perm_r: bool = True, + s1_v: bool = True, + s1_ppn_low: list[int] | None = None, + s1_valididx: list[int] | None = None, + s1_pteidx: list[int] | None = None, + s1_pf: bool = False, s1_af: bool = False, + s2_tag: int = 0, + s2_vmid: int = 0, + s2_n: bool = False, + s2_pbmt: int = 0, + s2_ppn: int = 0, + s2_perm_d: bool = False, s2_perm_a: bool = True, s2_perm_g: bool = False, + s2_perm_u: bool = False, s2_perm_x: bool = False, s2_perm_w: bool = False, s2_perm_r: bool = True, + s2_level: int = 0, + s2_gpf: bool = False, s2_gaf: bool = False, + ): + if not valid: + return None + + key = _page_key_from_va(vaddr) + pa_base = int(paddr) & ~0xFFF + + # 若已有同 VA:覆写 + touch(避免同一 VA 出现多份) + for idx, e in enumerate(self.entries): + if e["valid"] and e["va"] == key: + e["pa"] = pa_base + self.plru.touch(idx) + return None + + # 由 PLRU 直接给 wayIdx(即便有 invalid 也不优先用) + victim = self.plru.pick_victim() + self.entries[victim]["valid"] = True + self.entries[victim]["va"] = key + self.entries[victim]["pa"] = pa_base + self.plru.touch(victim) + return None diff --git a/ut_mem_block/dtlb/ld_tlb/test/__init__.py b/ut_mem_block/dtlb/ld_tlb/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/pf_tlb/__init__.py b/ut_mem_block/dtlb/pf_tlb/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/pf_tlb/agent/__init__.py b/ut_mem_block/dtlb/pf_tlb/agent/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/pf_tlb/bundle/__init__.py b/ut_mem_block/dtlb/pf_tlb/bundle/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/pf_tlb/env/__init__.py b/ut_mem_block/dtlb/pf_tlb/env/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/pf_tlb/scripts/__init__.py b/ut_mem_block/dtlb/pf_tlb/scripts/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/pf_tlb/scripts/bundle_code_gen.py b/ut_mem_block/dtlb/pf_tlb/scripts/bundle_code_gen.py deleted file mode 100644 index 66b24fe4..00000000 --- a/ut_mem_block/dtlb/pf_tlb/scripts/bundle_code_gen.py +++ /dev/null @@ -1,160 +0,0 @@ -import re - -from toffee import Bundle - - -def __gen_bundle_code(bundle_name: str, signals, max_width: int): - """ - Generates bundle code using a list of signals. - - Args: - bundle_name: The name of the bundle. - signals: The list of signals. - max_width: The maximum width of the line. - - Returns: - The bundle code. - """ - - signals_num = len(signals) - - if signals_num == 0: - end_code = f" = Signal()" - else: - end_code = f" = Signals({signals_num})" - - code = f"from toffee import Bundle\nfrom toffee import Signals, Signal\n\nclass {bundle_name}(Bundle):\n" - code_line = "\t" - index = 0 - - TAB_SIZE = 4 - while index < signals_num: - code_line = "\t" - - code_line += f"{signals[index]}, " - current_width = TAB_SIZE + len(signals[index]) + 2 + 1 - index += 1 - - while ( - index < signals_num and current_width + len(signals[index]) + 2 <= max_width - ): - code_line += f"{signals[index]}, " - current_width += len(signals[index]) + 2 - index += 1 - - if index == signals_num: - code_line = code_line[:-2] - current_width -= 3 - - if current_width + len(end_code) <= max_width: - code_line += end_code + "\n" - else: - code_line += " \\\n\t" + end_code[1:] + "\n" - else: - code_line += "\\\n" - - code += code_line - - return code - - -def gen_bundle_code_from_prefix( - bundle_name: str, dut, prefix: str = "", max_width: int = 120 -): - """ - Generates a bundle using all the signals with the specified prefix. - - Args: - bundle_name: The name of the bundle. - dut: The DUT. - prefix: The prefix of the signals. - max_width: The maximum width of the line. - - Returns: - The bundle code. - """ - - signals = [] - - for dict in Bundle.dut_all_signals(dut): - name = dict["name"] - if name.startswith(prefix): - signals.append(name[len(prefix) :]) - - signals.sort() - signals_num = len(signals) - assert signals_num > 0, f"No signals found with prefix {prefix}" - - code = __gen_bundle_code(bundle_name, signals, max_width) - code += "\n" - code += f'bundle = {bundle_name}.from_prefix("{prefix}")\n' - - return code - - -def gen_bundle_code_from_regex(bundle_name: str, dut, regex: str, max_width: int = 120): - """ - Generates a bundle using all the signals that match the specified regex. - - Args: - bundle_name: The name of the bundle. - dut: The DUT. - regex: The regex of the signals. - max_width: The maximum width of the line. - - Returns: - The bundle code. - """ - - signals = [] - - for dict in Bundle.dut_all_signals(dut): - name = dict["name"] - match = re.search(regex, name) - - if match is not None: - groups = ["" if x is None else x for x in match.groups()] - name = "".join(groups) - signals.append(name) - - signals.sort() - signals_num = len(signals) - assert signals_num > 0, f"No signals found with regex {regex}" - - code = __gen_bundle_code(bundle_name, signals, max_width) - code += "\n" - code += f'bundle = {bundle_name}.from_regex(r"{regex}")\n' - - return code - - -def gen_bundle_code_from_dict(bundle_name: str, dut, dict: dict, max_width: int = 120): - """ - Generates a bundle using a dictionary. - - Args: - bundle_name: The name of the bundle. - dut: The DUT. - dict: The dictionary of signals. - max_width: The maximum width of the line. - - Returns: - The bundle code. - """ - - signals = [] - - dut_signals = [signal["name"] for signal in Bundle.dut_all_signals(dut)] - for key, value in dict.items(): - if value in dut_signals: - signals.append(key) - - signals.sort() - signals_num = len(signals) - assert signals_num > 0, f"No signals found in the dictionary" - - code = __gen_bundle_code(bundle_name, signals, max_width) - code += "\n" - code += f"bundle = {bundle_name}.from_dict({dict})\n" - - return code diff --git a/ut_mem_block/dtlb/pf_tlb/test/__init__.py b/ut_mem_block/dtlb/pf_tlb/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/ld_tlb/env/__init__.py b/ut_mem_block/dtlb/scripts/__init__.py similarity index 100% rename from ut_mem_block/dtlb/ld_tlb/env/__init__.py rename to ut_mem_block/dtlb/scripts/__init__.py diff --git a/ut_mem_block/dtlb/ld_tlb/scripts/bundle_code_gen.py b/ut_mem_block/dtlb/scripts/bundle_code_gen.py similarity index 100% rename from ut_mem_block/dtlb/ld_tlb/scripts/bundle_code_gen.py rename to ut_mem_block/dtlb/scripts/bundle_code_gen.py diff --git a/ut_mem_block/dtlb/st_tlb/__init__.py b/ut_mem_block/dtlb/st_tlb/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/st_tlb/agent/__init__.py b/ut_mem_block/dtlb/st_tlb/agent/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/st_tlb/bundle/__init__.py b/ut_mem_block/dtlb/st_tlb/bundle/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/st_tlb/env/__init__.py b/ut_mem_block/dtlb/st_tlb/env/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/st_tlb/scripts/__init__.py b/ut_mem_block/dtlb/st_tlb/scripts/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/st_tlb/scripts/bundle_code_gen.py b/ut_mem_block/dtlb/st_tlb/scripts/bundle_code_gen.py deleted file mode 100644 index 66b24fe4..00000000 --- a/ut_mem_block/dtlb/st_tlb/scripts/bundle_code_gen.py +++ /dev/null @@ -1,160 +0,0 @@ -import re - -from toffee import Bundle - - -def __gen_bundle_code(bundle_name: str, signals, max_width: int): - """ - Generates bundle code using a list of signals. - - Args: - bundle_name: The name of the bundle. - signals: The list of signals. - max_width: The maximum width of the line. - - Returns: - The bundle code. - """ - - signals_num = len(signals) - - if signals_num == 0: - end_code = f" = Signal()" - else: - end_code = f" = Signals({signals_num})" - - code = f"from toffee import Bundle\nfrom toffee import Signals, Signal\n\nclass {bundle_name}(Bundle):\n" - code_line = "\t" - index = 0 - - TAB_SIZE = 4 - while index < signals_num: - code_line = "\t" - - code_line += f"{signals[index]}, " - current_width = TAB_SIZE + len(signals[index]) + 2 + 1 - index += 1 - - while ( - index < signals_num and current_width + len(signals[index]) + 2 <= max_width - ): - code_line += f"{signals[index]}, " - current_width += len(signals[index]) + 2 - index += 1 - - if index == signals_num: - code_line = code_line[:-2] - current_width -= 3 - - if current_width + len(end_code) <= max_width: - code_line += end_code + "\n" - else: - code_line += " \\\n\t" + end_code[1:] + "\n" - else: - code_line += "\\\n" - - code += code_line - - return code - - -def gen_bundle_code_from_prefix( - bundle_name: str, dut, prefix: str = "", max_width: int = 120 -): - """ - Generates a bundle using all the signals with the specified prefix. - - Args: - bundle_name: The name of the bundle. - dut: The DUT. - prefix: The prefix of the signals. - max_width: The maximum width of the line. - - Returns: - The bundle code. - """ - - signals = [] - - for dict in Bundle.dut_all_signals(dut): - name = dict["name"] - if name.startswith(prefix): - signals.append(name[len(prefix) :]) - - signals.sort() - signals_num = len(signals) - assert signals_num > 0, f"No signals found with prefix {prefix}" - - code = __gen_bundle_code(bundle_name, signals, max_width) - code += "\n" - code += f'bundle = {bundle_name}.from_prefix("{prefix}")\n' - - return code - - -def gen_bundle_code_from_regex(bundle_name: str, dut, regex: str, max_width: int = 120): - """ - Generates a bundle using all the signals that match the specified regex. - - Args: - bundle_name: The name of the bundle. - dut: The DUT. - regex: The regex of the signals. - max_width: The maximum width of the line. - - Returns: - The bundle code. - """ - - signals = [] - - for dict in Bundle.dut_all_signals(dut): - name = dict["name"] - match = re.search(regex, name) - - if match is not None: - groups = ["" if x is None else x for x in match.groups()] - name = "".join(groups) - signals.append(name) - - signals.sort() - signals_num = len(signals) - assert signals_num > 0, f"No signals found with regex {regex}" - - code = __gen_bundle_code(bundle_name, signals, max_width) - code += "\n" - code += f'bundle = {bundle_name}.from_regex(r"{regex}")\n' - - return code - - -def gen_bundle_code_from_dict(bundle_name: str, dut, dict: dict, max_width: int = 120): - """ - Generates a bundle using a dictionary. - - Args: - bundle_name: The name of the bundle. - dut: The DUT. - dict: The dictionary of signals. - max_width: The maximum width of the line. - - Returns: - The bundle code. - """ - - signals = [] - - dut_signals = [signal["name"] for signal in Bundle.dut_all_signals(dut)] - for key, value in dict.items(): - if value in dut_signals: - signals.append(key) - - signals.sort() - signals_num = len(signals) - assert signals_num > 0, f"No signals found in the dictionary" - - code = __gen_bundle_code(bundle_name, signals, max_width) - code += "\n" - code += f"bundle = {bundle_name}.from_dict({dict})\n" - - return code diff --git a/ut_mem_block/dtlb/st_tlb/test/__init__.py b/ut_mem_block/dtlb/st_tlb/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/ut_mem_block/dtlb/ld_tlb/scripts/__init__.py b/ut_mem_block/dtlb/test/__init__.py similarity index 100% rename from ut_mem_block/dtlb/ld_tlb/scripts/__init__.py rename to ut_mem_block/dtlb/test/__init__.py diff --git a/ut_mem_block/dtlb/test/funcov_dtlb.py b/ut_mem_block/dtlb/test/funcov_dtlb.py new file mode 100644 index 00000000..8f54df73 --- /dev/null +++ b/ut_mem_block/dtlb/test/funcov_dtlb.py @@ -0,0 +1,183 @@ +from dut.TLBNonBlock import DUTTLBNonBlock +import toffee.funcov as fc +from toffee.funcov import CovGroup + +def init_dtlb_funcov(dut, g: fc.CovGroup): + # ---------------- 功能点1:接收请求(valid/resp/miss-hit) ---------------- + for i in range(4): + g.add_watch_point(dut, { + f"F1.{i}.req_valid": lambda d, i=i: getattr(d, f"io_requestor_{i}_req_valid").value == 1, + f"F1.{i}.resp_valid": lambda d, i=i: getattr(d, f"io_requestor_{i}_resp_valid").value == 1, + f"F1.{i}.hit": lambda d, i=i: getattr(d, f"io_requestor_{i}_resp_valid").value == 1 + and getattr(d, f"io_requestor_{i}_resp_bits_miss").value == 0, + f"F1.{i}.miss": lambda d, i=i: getattr(d, f"io_requestor_{i}_resp_valid").value == 1 + and getattr(d, f"io_requestor_{i}_resp_bits_miss").value == 1, + }, name=f"F1_REQ_RESP_P{i}") + + # ---------------- 功能点2:miss 处理 & PTW 交互 ------------------------- + # 各端口 PTW 请求 valid(valid-ready 这层多数后端抽象不到,这里只看 valid) + for i in range(4): + g.add_watch_point(dut, { + f"F2.{i}.ptw_req_valid": lambda d, i=i: getattr(d, f"io_ptw_req_{i}_valid").value == 1, + }, name=f"F2_PTW_REQ_P{i}") + + # s2xlate 模式覆盖:0(bare?) / 1(onlyS1) / 2(onlyS2)(以你的 RTL 约定为准) + g.add_watch_point(dut, { + "F2.s2xlate==0": lambda d: any(getattr(d, f"io_ptw_req_{i}_valid").value == 1 + and getattr(d, f"io_ptw_req_{i}_bits_s2xlate").value == 0 for i in range(4)), + "F2.s2xlate==1": lambda d: any(getattr(d, f"io_ptw_req_{i}_valid").value == 1 + and getattr(d, f"io_ptw_req_{i}_bits_s2xlate").value == 1 for i in range(4)), + "F2.s2xlate==2": lambda d: any(getattr(d, f"io_ptw_req_{i}_valid").value == 1 + and getattr(d, f"io_ptw_req_{i}_bits_s2xlate").value == 2 for i in range(4)), + "F2.s2xlate==3": lambda d: any(getattr(d, f"io_ptw_req_{i}_valid").value == 1 + and getattr(d, f"io_ptw_req_{i}_bits_s2xlate").value == 3 for i in range(4)), + }, name="F2_S2XLATE_MODES") + + + + # ---------------- 功能点3:hit 返回(paddr/gpaddr/页内偏移一致) ---------- + for i in range(4): + g.add_watch_point(dut, { + f"F3.{i}.paddr_seen": lambda d, i=i: getattr(d, f"io_requestor_{i}_resp_valid").value == 1 \ + and getattr(d, f"io_requestor_{i}_resp_bits_miss").value == 0 \ + and getattr(d, f"io_requestor_{i}_resp_bits_paddr_0").value != 0, + }, name=f"F3_RET_P{i}") + for i in range(3): + g.add_watch_point(dut, { + f"F3.{i}.gpaddr_seen": lambda d, i=i: getattr(d, f"io_requestor_{i}_resp_valid").value == 1 \ + and getattr(d, f"io_requestor_{i}_resp_bits_gpaddr_0").value != 0, + }, name=f"F3_RET_gpaddr_P{i}") + + # ---------------- 功能点4:getGpa 功能测试 ---------- + g.add_watch_point(dut, { + "F4.getGpa==1_seen": lambda d: any(getattr(d, f"io_ptw_req_{i}_bits_getGpa").value == 1 for i in range(4)), + }, name="F4_GETGPA") + + # ---------------- 功能点5:H扩展功能测试 ---------- + g.add_watch_point(dut, { + "F5.Bare": lambda d: any(getattr(d, f"io_ptw_req_{i}_bits_s2xlate").value == 0 for i in range(4)), + "F5.onlyStage1": lambda d: any(getattr(d, f"io_ptw_req_{i}_bits_s2xlate").value == 1 for i in range(4)), + "F5.onlyStage2": lambda d: any(getattr(d, f"io_ptw_req_{i}_bits_s2xlate").value == 2 for i in range(4)), + "F5.allStage": lambda d: any(getattr(d, f"io_ptw_req_{i}_bits_s2xlate").value == 3 for i in range(4)), + }, name="F5_S2XLATE_MODES") + + # ---------------- 功能点6:TLB 压缩(valididx 统计) -------------------- + # io_ptw_resp_bits_s1_valididx_0..7 为 8 位 one-hot/稀疏集合 + def _sum_valididx(d): + return sum(getattr(d, f"io_ptw_resp_bits_s1_valididx_{k}").value for k in range(8)) + g.add_watch_point(dut, { + "F6.valididx_onehot": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 and _sum_valididx(d) == 1, + "F6.valididx_full8": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 and _sum_valididx(d) == 8, + "F6.valididx_mid": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 and 1 < _sum_valididx(d) < 8, + }, name="F6_COMPRESSION_IDX") + + # ---------------- 功能点7:刷新(sfence & 管道) ----------------------- + g.add_watch_point(dut, { + "F7.sfence_all(0,0)": lambda d: getattr(d, "io_sfence_valid").value == 1 + and getattr(d, "io_sfence_bits_rs1").value == 0 + and getattr(d, "io_sfence_bits_rs2").value == 0, + "F7.sfence_by_va(1,0)": lambda d: getattr(d, "io_sfence_valid").value == 1 + and getattr(d, "io_sfence_bits_rs1").value == 1 + and getattr(d, "io_sfence_bits_rs2").value == 0, + "F7.sfence_by_id(0,1)": lambda d: getattr(d, "io_sfence_valid").value == 1 + and getattr(d, "io_sfence_bits_rs1").value == 0 + and getattr(d, "io_sfence_bits_rs2").value == 1, + "F7.sfence_va_id(1,1)": lambda d: getattr(d, "io_sfence_valid").value == 1 + and getattr(d, "io_sfence_bits_rs1").value == 1 + and getattr(d, "io_sfence_bits_rs2").value == 1, + "F7.hv==1": lambda d: getattr(d, "io_sfence_valid").value == 1 + and getattr(d, "io_sfence_bits_hv").value == 1, + "F7.hg==1": lambda d: getattr(d, "io_sfence_valid").value == 1 + and getattr(d, "io_sfence_bits_hg").value == 1, + }, name="F7_SFENCE") + + # ---------------- 功能点8:Reset -------------------------------------- + g.add_watch_point(dut, { + "F8.reset_high": lambda d: getattr(d, "reset").value == 1, + "F8.reset_low": lambda d: getattr(d, "reset").value == 0, + }, name="F8_RESET") + + # ---------------- 功能点9:权限检查(CSR + resp 异常位) ---------------- + g.add_watch_point(dut, { + "F9.mxr==1": lambda d: getattr(d, "io_csr_priv_mxr").value == 1, + "F9.sum==1": lambda d: getattr(d, "io_csr_priv_sum").value == 1, + "F9.vmxr==1": lambda d: getattr(d, "io_csr_priv_vmxr").value == 1, + "F9.vsum==1": lambda d: getattr(d, "io_csr_priv_vsum").value == 1, + }, name="F9_PRIV_SWITCHES") + for i in range(4): + g.add_watch_point(dut, { + f"F9.{i}.pf.ld": lambda d, i=i: getattr(d, f"io_requestor_{i}_resp_bits_excp_0_pf_ld").value == 1, + f"F9.{i}.af.ld": lambda d, i=i: getattr(d, f"io_requestor_{i}_resp_bits_excp_0_af_ld").value == 1, + }, name=f"F9_EXCP_ld_P{i}") + g.add_watch_point(dut, { + f"F9.0.pf.st": lambda d: getattr(d, f"io_requestor_0_resp_bits_excp_0_pf_st").value == 1, + f"F9.0.af.st": lambda d: getattr(d, f"io_requestor_0_resp_bits_excp_0_af_st").value == 1, + }, name=f"F9_EXCP_st_P0") + # ---------------- 功能点10:异常(GPF/GAF) ---------------------------- + g.add_watch_point(dut, { + "F10.s1_pf": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 + and getattr(d, "io_ptw_resp_bits_s1_pf").value == 1, + "F10.s1_af": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 + and getattr(d, "io_ptw_resp_bits_s1_af").value == 1, + "F10.s2_gpf": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 + and getattr(d, "io_ptw_resp_bits_s2_gpf").value == 1, + }, name="F10_PTW_RESP_FAULTS") + for i in range(4): + g.add_watch_point(dut, { + f"F10.{i}.gpf.ld": lambda d, i=i: getattr(d, f"io_requestor_{i}_resp_bits_excp_0_gpf_ld").value == 1, + }, name=f"F10_GPF_ld_P{i}") + g.add_watch_point(dut, { + f"F10.0.gpf.st": lambda d: getattr(d, f"io_requestor_0_resp_bits_excp_0_gpf_st").value == 1, + }, name=f"F10_GPF_st_P0") + + # ---------------- 功能点11:隔离(ASID/VMID/changed) ------------------- + g.add_watch_point(dut, { + "F11.asid!=0": lambda d: getattr(d, "io_csr_satp_asid").value != 0, + "F11.vmid!=0": lambda d: getattr(d, "io_csr_hgatp_vmid").value != 0, + "F11.satp.changed": lambda d: getattr(d, "io_csr_satp_changed").value == 1, + "F11.vsatp.changed": lambda d: getattr(d, "io_csr_vsatp_changed").value == 1, + "F11.hgatp.changed": lambda d: getattr(d, "io_csr_hgatp_changed").value == 1, + }, name="F11_ISOLATION") + + # ---------------- 功能点12:并行访问(同拍多端口) --------------------- + g.add_watch_point(dut, { + "F12.simul_req>=2": lambda d: sum(getattr(d, f"io_requestor_{i}_req_valid").value for i in range(4)) >= 2, + "F12.simul_miss>=2": lambda d: sum( + getattr(d, f"io_requestor_{i}_resp_valid").value == 1 + and getattr(d, f"io_requestor_{i}_resp_bits_miss").value == 1 + for i in range(4) + ) >= 2, + "F12.simul_hit>=2": lambda d: sum( + getattr(d, f"io_requestor_{i}_resp_valid").value == 1 + and getattr(d, f"io_requestor_{i}_resp_bits_miss").value == 0 + for i in range(4) + ) >= 2, + }, name="F12_PARALLEL") + + # ---------------- 功能点13:大小页(level 分布) ------------------------ + g.add_watch_point(dut, { + "F13.s1_level==0": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 + and getattr(d, "io_ptw_resp_bits_s1_entry_level").value == 0, + "F13.s1_level==1": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 + and getattr(d, "io_ptw_resp_bits_s1_entry_level").value == 1, + "F13.s1_level==2": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 + and getattr(d, "io_ptw_resp_bits_s1_entry_level").value == 2, + "F13.s2_level==0": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 + and getattr(d, "io_ptw_resp_bits_s2_entry_level").value == 0, + "F13.s2_level==1": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 + and getattr(d, "io_ptw_resp_bits_s2_entry_level").value == 1, + "F13.s2_level==2": lambda d: getattr(d, "io_ptw_resp_valid").value == 1 + and getattr(d, "io_ptw_resp_bits_s2_entry_level").value == 2, + }, name="F13_LEVELS") + + # ---------------- 功能点14: redirect ------------------------ + g.add_watch_point(dut, { + "F14.redirect_seen": lambda d: getattr(d, "io_redirect_valid").value == 1, + "F14.redirect_bits_level==0": lambda d: getattr(d, "io_redirect_valid").value == 1 + and getattr(d, "io_redirect_bits_level").value == 0, + "F14.redirect_bits_level==1": lambda d: getattr(d, "io_redirect_valid").value == 1 + and getattr(d, "io_redirect_bits_level").value == 1, + + }, name="F14_REDIRECT") + + return g \ No newline at end of file diff --git a/ut_mem_block/dtlb/test/ld_tlb_fixture.py b/ut_mem_block/dtlb/test/ld_tlb_fixture.py new file mode 100644 index 00000000..77965c66 --- /dev/null +++ b/ut_mem_block/dtlb/test/ld_tlb_fixture.py @@ -0,0 +1,60 @@ +import toffee +import toffee_test +import toffee.funcov as fc +from ..env.dtlb_env import DTLBEnv, DTLBEnv_PLRU +from dut.TLBNonBlock import DUTTLBNonBlock +from .funcov_dtlb import init_dtlb_funcov + +@toffee_test.fixture +async def dtlb_env_plru(toffee_request: toffee_test.ToffeeRequest): + + toffee.setup_logging(toffee.ERROR) + dut = toffee_request.create_dut(DUTTLBNonBlock) + + dut.InitClock("clock") + toffee.start_clock(dut) + env = DTLBEnv_PLRU(dut) + # env.req.start_monitor("monitor_req", 50) + # env.req.start_monitor("monitor_resp", 50) + # env.req.start_monitor("monitor_ptw_resp", 50) + # env.req.start_monitor("monitor_ptw_req", 50) + + yield env + + import asyncio + loop = asyncio.get_event_loop() + for task in asyncio.all_tasks(loop): + if task.get_name() == "__clock_loop": + task.cancel() + try: + await task + except asyncio.CancelledError: + pass + +g = fc.CovGroup("DTLB_FCOV_CASE") +@toffee_test.fixture +async def dtlb_env(toffee_request: toffee_test.ToffeeRequest): + + toffee.setup_logging(toffee.ERROR) + dut = toffee_request.create_dut(DUTTLBNonBlock) + + dut.InitClock("clock") + toffee.start_clock(dut) + env = DTLBEnv(dut) + toffee_request.add_cov_groups(init_dtlb_funcov(dut, g)) + # env.req.start_monitor("monitor_req", 50) + # env.req.start_monitor("monitor_resp", 50) + # env.req.start_monitor("monitor_ptw_resp", 50) + # env.req.start_monitor("monitor_ptw_req", 50) + yield env + + + import asyncio + loop = asyncio.get_event_loop() + for task in asyncio.all_tasks(loop): + if task.get_name() == "__clock_loop": + task.cancel() + try: + await task + except asyncio.CancelledError: + pass diff --git a/ut_mem_block/dtlb/test/test_ld_dtlb.py b/ut_mem_block/dtlb/test/test_ld_dtlb.py new file mode 100644 index 00000000..91ee0677 --- /dev/null +++ b/ut_mem_block/dtlb/test/test_ld_dtlb.py @@ -0,0 +1,896 @@ +import toffee +import toffee_test +import random +from .ld_tlb_fixture import dtlb_env + +LOAD = 0 + +async def _wait_ptw_req_and_capture(dtlb_env, vaddr, max_cycles=32): + vpn_expect = (int(vaddr) >> 12) & ((1 << 27) - 1) + for _ in range(max_cycles): + for i in range(4): + req = dtlb_env.bundle.ptw.req[i] + if int(req.valid.value) == 1 and int(req.bits_vpn.value) == vpn_expect: + return i, int(req.bits_s2xlate.value), int(req.bits_getGpa.value) + await dtlb_env.bundle.step() + raise AssertionError("Timed out waiting for ptw.req on vpn=0x%x" % vpn_expect) + +def _canon_sv39_vaddr(va: int) -> int: + """把 64 位 VA 规范化为 Sv39 canonical 形式(按 bit[38] 符号扩展)""" + va = int(va) & ((1 << 64) - 1) + low39 = va & ((1 << 39) - 1) + sign = (low39 >> 38) & 1 + upper = ((-sign) & ((1 << (64 - 39)) - 1)) << 39 + return (upper | low39) & ((1 << 64) - 1) +@toffee_test.testcase +async def test_ptwresp_s1_pf_propagates_to_requestor(dtlb_env): + for i in range(4): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + await dtlb_env.bundle.step() + + port =i + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r0 is None + + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp(vaddr=va, paddr=pa, level=0, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s1_pf=True, + s2xlate=s2x) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r1 == -1 + + resp = dtlb_env.bundle.requestor[port].resp + assert int(resp.bits_excp_0_pf_ld.value) == 1 + +@toffee_test.testcase +async def test_ptwresp_s1_af_propagates_to_requestor(dtlb_env): + for i in range(4): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + await dtlb_env.bundle.step() + + port = i + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r0 is None + + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp(vaddr=va, paddr=pa, level=0, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s1_af=True, + s2xlate=s2x) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r1 == -1 + + resp = dtlb_env.bundle.requestor[port].resp + assert int(resp.bits_excp_0_af_ld.value) == 1 + +@toffee_test.testcase +async def test_ptwresp_s2_gpf_propagates_to_requestor(dtlb_env): + for i in range(4): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + gpa = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=LOAD) + assert r0 is None + + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, gpa) + await dtlb_env.agent.set_ptw_resp(vaddr=gpa, paddr=0, level=0, + s1_v=False, + s2_tag=(gpa >> 12) & ((1<<27)-1), + s2_gpf=True, + s2xlate=s2x) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=LOAD) + assert r1 == -1 + + resp = dtlb_env.bundle.requestor[port].resp + assert int(resp.bits_excp_0_gpf_ld.value) == 1 + +@toffee_test.testcase +async def test_priv_mxr_allows_read_exec_only(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + csr.Satp.asid.value = 0x1 + csr.priv_mxr.value = 0 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=pa, level=0, + s1_asid=0x1, + s1_perm_r=False, s1_perm_x=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r1 == -1 + + csr.priv_mxr.value = 1 + await dtlb_env.bundle.step() + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert r2 == expect + +@toffee_test.testcase +async def test_priv_sum_supervisor_can_read_user_page(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + csr.Satp.asid.value = 0x2 + csr.priv_dmode.value = 1 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=pa, level=0, + s1_asid=0x2, + s1_perm_u=True, s1_perm_r=True, s1_perm_a=True, + s2xlate=s2x + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r1 == -1 + + csr.priv_sum.value = 1 + await dtlb_env.bundle.step() + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert r2 == expect + +@toffee_test.testcase +async def test_priv_vmxr_vs_allows_read_exec_only(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 0 + csr.Vsatp.asid.value = 0x12 + csr.HGatp.vmid.value = 0xA + await dtlb_env.bundle.step() + + port = 0 + va = 0x0000_0000_8020_3000 + gpa = 0x0000_0002_2222_3000 + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=gpa, level=0, + s1_asid=0x12, s1_vmid=0xA, + s1_perm_r=False, s1_perm_x=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r1 == -1 + + csr.priv_vmxr.value = 1 + await dtlb_env.bundle.step() + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + expect = (gpa & ~0xFFF) | (va & 0xFFF) + assert r2 == expect + +@toffee_test.testcase +async def test_priv_vsum_vs_supervisor_can_read_user_page(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 0 + csr.Vsatp.asid.value = 0x34 + csr.HGatp.vmid.value = 0xB + csr.priv_dmode.value = 1 + await dtlb_env.bundle.step() + + port = 1 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=gpa, level=0, + s1_asid=0x34, s1_vmid=0xB, + s1_perm_u=True, s1_perm_r=True, s1_perm_a=True, + s2xlate=s2x + ) + + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r1 == -1 + + csr.priv_vsum.value = 1 + await dtlb_env.bundle.step() + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + expect = (gpa & ~0xFFF) | (va & 0xFFF) + assert r2 == expect + +@toffee_test.testcase +async def test_asid_isolation_bare(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + csr.Satp.asid.value = 0x10 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=pa, level=0, + s1_asid=0x10, s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x + ) + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert r1 == expect + csr.Satp.asid.value = 0x21 + csr.Satp.changed.value = True + await dtlb_env.bundle.step() + csr.Satp.changed.value = False + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r2 is None + +@toffee_test.testcase +async def test_asid_isolation_onlyStage1(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 0 + csr.Vsatp.asid.value = 0x33 + csr.HGatp.vmid.value = 0xA + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 - 1) + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=gpa, level=0, + s1_asid=0x33, s1_vmid=0xA, s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x + ) + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + expect = (gpa & ~0xFFF) | (va & 0xFFF) + assert r1 == expect + + csr.Vsatp.asid.value = 0x44 + csr.Vsatp.changed.value = True + await dtlb_env.bundle.step() + csr.Vsatp.changed.value = False + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r2 is None + + csr.Vsatp.asid.value = 0x33 + csr.HGatp.vmid.value = 0xB + csr.Vsatp.changed.value = True + csr.HGatp.changed.value = True + await dtlb_env.bundle.step() + csr.Vsatp.changed.value = False + csr.HGatp.changed.value = False + + r3 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r3 is None + +@toffee_test.testcase +async def test_vmid_isolation_onlyStage2(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + csr.HGatp.vmid.value = 0x55 + await dtlb_env.bundle.step() + + port = 0 + gpa_in = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=LOAD) + assert r0 is None + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa_in) + await dtlb_env.agent.set_ptw_resp( + vaddr=gpa_in, paddr=0, level=0, + s1_v=False, + s2xlate=s2x, getGpa=getGpa_req, + s2_tag=(gpa_in >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=0, + s1_vmid=0x55, s2_perm_r=True, s2_perm_a=True + ) + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=LOAD) + expect = (hpa & ~0xFFF) | (gpa_in & 0xFFF) + assert r1 == expect + + csr.HGatp.vmid.value = 0x66 + csr.HGatp.changed.value = True + await dtlb_env.bundle.step() + csr.HGatp.changed.value = False + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=LOAD) + assert r2 is None + +@toffee_test.testcase +async def test_hit_onlyStage1(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 0 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + assert r0 is None + + ptw_port, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=gpa, level=0, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x, getGpa=getGpa_req + ) + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) + expect = (gpa & ~0xFFF) | (va & 0xFFF) + assert r1 == expect + + +@toffee_test.testcase +async def test_hit_onlyStage2_4k(dtlb_env): + + + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 1 + gpa = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=LOAD) + assert r0 is None + + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa) + await dtlb_env.agent.set_ptw_resp( + vaddr=gpa, paddr=0, level=0, + s1_v=False, + s2xlate=s2x, getGpa=False, + s2_tag=(gpa >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=0, + s2_perm_r=True, s2_perm_a=True + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=LOAD) + expect = (hpa & ~0xFFF) | (gpa & 0xFFF) + assert r1 == expect + +@toffee_test.testcase +async def test_hit_onlyStage2_2m(dtlb_env): + + + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 1 + gpa = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) & ~((1<<21)-1) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) & ~((1<<21)-1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=LOAD) + assert r0 is None + + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa) + await dtlb_env.agent.set_ptw_resp( + vaddr=gpa, paddr=0, level=0, + s1_v=False, + s2xlate=s2x, getGpa=False, + s2_tag=(gpa >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=1, + s2_perm_r=True, s2_perm_a=True + ) + for off in (0x000, 0x1000, 0x1FF000): + va = gpa + off + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD) + mask = (1 << 21) - 1 + expect = (hpa & ~mask) | (va & mask) + assert r1 == expect + +@toffee_test.testcase +async def test_hit_onlyStage2_1g(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 1 + gpa = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) & ~((1<<30)-1) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) & ~((1<<30)-1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=LOAD) + assert r0 is None + + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa) + await dtlb_env.agent.set_ptw_resp( + vaddr=gpa, paddr=0, level=0, + s1_v=False, + s2xlate=s2x, getGpa=False, + s2_tag=(gpa >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=2, + s2_perm_r=True, s2_perm_a=True + ) + for off in (0x0, 0x21_000, 0x3FF_F000): + va = gpa + off + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD) + mask = (1 << 30) - 1 + expect = (hpa & ~mask) | (va & mask) + assert r1 == expect + +@toffee_test.testcase +async def test_hit_allStage(dtlb_env): + + + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 2 + gva = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 -1) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) + + + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) + assert r0 is None + + _, s2x, getGpa_ptw = await _wait_ptw_req_and_capture(dtlb_env, gva) + await dtlb_env.agent.set_ptw_resp( + vaddr=gva, paddr=gpa, level=0, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x, getGpa=getGpa_ptw, + s2_tag=(gpa>>12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=0, + s2_perm_r=True, s2_perm_a=True + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) + expect = (hpa & ~0xFFF) | (gva & 0xFFF) + assert r1 == expect + +@toffee_test.testcase +async def test_hit_allStage_with_getGpa(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + gva = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 -1) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) + + _ = await dtlb_env.agent.drive_request(port=0, vaddr=gva, cmd=LOAD) + _, s2x, getGpa_ptw = await _wait_ptw_req_and_capture(dtlb_env, gva) + + await dtlb_env.agent.set_ptw_resp( + vaddr=gva, paddr=gpa, level=0, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x, getGpa=getGpa_ptw, + s2_ppn=(hpa >> 12), s2_level=0, + s2_perm_r=False, s2_perm_a=True + ) + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=gva, cmd=LOAD) + + _, s2x_req, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gva) + assert getGpa_req == 1 + +@toffee_test.testcase +async def test_parallel_simul_req_miss(dtlb_env): + """ + 覆盖 F12.simul_req>=2:同一拍向多个端口同时打 req.valid=1 + """ + requestor = dtlb_env.bundle.requestor + + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + VA0 = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + VA1 = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + VA2 = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + + requestor[0].req.valid.value = 1 + requestor[0].req.bits_vaddr.value = VA0 + requestor[0].req.bits_fullva.value = VA0 + requestor[0].req.bits_cmd.value = LOAD + requestor[0].req.bits_no_translate.value = 0 + + requestor[1].req.valid.value = 1 + requestor[1].req.bits_vaddr.value = VA1 + requestor[1].req.bits_fullva.value = VA1 + requestor[1].req.bits_cmd.value = LOAD + requestor[1].req.bits_no_translate.value = 0 + + requestor[2].req.valid.value = 1 + requestor[2].req.bits_vaddr.value = VA2 + requestor[2].req.bits_fullva.value = VA2 + requestor[2].req.bits_cmd.value = LOAD + requestor[2].req.bits_no_translate.value = 0 + await dtlb_env.bundle.step() + requestor[0].req.valid.value = 0 + requestor[1].req.valid.value = 0 + requestor[2].req.valid.value = 0 + await dtlb_env.bundle.step() + +@toffee_test.testcase +async def test_parallel_simul_req_hit(dtlb_env): + requestor = dtlb_env.bundle.requestor + + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + VA0 = 0x0000_0000_8000_0000 + VA1 = 0x0000_0000_8000_1000 + VA2 = 0x0000_0000_8000_2000 + PA0 = 0x0000_0001_0000_0000 + PA1 = 0x0000_0001_0000_1000 + PA2 = 0x0000_0001_0000_2000 + + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA0, cmd=LOAD) == None + await dtlb_env.agent.set_ptw_resp(vaddr=VA0,paddr=PA0,level=0) + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA0, cmd=LOAD) == ((VA0 & (0xFFF)) | (PA0 & ~(0xFFF))) + + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA1, cmd=LOAD) == None + await dtlb_env.agent.set_ptw_resp(vaddr=VA1,paddr=PA1,level=0) + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA1, cmd=LOAD) == ((VA1 & (0xFFF)) | (PA1 & ~(0xFFF))) + + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA2, cmd=LOAD) == None + await dtlb_env.agent.set_ptw_resp(vaddr=VA2,paddr=PA2,level=0) + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA2, cmd=LOAD) == ((VA2 & (0xFFF)) | (PA2 & ~(0xFFF))) + await dtlb_env.bundle.step() + + requestor[0].req.valid.value = 1 + requestor[0].req.bits_vaddr.value = VA0 + requestor[0].req.bits_fullva.value = VA0 + requestor[0].req.bits_cmd.value = LOAD + requestor[0].req.bits_no_translate.value = 0 + + requestor[1].req.valid.value = 1 + requestor[1].req.bits_vaddr.value = VA1 + requestor[1].req.bits_fullva.value = VA1 + requestor[1].req.bits_cmd.value = LOAD + requestor[1].req.bits_no_translate.value = 0 + + requestor[2].req.valid.value = 1 + requestor[2].req.bits_vaddr.value = VA2 + requestor[2].req.bits_fullva.value = VA2 + requestor[2].req.bits_cmd.value = LOAD + requestor[2].req.bits_no_translate.value = 0 + + await dtlb_env.bundle.step() + + requestor[0].req.valid.value = 0 + requestor[1].req.valid.value = 0 + requestor[2].req.valid.value = 0 + await dtlb_env.bundle.step() + +@toffee_test.testcase +async def test_store_2mb_page(dtlb_env): + + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + va_base = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) & ~((1<<21)-1) + pa_base = random.randint(2 ** 12, 2 ** 36 - 1) & ~((1<<21)-1) + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va_base, cmd=LOAD) + assert r0 is None + + await dtlb_env.agent.set_ptw_resp( + vaddr=va_base, paddr=pa_base, level=1, s1_asid=0, + s1_ppn_low=[0]*8, s1_valididx=[1]*8, s1_pteidx=[0]*8 + ) + for off in (0x000, 0x1000, 0x1FF000): + va = va_base + off + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD) + mask = (1 << 21) - 1 + expect = (pa_base & ~mask) | (va & mask) + assert r1 == expect + +@toffee_test.testcase +async def test_store_1gb_page(dtlb_env): + + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + va_base = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) & ~((1<<30)-1) + pa_base = random.randint(2 ** 12, 2 ** 36 - 1) & ~((1<<30)-1) + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va_base, cmd=LOAD) + assert r0 is None + + await dtlb_env.agent.set_ptw_resp( + vaddr=va_base, paddr=pa_base, level=2, s1_asid=0, + s1_ppn_low=[0]*8, s1_valididx=[1]*8, s1_pteidx=[0]*8 + ) + + for off in (0x0, 0x21_000, 0x3FF_F000): + va = va_base + off + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD) + mask = (1 << 30) - 1 + expect = (pa_base & ~mask) | (va & mask) + assert r1 == expect + +@toffee_test.testcase +async def test_express_random(dtlb_env): + + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD) + assert r0 is None + vpn_low = (va >> 12) & 0x7 + rand_valididx = [0]*8 + rand_ppn_low = [0]*8 + pteidx = [0]*8 + for i in range(8): + rand_valididx[i] = random.randint(0,1) + rand_ppn_low[i] = i + rand_valididx[vpn_low] = 1 + pteidx[vpn_low] = 1 + await dtlb_env.agent.set_ptw_resp( + vaddr=va, + paddr=pa, + level=0, + s1_asid=0, + s1_ppn_low=rand_ppn_low, + s1_valididx=rand_valididx, + s1_pteidx=pteidx + ) + va_base = va & ~((1<<15)-1) + pa_base = pa & ~((1<<15)-1) + for i in range(8): + va = va_base | (i << 12) + pa = pa_base | (rand_ppn_low[i] << 12) + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD) + if rand_valididx[i] != 0: + expect = (pa & ~0xFFF) | (va & 0xFFF) + else: + expect = None + assert r1 == expect + +@toffee_test.testcase +async def test_express_full(dtlb_env): + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD) + assert r0 is None + vpn_low = (va >> 12) & 0x7 + pteidx = [0]*8 + pteidx[vpn_low] = 1 + await dtlb_env.agent.set_ptw_resp( + vaddr=va, + paddr=pa, + level=0, + s1_asid=0, + s1_ppn_low=[0,1,2,3,4,5,6,7], + s1_valididx=[1,1,1,1,1,1,1,1], + s1_pteidx=pteidx + ) + va_base = va & ~((1<<15)-1) + pa_base = pa & ~((1<<15)-1) + for i in range(8): + va = va_base | (i << 12) + pa = pa_base | (i << 12) + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert r1 == expect + +@toffee_test.testcase +async def test_req_kill(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 0 + va = 0x0000_0000_8020_9000 + + req = dtlb_env.bundle.requestor[port].req + req.bits_vaddr.value = va & ((1<<50)-1) + req.bits_fullva.value = va + req.bits_cmd.value = 0 + req.bits_kill.value = 1 + req.valid.value = 1 + + req.valid.value = 0 + + N = 32 + for _ in range(N): + assert dtlb_env.bundle.requestor[port].resp.valid.value == 0 + for i in range(4): + assert dtlb_env.bundle.ptw.req[i].valid.value == 0 + await dtlb_env.bundle.step() + +@toffee_test.testcase +async def test_req_notranslate(dtlb_env): + + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + + va = 0x0000_0000_8020_1000 + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD, no_translate = 1) + assert r0 == va + +@toffee_test.testcase +async def test_hit_smoke(dtlb_env): + + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=LOAD) + assert r0 is None + + await dtlb_env.agent.set_ptw_resp( + vaddr=va, + paddr=pa, + level=0, + s1_asid=0, + ) + for i in range(3): + r1 = await dtlb_env.agent.drive_request(port=i, vaddr=va, cmd=LOAD) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert r1 == expect + +@toffee_test.testcase +async def test_miss_smoke(dtlb_env): + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=0x0000_0000_8020_1000, cmd=LOAD, kill=False ,no_translate=False) + + assert r0 is None \ No newline at end of file diff --git a/ut_mem_block/dtlb/test/test_plru.py b/ut_mem_block/dtlb/test/test_plru.py new file mode 100644 index 00000000..6aa88380 --- /dev/null +++ b/ut_mem_block/dtlb/test/test_plru.py @@ -0,0 +1,98 @@ +import toffee +import toffee_test +import random +from .ld_tlb_fixture import dtlb_env_plru + +LOAD = 0 +STORE = 1 + +async def _wait_ptw_req_and_capture(dtlb_env_plru, vaddr, max_cycles=32): + vpn_expect = (int(vaddr) >> 12) & ((1 << 27) - 1) + for _ in range(max_cycles): + for i in range(4): + req = dtlb_env_plru.bundle.ptw.req[i] + if int(req.valid.value) == 1 and int(req.bits_vpn.value) == vpn_expect: + return i, int(req.bits_s2xlate.value), int(req.bits_getGpa.value) + await dtlb_env_plru.bundle.step() + raise AssertionError("Timed out waiting for ptw.req on vpn=0x%x" % vpn_expect) + +def _canon_sv39_vaddr(va: int) -> int: + """把 64 位 VA 规范化为 Sv39 形式""" + va = int(va) & ((1 << 64) - 1) + low39 = va & ((1 << 39) - 1) + sign = (low39 >> 38) & 1 + upper = ((-sign) & ((1 << (64 - 39)) - 1)) << 39 + return (upper | low39) & ((1 << 64) - 1) + +@toffee_test.testcase +async def test_plru_random(dtlb_env_plru): + dtlb_env_plru.dut.reset.value = 1; await dtlb_env_plru.bundle.step() + dtlb_env_plru.dut.reset.value = 0; await dtlb_env_plru.bundle.step() + await dtlb_env_plru.set_sv39_defaults(); await dtlb_env_plru.bundle.step() + va = [0]*100 + pa = [0]*100 + for i in range(100): + va[i] = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa[i] = random.randint(2 ** 12, 2 ** 36 - 1) + for i in range(200): + index = random.randint(0, 99) + vaddr = va[index] + paddr = pa[index] + + r0 = await dtlb_env_plru.agent.drive_request(port=0, vaddr=vaddr, cmd=LOAD) + + if r0 == None: + await dtlb_env_plru.agent.set_ptw_resp( + vaddr=vaddr, + paddr=paddr, + level=0 + ) + await dtlb_env_plru.agent.drive_request(port=0, vaddr=vaddr, cmd=LOAD) + else: + continue + +@toffee_test.testcase +async def test_plru_sweep(dtlb_env_plru): + dtlb_env_plru.dut.reset.value = 1; await dtlb_env_plru.bundle.step() + dtlb_env_plru.dut.reset.value = 0; await dtlb_env_plru.bundle.step() + await dtlb_env_plru.set_sv39_defaults(); await dtlb_env_plru.bundle.step() + + BASE_VA = 0x0000_0000_8020_0000 + BASE_PA = 0x0000_0001_2340_0000 + for i in range(48): + va = BASE_VA + i * 0x8000 + pa = BASE_PA + i * 0x1000 + + r0 = await dtlb_env_plru.agent.drive_request(port=1, vaddr=va, cmd=LOAD) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env_plru, va) + await dtlb_env_plru.agent.set_ptw_resp( + vaddr=va, paddr=pa, level=0, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x + ) + r2 = await dtlb_env_plru.agent.drive_request(port=1, vaddr=va, cmd=LOAD) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert r2 == expect + new_va = BASE_VA + 0x1000 * 50 + new_pa = BASE_PA + 0x1000 * 50 + r0 = await dtlb_env_plru.agent.drive_request(port=1, vaddr=new_va, cmd=LOAD) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env_plru, new_va) + await dtlb_env_plru.agent.set_ptw_resp( + vaddr=new_va, paddr=new_pa, level=0, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x + ) + r2 = await dtlb_env_plru.agent.drive_request(port=1, vaddr=new_va, cmd=LOAD) + expect = (new_pa & ~0xFFF) | (new_va & 0xFFF) + assert r2 == expect + miss_cnt = 0 + for i in range(48): + va = BASE_VA + i * 0x8000 + ret = await dtlb_env_plru.agent.drive_request(port=1, vaddr=va, cmd=LOAD) + if ret is None: + miss_cnt += 1 + + print(f"After inserting 49th entry, total miss when accessing original 48 entries: {miss_cnt}") + diff --git a/ut_mem_block/dtlb/test/test_redirect.py b/ut_mem_block/dtlb/test/test_redirect.py new file mode 100644 index 00000000..1acfdb1a --- /dev/null +++ b/ut_mem_block/dtlb/test/test_redirect.py @@ -0,0 +1,128 @@ +import toffee +import toffee_test +from .ld_tlb_fixture import dtlb_env + +LOAD = 0 +async def _wait_ptw_req_and_capture(dtlb_env, vaddr, max_cycles=32): + vpn_expect = (int(vaddr) >> 12) & ((1 << 27) - 1) + for _ in range(max_cycles): + for i in range(4): + req = dtlb_env.bundle.ptw.req[i] + if int(req.valid.value) == 1 and int(req.bits_vpn.value) == vpn_expect: + return i, int(req.bits_s2xlate.value), int(req.bits_getGpa.value) + await dtlb_env.bundle.step() + raise AssertionError("Timed out waiting for ptw.req on vpn=0x%x" % vpn_expect) + + +@toffee_test.testcase +async def test_redirect_masks_miss_via_lastCycleRedirect(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + rob = 0x5C + gva = 0x0000_0000_8020_5000 + gpa = 0x0000_0001_2345_0000 + hpa = 0x0000_0003_ABCD_0000 + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) + assert r0 is None + _, s2x, getGpa_flag = await _wait_ptw_req_and_capture(dtlb_env, gva) + + await dtlb_env.agent.set_ptw_resp( + vaddr=gva, paddr=gpa, level=0, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x, getGpa=getGpa_flag, + s2_ppn=(hpa >> 12), s2_level=0, + s2_perm_r=False, s2_perm_a=True + ) + + req = dtlb_env.bundle.requestor[port].req + req.bits_vaddr.value = gva & ((1<<50)-1) + req.bits_fullva.value = gva + req.bits_cmd.value = 0 # LOAD + req.bits_isPrefetch.value = 0 + req.bits_no_translate.value = 0 + req.bits_debug_robIdx_flag.value = 1 + req.bits_debug_robIdx_value.value = rob + req.valid.value = 1 + + redir = dtlb_env.bundle.redirect + redir.valid.value = 1 + redir.bits_robIdx_flag.value = 1 + redir.bits_robIdx_value.value = rob + redir.bits_level.value = 1 + + await dtlb_env.bundle.step() + + + req.valid.value = 0 + redir.valid.value = 0 + + for _ in range(32): + for i in range(4): + assert dtlb_env.bundle.ptw.req[i].valid.value == 0 + await dtlb_env.bundle.step() + + +@toffee_test.testcase +async def test_redirect_level0_kill_younger(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + rob = 0x50 + pivot= 0x4A + gva = 0x0000_0000_8020_5000 + gpa = 0x0000_0001_2345_0000 + hpa = 0x0000_0003_ABCD_0000 + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, gva) + await dtlb_env.agent.set_ptw_resp( + vaddr=gva, paddr=gpa, level=0, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, + s2xlate=s2x, getGpa=False, + s2_ppn=(hpa >> 12), s2_level=0, + s2_perm_r=False, s2_perm_a=True + ) + + req = dtlb_env.bundle.requestor[port].req + req.bits_vaddr.value = gva & ((1<<50)-1) + req.bits_fullva.value = gva + req.bits_cmd.value = LOAD + req.bits_isPrefetch.value = 0 + req.bits_no_translate.value = 0 + req.bits_debug_robIdx_flag.value = 1 + req.bits_debug_robIdx_value.value = rob + req.valid.value = 1 + + redir = dtlb_env.bundle.redirect + redir.valid.value = 1 + redir.bits_robIdx_flag.value = 1 + redir.bits_robIdx_value.value = pivot + redir.bits_level.value = 0 + + await dtlb_env.bundle.step() + + req.valid.value = 0 + redir.valid.value = 0 + + for _ in range(32): + for i in range(4): + assert dtlb_env.bundle.ptw.req[i].valid.value == 0 + await dtlb_env.bundle.step() \ No newline at end of file diff --git a/ut_mem_block/dtlb/test/test_sfence.py b/ut_mem_block/dtlb/test/test_sfence.py new file mode 100644 index 00000000..d2bfed1e --- /dev/null +++ b/ut_mem_block/dtlb/test/test_sfence.py @@ -0,0 +1,328 @@ +# -*- coding: utf-8 -*- +import toffee +import toffee_test +from .ld_tlb_fixture import dtlb_env + +LOAD = 0 +STORE = 1 + +async def _wait_ptw_req_and_capture(dtlb_env, vaddr, max_cycles=32): + vpn_expect = (int(vaddr) >> 12) & ((1 << 27) - 1) + for _ in range(max_cycles): + for i in range(4): + req = dtlb_env.bundle.ptw.req[i] + if int(req.valid.value) == 1 and int(req.bits_vpn.value) == vpn_expect: + return i, int(req.bits_s2xlate.value), int(req.bits_getGpa.value) + await dtlb_env.bundle.step() + raise AssertionError("Timed out waiting for ptw.req on vpn=0x%x" % vpn_expect) + +def _canon_sv39_to_50b(va: int, page_align: bool) -> int: + low39 = va & ((1 << 39) - 1) + sign = (low39 >> 38) & 1 + upper = ((-sign) & ((1 << (50 - 39)) - 1)) << 39 + va50 = (upper | low39) & ((1 << 50) - 1) + if page_align: + va50 &= ~0xFFF + return va50 + +async def do_sfence(dtlb_env, *, hv=False, hg=False, rs1=False, addr=0, rs2=False, id_=0, settle_cycles=10): + sf = dtlb_env.bundle.sfence + sf.bits_hv.value = 1 if hv else 0 + sf.bits_hg.value = 1 if hg else 0 + sf.bits_rs1.value = 1 if rs1 else 0 + sf.bits_rs2.value = 1 if rs2 else 0 + + sf.bits_addr.value = _canon_sv39_to_50b(addr, page_align=rs1) + sf.bits_id.value = int(id_) & ((1 << 16) - 1) + sf.bits_flushPipe.value = 0 + + sf.valid.value = 1 + await dtlb_env.bundle.step() + sf.valid.value = 0 + + for _ in range(settle_cycles): + await dtlb_env.bundle.step() + + +# ========================= +# 1) SFENCE.VMA 全量清(非 G) +# ========================= +@toffee_test.testcase +async def test_sfence_vma_global_noG(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 0 + va = 0x0000_0000_8020_1000 + pa = 0x0000_0001_0000_9000 + asid = 0x11 + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + csr.Satp.asid.value = asid + await dtlb_env.bundle.step() + + # miss → 回填(非 G)→ hit + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp(vaddr=va, paddr=pa, level=0, + s1_asid=asid, s1_perm_r=True, s1_perm_a=True, s1_perm_g=False, + s2xlate=s2x) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) == expect + + # 全量清(rs1=0, rs2=0) + await do_sfence(dtlb_env, hv=False, hg=False, rs1=False, rs2=False) + + # 应 miss + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) is None + + +# ========================= +# 2) SFENCE.VMA 按 ASID 清 +# ========================= +@toffee_test.testcase +async def test_sfence_vma_by_asid(dtlb_env): + dtlb_env = dtlb_env + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 0 + va = 0x0000_0000_8020_1000 + pa = 0x0000_0001_0000_9000 + asid = 0x22 + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + csr.Satp.asid.value = asid + await dtlb_env.bundle.step() + + + + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp(vaddr=va, paddr=pa, level=0, + s1_asid=asid, s1_perm_r=True, s1_perm_a=True, s1_perm_g=False, + s2xlate=s2x) + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) == ((pa & ~0xFFF) | (va & 0xFFF)) + + # 按当前 satp.asid 清(id 用 CSR.Satp.asid) + await do_sfence(dtlb_env, hv=False, hg=False, rs1=False, rs2=True, id_=asid) + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) is None + + +# ========================= +# 3) SFENCE.VMA 按 VA 清 +# ========================= +@toffee_test.testcase +async def test_sfence_vma_by_va(dtlb_env): + dtlb_env = dtlb_env + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 1 + va = 0x0000_0000_8020_1000 + pa = 0x0000_0001_0000_9000 + asid = 0x33 + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + csr.Satp.asid.value = asid + await dtlb_env.bundle.step() + + + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp(vaddr=va, paddr=pa, level=0, + s1_asid=asid, s1_perm_r=True, s1_perm_a=True, s1_perm_g=False, + s2xlate=s2x) + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) == ((pa & ~0xFFF) | (va & 0xFFF)) + + await do_sfence(dtlb_env, hv=False, hg=False, rs1=True, addr=va, rs2=False) + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) is None + + +# ========================= +# 4) SFENCE.VMA 按 (VA, ASID) 清 +# ========================= +@toffee_test.testcase +async def test_sfence_vma_by_va_asid(dtlb_env): + dtlb_env = dtlb_env + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 2 + va = 0x0000_0000_8020_1000 + pa = 0x0000_0001_0000_9000 + asid = 0x44 + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + csr.Satp.asid.value = asid + await dtlb_env.bundle.step() + + + + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp(vaddr=va, paddr=pa, level=0, + s1_asid=asid, s1_perm_r=True, s1_perm_a=True, s1_perm_g=False, + s2xlate=s2x) + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) == ((pa & ~0xFFF) | (va & 0xFFF)) + + await do_sfence(dtlb_env, hv=False, hg=False, rs1=True, addr=va, rs2=True, id_=asid) + assert await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=LOAD) is None + + +# ========================= +# 5) HFENCE.VVMA:按 VMID(全 GVA) +# ========================= +@toffee_test.testcase +async def test_hfence_vvma_by_vmid(dtlb_env): + dtlb_env = dtlb_env + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 0 + gva = 0x0000_0000_8020_1000 + gpa = 0x0000_0001_0000_9000 + asid = 0x12 + vmid = 0xAA + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 # VS-S1 开启 + csr.HGatp.mode.value = 0 # S2 关闭(only VS-S1) + csr.Vsatp.asid.value = asid + csr.HGatp.vmid.value = vmid # 关键:VVMA 用 CSR.HGATP.vmid 过滤 + await dtlb_env.bundle.step() + + + + assert await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, gva) + await dtlb_env.agent.set_ptw_resp(vaddr=gva, paddr=gpa, level=0, + s1_asid=asid, s1_vmid=vmid, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, s1_perm_g=False, + s2xlate=s2x) + assert await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) == ((gpa & ~0xFFF) | (gva & 0xFFF)) + + # VVMA:hv=1,通常按 VMID 过滤需 rs2=1;VMID 来源于 CSR.HGATP.vmid(id 忽略) + await do_sfence(dtlb_env, hv=True, hg=False, rs1=False, rs2=True, id_=vmid) + assert await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) is None + + +# ========================= +# 6) HFENCE.VVMA:按 (GVA, VMID) 清 +# ========================= +@toffee_test.testcase +async def test_hfence_vvma_by_gva_vmid(dtlb_env): + dtlb_env = dtlb_env + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 1 + gva = 0x0000_0000_8020_1000 + gpa = 0x0000_0001_0000_9000 + asid = 0x21 + vmid = 0xBB + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 0 + csr.Vsatp.asid.value = asid + csr.HGatp.vmid.value = vmid + await dtlb_env.bundle.step() + + assert await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, gva) + await dtlb_env.agent.set_ptw_resp(vaddr=gva, paddr=gpa, level=0, + s1_asid=asid, s1_vmid=vmid, + s1_perm_r=True, s1_perm_a=True, s1_perm_u=True, s1_perm_g=False, + s2xlate=s2x) + assert await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) == ((gpa & ~0xFFF) | (gva & 0xFFF)) + + await do_sfence(dtlb_env, hv=True, hg=False, rs1=True, addr=gva, rs2=True, id_=vmid) + assert await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=LOAD) is None + + +# ========================= +# 7) HFENCE.GVMA:按 VMID 清 S2(GPA 全范围) +# ========================= +@toffee_test.testcase +async def test_hfence_gvma_by_vmid(dtlb_env): + dtlb_env = dtlb_env + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 2 + gpa_in = 0x0000_0000_8020_1000 + hpa = 0x0000_0002_2000_0000 + vmid = 0x66 + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + csr.HGatp.vmid.value = vmid + await dtlb_env.bundle.step() + + assert await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=LOAD) is None + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa_in) + await dtlb_env.agent.set_ptw_resp(vaddr=gpa_in, paddr=0, level=0, s1_v=False, + s2xlate=s2x, getGpa=getGpa_req, + s2_tag=(gpa_in >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=0, + s1_vmid=vmid, s2_perm_r=True, s2_perm_a=True) + assert await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=LOAD) == ((hpa & ~0xFFF) | (gpa_in & 0xFFF)) + + # GVMA:hg=1,按 VMID(来自 bits_id)清 S2 + await do_sfence(dtlb_env, hv=False, hg=True, rs1=False, rs2=True, id_=vmid) + assert await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=LOAD) is None + + +# ========================= +# 8) HFENCE.GVMA:按 (GPA, VMID) 清 S2 +# ========================= +@toffee_test.testcase +async def test_hfence_gvma_by_gpa_vmid(dtlb_env): + dtlb_env = dtlb_env + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 3 + gpa_in = 0x0000_0000_8020_1000 + hpa = 0x0000_0002_2000_1000 + vmid = 0x77 + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + csr.HGatp.vmid.value = vmid + await dtlb_env.bundle.step() + + assert await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=LOAD) is None + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa_in) + await dtlb_env.agent.set_ptw_resp(vaddr=gpa_in, paddr=0, level=0, s1_v=False, + s2xlate=s2x, getGpa=getGpa_req, + s2_tag=(gpa_in >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=0, + s1_vmid=vmid, s2_perm_r=True, s2_perm_a=True) + assert await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=LOAD) == ((hpa & ~0xFFF) | (gpa_in & 0xFFF)) + + await do_sfence(dtlb_env, hv=False, hg=True, rs1=True, addr=gpa_in, rs2=True, id_=vmid) + assert await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=LOAD) is None diff --git a/ut_mem_block/dtlb/test/test_st_dtlb.py b/ut_mem_block/dtlb/test/test_st_dtlb.py new file mode 100644 index 00000000..37f278d9 --- /dev/null +++ b/ut_mem_block/dtlb/test/test_st_dtlb.py @@ -0,0 +1,833 @@ +import toffee +import toffee_test +import random +from .ld_tlb_fixture import dtlb_env + +LOAD = 0 +STORE = 1 + +async def _wait_ptw_req_and_capture(dtlb_env, vaddr, max_cycles=32): + vpn_expect = (int(vaddr) >> 12) & ((1 << 27) - 1) + for _ in range(max_cycles): + for i in range(4): + req = dtlb_env.bundle.ptw.req[i] + if int(req.valid.value) == 1 and int(req.bits_vpn.value) == vpn_expect: + return i, int(req.bits_s2xlate.value), int(req.bits_getGpa.value) + await dtlb_env.bundle.step() + raise AssertionError("Timed out waiting for ptw.req on vpn=0x%x" % vpn_expect) +def _canon_sv39_vaddr(va: int) -> int: + va = int(va) & ((1 << 64) - 1) + low39 = va & ((1 << 39) - 1) + sign = (low39 >> 38) & 1 + upper = ((-sign) & ((1 << (64 - 39)) - 1)) << 39 + return (upper | low39) & ((1 << 64) - 1) + +@toffee_test.testcase +async def test_ptwresp_s1_pf_propagates_to_requestor(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r0 is None + + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp(vaddr=va, paddr=pa, level=0, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s1_pf=True, + s2xlate=s2x) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r1 == -1 + + resp = dtlb_env.bundle.requestor[port].resp + assert int(resp.bits_excp_0_pf_st.value) == 1 + +@toffee_test.testcase +async def test_ptwresp_s1_af_propagates_to_requestor(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r0 is None + + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp(vaddr=va, paddr=pa, level=0, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s1_af=True, + s2xlate=s2x) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r1 == -1 + + resp = dtlb_env.bundle.requestor[port].resp + assert int(resp.bits_excp_0_af_st.value) == 1 + +@toffee_test.testcase +async def test_ptwresp_s2_gpf_propagates_to_requestor(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + gpa = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=STORE) + assert r0 is None + + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, gpa) + await dtlb_env.agent.set_ptw_resp(vaddr=gpa, paddr=0, level=0, + s1_v=False, + s2_tag=(gpa >> 12) & ((1<<27)-1), + s2_perm_u=True, s2_perm_w=True, s2_perm_a=True, + s2_perm_d=True, s2_perm_r=False, + s2_gpf=True, + s2xlate=s2x) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=STORE) + assert r1 == -1 + + resp = dtlb_env.bundle.requestor[port].resp + assert int(resp.bits_excp_0_gpf_st.value) == 1 + +@toffee_test.testcase +async def test_priv_sum_supervisor_can_read_user_page(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + csr.Satp.asid.value = 0x2 + csr.priv_dmode.value = 1 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=pa, level=0, + s1_asid=0x2, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s2xlate=s2x + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r1 == -1 + + csr.priv_sum.value = 1 + await dtlb_env.bundle.step() + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert r2 == expect + +@toffee_test.testcase +async def test_priv_vsum_vs_supervisor_can_read_user_page(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 0 + csr.Vsatp.asid.value = 0x34 + csr.HGatp.vmid.value = 0xB + csr.priv_dmode.value = 1 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=gpa, level=0, + s1_asid=0x34, s1_vmid=0xB, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s2xlate=s2x + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r1 == -1 + + csr.priv_vsum.value = 1 + await dtlb_env.bundle.step() + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + expect = (gpa & ~0xFFF) | (va & 0xFFF) + assert r2 == expect + +@toffee_test.testcase +async def test_asid_isolation_bare(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 0 + csr.Satp.mode.value = 8 + csr.Satp.asid.value = 0x10 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=pa, level=0, + s1_asid=0x10, s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s2xlate=s2x + ) + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert r1 == expect + + csr.Satp.asid.value = 0x21 + await dtlb_env.bundle.step() + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r2 is None + +@toffee_test.testcase +async def test_asid_isolation_onlyStage1(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 0 + csr.Vsatp.asid.value = 0x33 + csr.HGatp.vmid.value = 0xA + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r0 is None + _, s2x, _ = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=gpa, level=0, + s1_asid=0x33, s1_vmid=0xA, s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s2xlate=s2x + ) + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + expect = (gpa & ~0xFFF) | (va & 0xFFF) + assert r1 == expect + + csr.Vsatp.asid.value = 0x44 + csr.Vsatp.changed.value = True + await dtlb_env.bundle.step() + csr.Vsatp.changed.value = False + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r2 is None + + csr.Vsatp.asid.value = 0x33 + csr.HGatp.vmid.value = 0xB + csr.Vsatp.changed.value = True + csr.HGatp.changed.value = True + await dtlb_env.bundle.step() + csr.Vsatp.changed.value = False + csr.HGatp.changed.value = False + + r3 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r3 is None + +@toffee_test.testcase +async def test_vmid_isolation_onlyStage2(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + csr.HGatp.vmid.value = 0x55 + await dtlb_env.bundle.step() + + port = 0 + gpa_in = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=STORE) + assert r0 is None + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa_in) + await dtlb_env.agent.set_ptw_resp( + vaddr=gpa_in, paddr=0, level=0, + s1_v=False, + s2xlate=s2x, getGpa=getGpa_req, + s2_tag=(gpa_in >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=0, + s1_vmid=0x55, s2_perm_u=True, s2_perm_w=True, s2_perm_a=True, + s2_perm_d=True, s2_perm_r=False, + ) + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=STORE) + expect = (hpa & ~0xFFF) | (gpa_in & 0xFFF) + assert r1 == expect + + csr.HGatp.vmid.value = 0x66 + csr.HGatp.changed.value = True + await dtlb_env.bundle.step() + csr.HGatp.changed.value = False + + r2 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa_in, cmd=STORE) + assert r2 is None + +@toffee_test.testcase +async def test_hit_onlyStage1(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 0 + await dtlb_env.bundle.step() + + port = 0 + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + assert r0 is None + + ptw_port, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, va) + await dtlb_env.agent.set_ptw_resp( + vaddr=va, paddr=gpa, level=0, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s2xlate=s2x, getGpa=getGpa_req + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + expect = (gpa & ~0xFFF) | (va & 0xFFF) + assert r1 == expect + + +@toffee_test.testcase +async def test_hit_onlyStage2_4k(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + gpa = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=STORE) + assert r0 is None + + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa) + await dtlb_env.agent.set_ptw_resp( + vaddr=gpa, paddr=0, level=0, + s1_v=False, + s2xlate=s2x, getGpa=False, + s2_tag=(gpa >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=0, + s2_perm_u=True, s2_perm_w=True, s2_perm_a=True, + s2_perm_d=True, s2_perm_r=False, + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=STORE) + expect = (hpa & ~0xFFF) | (gpa & 0xFFF) + assert r1 == expect + +@toffee_test.testcase +async def test_hit_onlyStage2_2m(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + gpa = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) & ~((1<<21)-1) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) & ~((1<<21)-1) + + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=STORE) + assert r0 is None + + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa) + await dtlb_env.agent.set_ptw_resp( + vaddr=gpa, paddr=0, level=0, + s1_v=False, + s2xlate=s2x, getGpa=False, + s2_tag=(gpa >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=1, + s2_perm_u=True, s2_perm_w=True, s2_perm_a=True, + s2_perm_d=True, s2_perm_r=False, + ) + for off in (0x000, 0x1000, 0x1FF000): + va = gpa + off + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=STORE) + mask = (1 << 21) - 1 + expect = (hpa & ~mask) | (va & mask) + assert r1 == expect + +@toffee_test.testcase +async def test_hit_onlyStage2_1g(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 0 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + gpa = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) & ~((1<<30)-1) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) & ~((1<<30)-1) + + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gpa, cmd=STORE) + assert r0 is None + + _, s2x, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gpa) + await dtlb_env.agent.set_ptw_resp( + vaddr=gpa, paddr=0, level=0, + s1_v=False, + s2xlate=s2x, getGpa=False, + s2_tag=(gpa >> 12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=2, + s2_perm_u=True, s2_perm_w=True, s2_perm_a=True, + s2_perm_d=True, s2_perm_r=False, + ) + for off in (0x0, 0x21_000, 0x3FF_F000): + va = gpa + off + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=va, cmd=STORE) + mask = (1 << 30) - 1 + expect = (hpa & ~mask) | (va & mask) + assert r1 == expect + +@toffee_test.testcase +async def test_hit_allStage(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + port = 0 + gva = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 -1) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) + + r0 = await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=STORE) + assert r0 is None + + _, s2x, getGpa_ptw = await _wait_ptw_req_and_capture(dtlb_env, gva) + await dtlb_env.agent.set_ptw_resp( + vaddr=gva, paddr=gpa, level=0, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s2xlate=s2x, getGpa=getGpa_ptw, + s2_tag=(gpa>>12) & ((1<<27)-1), + s2_ppn=(hpa >> 12), s2_level=0, + s2_perm_u=True, s2_perm_w=True, s2_perm_a=True, + s2_perm_d=True, s2_perm_r=False, + ) + + r1 = await dtlb_env.agent.drive_request(port=port, vaddr=gva, cmd=STORE) + expect = (hpa & ~0xFFF) | (gva & 0xFFF) + assert r1 == expect + +@toffee_test.testcase +async def test_hit_allStage_with_getGpa(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + csr = dtlb_env.bundle.csr + csr.priv_virt.value = 1 + csr.Vsatp.mode.value = 8 + csr.HGatp.mode.value = 8 + await dtlb_env.bundle.step() + + gva = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + gpa = random.randint(2 ** 12, 2 ** 41 -1) + hpa = random.randint(2 ** 12, 2 ** 36 - 1) + + _ = await dtlb_env.agent.drive_request(port=0, vaddr=gva, cmd=STORE) + _, s2x, getGpa_ptw = await _wait_ptw_req_and_capture(dtlb_env, gva) + + await dtlb_env.agent.set_ptw_resp( + vaddr=gva, paddr=gpa, level=0, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s2xlate=s2x, getGpa=getGpa_ptw, + s2_ppn=(hpa >> 12), s2_level=0, + s2_perm_u=True, s2_perm_w=False, s2_perm_a=True, + s2_perm_d=True, s2_perm_r=False, + ) + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=gva, cmd=STORE) + + _, s2x_req, getGpa_req = await _wait_ptw_req_and_capture(dtlb_env, gva) + assert getGpa_req == 1 + + +@toffee_test.testcase +async def test_parallel_simul_req_miss(dtlb_env): + requestor = dtlb_env.bundle.requestor + + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + VA0 = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + VA1 = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + VA2 = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + + requestor[0].req.valid.value = 1 + requestor[0].req.bits_vaddr.value = VA0 + requestor[0].req.bits_fullva.value = VA0 + requestor[0].req.bits_cmd.value = STORE + requestor[0].req.bits_no_translate.value = 0 + requestor[1].req.valid.value = 1 + requestor[1].req.bits_vaddr.value = VA1 + requestor[1].req.bits_fullva.value = VA1 + requestor[1].req.bits_cmd.value = STORE + requestor[1].req.bits_no_translate.value = 0 + requestor[2].req.valid.value = 1 + requestor[2].req.bits_vaddr.value = VA2 + requestor[2].req.bits_fullva.value = VA2 + requestor[2].req.bits_cmd.value = STORE + requestor[2].req.bits_no_translate.value = 0 + + await dtlb_env.bundle.step() + + requestor[0].req.valid.value = 0 + requestor[1].req.valid.value = 0 + requestor[2].req.valid.value = 0 + await dtlb_env.bundle.step() + + +@toffee_test.testcase +async def test_parallel_simul_req_hit(dtlb_env): + requestor = dtlb_env.bundle.requestor + + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + VA0 = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + VA1 = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + VA2 = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + PA0 = random.randint(2 ** 12, 2** 36 - 1) + PA1 = random.randint(2 ** 12, 2** 36 - 1) + PA2 = random.randint(2 ** 12, 2** 36 - 1) + + + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA0, cmd=STORE) == None + await dtlb_env.agent.set_ptw_resp(vaddr=VA0,paddr=PA0,level=0,s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False) + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA0, cmd=STORE) == ((VA0 & (0xFFF)) | (PA0 & ~(0xFFF))) + + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA1, cmd=LOAD) == None + await dtlb_env.agent.set_ptw_resp(vaddr=VA1,paddr=PA1,level=0,s1_perm_u=True, s1_perm_r=True, s1_perm_a=True) + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA1, cmd=LOAD) == ((VA1 & (0xFFF)) | (PA1 & ~(0xFFF))) + + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA2, cmd=LOAD) == None + await dtlb_env.agent.set_ptw_resp(vaddr=VA2,paddr=PA2,level=0,s1_perm_u=True, s1_perm_r=True, s1_perm_a=True) + assert await dtlb_env.agent.drive_request(port=0, vaddr=VA2, cmd=LOAD) == ((VA2 & (0xFFF)) | (PA2 & ~(0xFFF))) + await dtlb_env.bundle.step() + requestor[0].req.valid.value = 1 + requestor[0].req.bits_vaddr.value = VA0 + requestor[0].req.bits_fullva.value = VA0 + requestor[0].req.bits_cmd.value = STORE + requestor[0].req.bits_no_translate.value = 0 + requestor[1].req.valid.value = 1 + requestor[1].req.bits_vaddr.value = VA1 + requestor[1].req.bits_fullva.value = VA1 + requestor[1].req.bits_cmd.value = LOAD + requestor[1].req.bits_no_translate.value = 0 + requestor[2].req.valid.value = 1 + requestor[2].req.bits_vaddr.value = VA2 + requestor[2].req.bits_fullva.value = VA2 + requestor[2].req.bits_cmd.value = LOAD + requestor[2].req.bits_no_translate.value = 0 + + await dtlb_env.bundle.step() + + requestor[0].req.valid.value = 0 + requestor[1].req.valid.value = 0 + requestor[2].req.valid.value = 0 + await dtlb_env.bundle.step() + +@toffee_test.testcase +async def test_store_2mb_page(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + va_base = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) & ~((1<<21)-1) + pa_base = random.randint(2 ** 12, 2 ** 36 - 1) & ~((1<<21)-1) + + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va_base, cmd=STORE) + assert r0 is None + + await dtlb_env.agent.set_ptw_resp( + vaddr=va_base, paddr=pa_base, level=1, s1_asid=0, + s1_ppn_low=[0]*8, s1_valididx=[1]*8, s1_pteidx=[0]*8, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + ) + + for off in (0x000, 0x1000, 0x1FF000): + va = va_base + off + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=STORE) + mask = (1 << 21) - 1 + expect = (pa_base & ~mask) | (va & mask) + assert r1 == expect + + +@toffee_test.testcase +async def test_store_1gb_page(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + va_base = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) & ~((1<<30)-1) + pa_base = random.randint(2 ** 12, 2 ** 36 - 1) & ~((1<<30)-1) + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va_base, cmd=STORE) + assert r0 is None + + await dtlb_env.agent.set_ptw_resp( + vaddr=va_base, paddr=pa_base, level=2, s1_asid=0, + s1_ppn_low=[0]*8, s1_valididx=[1]*8, s1_pteidx=[0]*8, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + ) + + for off in (0x0, 0x21_000, 0x3FF_F000): + va = va_base + off + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=STORE) + mask = (1 << 30) - 1 + expect = (pa_base & ~mask) | (va & mask) + assert r1 == expect + +@toffee_test.testcase +async def test_express_random(dtlb_env): + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=STORE) + assert r0 is None + vpn_low = (va >> 12) & 0x7 + ppn_low = (pa >> 12) & 0x7 + rand_valididx = [0]*8 + rand_ppn_low = [0]*8 + pteidx = [0]*8 + for i in range(8): + rand_valididx[i] = random.randint(0,1) + rand_ppn_low[i] = i + rand_valididx[vpn_low] = 1 + pteidx[vpn_low] = 1 + await dtlb_env.agent.set_ptw_resp( + vaddr=va, + paddr=pa, + level=0, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s1_asid=0, + s1_ppn_low=rand_ppn_low, + s1_valididx=rand_valididx, + s1_pteidx=pteidx + ) + va_base = va & ~((1<<15)-1) + pa_base = pa & ~((1<<15)-1) + for i in range(8): + va = va_base | (i << 12) + pa = pa_base | (rand_ppn_low[i] << 12) + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=STORE) + if rand_valididx[i] != 0: + expect = (pa & ~0xFFF) | (va & 0xFFF) + else: + expect = None + assert r1 == expect + +@toffee_test.testcase +async def test_express_full(dtlb_env): + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + + va = _canon_sv39_vaddr(random.randint(2 ** 12, 2 ** 39 - 1)) + pa = random.randint(2 ** 12, 2 ** 36 - 1) + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=STORE) + assert r0 is None + vpn_low = (va >> 12) & 0x7 + pteidx = [0]*8 + pteidx[vpn_low] = 1 + await dtlb_env.agent.set_ptw_resp( + vaddr=va, + paddr=pa, + level=0, + s1_asid=0, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + s1_ppn_low=[0,1,2,3,4,5,6,7], + s1_valididx=[1,1,1,1,1,1,1,1], + s1_pteidx=pteidx + ) + va_base = va & ~((1<<15)-1) + pa_base = pa & ~((1<<15)-1) + for i in range(8): + va = va_base | (i << 12) + pa = pa_base | (i << 12) + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=STORE) + expect = (pa & ~0xFFF) | (va & 0xFFF) + assert r1 == expect + +@toffee_test.testcase +async def test_req_kill(dtlb_env): + dtlb_env.dut.reset.value = 1; await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0; await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults(); await dtlb_env.bundle.step() + + port = 0 + rob = 0x3A + va = 0x0000_0000_8020_9000 + + req = dtlb_env.bundle.requestor[port].req + req.bits_vaddr.value = va & ((1<<50)-1) + req.bits_fullva.value = va + req.bits_cmd.value = 0 + req.bits_kill.value = 1 + req.valid.value = 1 + + req.valid.value = 0 + + N = 32 + for _ in range(N): + assert dtlb_env.bundle.requestor[port].resp.valid.value == 0 + for i in range(4): + assert dtlb_env.bundle.ptw.req[i].valid.value == 0 + await dtlb_env.bundle.step() + +@toffee_test.testcase +async def test_req_notranslate(dtlb_env): + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + + va = 0x0000_0000_8020_1000 + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=va, cmd=STORE, no_translate = 1) + assert r0 == va + + +@toffee_test.testcase +async def test_hit_smoke(dtlb_env): + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=0x0000_0000_8020_1000, cmd=STORE) + assert r0 is None + + await dtlb_env.agent.set_ptw_resp( + vaddr=0x0000_0000_8020_1000, + paddr=0x0000_0001_2345_6000, + level=0, + s1_asid=0, + s1_perm_u=True, s1_perm_w=True, s1_perm_a=True, + s1_perm_d=True, s1_perm_r=False, + ) + + r1 = await dtlb_env.agent.drive_request(port=0, vaddr=0x0000_0000_8020_1000, cmd=STORE) + expect = (0x0000_0001_2345_6000 & ~0xFFF) | (0x0000_0000_8020_1000 & 0xFFF) + assert r1 == expect + + +@toffee_test.testcase +async def test_miss_smoke(dtlb_env): + dtlb_env.dut.reset.value = 1 + await dtlb_env.bundle.step() + dtlb_env.dut.reset.value = 0 + await dtlb_env.bundle.step() + + await dtlb_env.set_sv39_defaults() + await dtlb_env.bundle.step() + + r0 = await dtlb_env.agent.drive_request(port=0, vaddr=0x0000_0000_8020_1000, cmd=STORE, kill=False ,no_translate=False) + + assert r0 is None \ No newline at end of file