From 65b2370e9dd07b3fb4aba82d4adb9b7b93f1a731 Mon Sep 17 00:00:00 2001 From: chenjinhua Date: Mon, 23 Feb 2026 16:46:35 +0800 Subject: [PATCH] ifu top ref and env test added --- scripts/ifu_related/ifu_top_internals.yaml | 320 +++++++++- ut_frontend/ifu/ifu_top/agent/all_agent.py | 551 ++++++++++++++++-- ut_frontend/ifu/ifu_top/bundle/__init__.py | 2 +- ut_frontend/ifu/ifu_top/bundle/auto_bundle.py | 271 ++++----- ut_frontend/ifu/ifu_top/commons.py | 37 ++ ut_frontend/ifu/ifu_top/datadef/__init__.py | 10 +- .../datadef/frontend_trigger_datadef.py | 25 +- .../ifu/ifu_top/datadef/ftq_datadef.py | 229 ++++++-- .../ifu/ifu_top/datadef/ibuffer_datadef.py | 136 ++++- .../ifu/ifu_top/datadef/icache_datadef.py | 138 ++++- .../ifu/ifu_top/datadef/mmio_related.py | 279 ++++++++- .../ifu/ifu_top/datadef/req_datadef.py | 226 +++++++ .../ifu/ifu_top/datadef/sub_modules_def.py | 94 +++ ut_frontend/ifu/ifu_top/env/__init__.py | 3 +- .../ifu/ifu_top/env/ifu_icache_receiver.py | 28 + ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py | 172 ++++++ .../ifu/ifu_top/env/ifu_req_receiver_ref.py | 369 ++++++++++++ ut_frontend/ifu/ifu_top/env/ifu_top_env.py | 6 +- .../ifu/ifu_top/env/pred_checker_ref.py | 222 +++++++ ut_frontend/ifu/ifu_top/env/predecode_ref.py | 77 +++ .../ifu/ifu_top/env/rvc_expander_ref.py | 329 +++++++++++ ut_frontend/ifu/ifu_top/instr_utils.py | 275 +++++++++ ut_frontend/ifu/ifu_top/test/ckpt_mmio.py | 75 +++ ut_frontend/ifu/ifu_top/test/ckpt_subs.py | 122 ++++ ut_frontend/ifu/ifu_top/test/ckpt_tops.py | 86 +++ .../ifu/ifu_top/test/mmio_states_test.py | 271 +++++++++ ut_frontend/ifu/ifu_top/test/non_mmio_test.py | 299 ++++++++++ ut_frontend/ifu/ifu_top/test/smoke_test.py | 132 ----- .../ifu/ifu_top/test/top_test_fixture.py | 21 +- 29 files changed, 4341 insertions(+), 464 deletions(-) create mode 100644 ut_frontend/ifu/ifu_top/commons.py create mode 100644 ut_frontend/ifu/ifu_top/datadef/req_datadef.py create mode 100644 ut_frontend/ifu/ifu_top/datadef/sub_modules_def.py create mode 100644 ut_frontend/ifu/ifu_top/env/ifu_icache_receiver.py create mode 100644 ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/ifu_req_receiver_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/pred_checker_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/predecode_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/rvc_expander_ref.py create mode 100644 ut_frontend/ifu/ifu_top/instr_utils.py create mode 100644 ut_frontend/ifu/ifu_top/test/ckpt_mmio.py create mode 100644 ut_frontend/ifu/ifu_top/test/ckpt_subs.py create mode 100644 ut_frontend/ifu/ifu_top/test/ckpt_tops.py create mode 100644 ut_frontend/ifu/ifu_top/test/mmio_states_test.py create mode 100644 ut_frontend/ifu/ifu_top/test/non_mmio_test.py delete mode 100644 ut_frontend/ifu/ifu_top/test/smoke_test.py diff --git a/scripts/ifu_related/ifu_top_internals.yaml b/scripts/ifu_related/ifu_top_internals.yaml index c71d7314..07a0d596 100644 --- a/scripts/ifu_related/ifu_top_internals.yaml +++ b/scripts/ifu_related/ifu_top_internals.yaml @@ -1,4 +1,322 @@ NewIFU: # - "wire f1_flush" - "wire f2_flush" - - "wire f3_flush" \ No newline at end of file + - "wire f3_flush" + - "wire f1_ready" + - "wire f1_valid" + - "wire wb_enable" + - "wire f1_fire" + - "wire f0_fire" + - "wire f2_ready" + - "wire f3_ready" + - "wire f2_fire" + - "wire f2_valid" + - "wire f3_valid" + - "wire f2_icache_all_resp_wire" + - "wire f2_icache_all_resp_reg" + - "wire icacheRespAllValid" + - "wire f0_flush_from_bpu_probe" + - "wire [1:0] f2_exception_0" + - "wire [1:0] f2_exception_1" + - "wire is_first_instr" + - "reg f3_lastHalf_valid" + - "reg [3:0] mmio_state" + - "reg [1:0] f3_exception_vec_0" + - "reg [1:0] f3_exception_vec_1" + - "reg [1:0] f3_exception_vec_2" + - "reg [1:0] f3_exception_vec_3" + - "reg [1:0] f3_exception_vec_4" + - "reg [1:0] f3_exception_vec_5" + - "reg [1:0] f3_exception_vec_6" + - "reg [1:0] f3_exception_vec_7" + - "reg [1:0] f3_exception_vec_8" + - "reg [1:0] f3_exception_vec_9" + - "reg [1:0] f3_exception_vec_10" + - "reg [1:0] f3_exception_vec_11" + - "reg [1:0] f3_exception_vec_12" + - "reg [1:0] f3_exception_vec_13" + - "reg [1:0] f3_exception_vec_14" + - "reg [1:0] f3_exception_vec_15" + - "reg [6:0] f2_cut_ptr_0" + - "reg [6:0] f2_cut_ptr_1" + - "reg [6:0] f2_cut_ptr_2" + - "reg [6:0] f2_cut_ptr_3" + - "reg [6:0] f2_cut_ptr_4" + - "reg [6:0] f2_cut_ptr_5" + - "reg [6:0] f2_cut_ptr_6" + - "reg [6:0] f2_cut_ptr_7" + - "reg [6:0] f2_cut_ptr_8" + - "reg [6:0] f2_cut_ptr_9" + - "reg [6:0] f2_cut_ptr_10" + - "reg [6:0] f2_cut_ptr_11" + - "reg [6:0] f2_cut_ptr_12" + - "reg [6:0] f2_cut_ptr_13" + - "reg [6:0] f2_cut_ptr_14" + - "reg [6:0] f2_cut_ptr_15" + - "reg [6:0] f2_cut_ptr_16" + - "reg [49:0] f3_pc_0" + - "reg [49:0] f3_pc_1" + - "reg [49:0] f3_pc_2" + - "reg [49:0] f3_pc_3" + - "reg [49:0] f3_pc_4" + - "reg [49:0] f3_pc_5" + - "reg [49:0] f3_pc_6" + - "reg [49:0] f3_pc_7" + - "reg [49:0] f3_pc_8" + - "reg [49:0] f3_pc_9" + - "reg [49:0] f3_pc_10" + - "reg [49:0] f3_pc_11" + - "reg [49:0] f3_pc_12" + - "reg [49:0] f3_pc_13" + - "reg [49:0] f3_pc_14" + - "reg [49:0] f3_pc_15" + - "reg [47:0] f3_paddrs_0" + - "reg [55:0] f3_gpaddr" + - "wire [15:0] f3_instr_range" + # - "wire mmioFlushWb_bits_pd_0_isRVC" + # - "wire mmioFlushWb_bits_pd_0_brType" + # - "wire mmioFlushWb_bits_pd_0_isRet" + # - "wire mmioFlushWb_bits_pd_0_isCall" + + - preDecoder: + - "wire [15:0] io_in_bits_data_0" + - "wire [15:0] io_in_bits_data_1" + - "wire [15:0] io_in_bits_data_2" + - "wire [15:0] io_in_bits_data_3" + - "wire [15:0] io_in_bits_data_4" + - "wire [15:0] io_in_bits_data_5" + - "wire [15:0] io_in_bits_data_6" + - "wire [15:0] io_in_bits_data_7" + - "wire [15:0] io_in_bits_data_8" + - "wire [15:0] io_in_bits_data_9" + - "wire [15:0] io_in_bits_data_10" + - "wire [15:0] io_in_bits_data_11" + - "wire [15:0] io_in_bits_data_12" + - "wire [15:0] io_in_bits_data_13" + - "wire [15:0] io_in_bits_data_14" + - "wire [15:0] io_in_bits_data_15" + - "wire [15:0] io_in_bits_data_16" + - "wire io_out_pd_0_isRVC" + - "wire io_out_pd_1_valid" + - "wire io_out_pd_1_isRVC" + - "wire io_out_pd_2_valid" + - "wire io_out_pd_2_isRVC" + - "wire io_out_pd_3_valid" + - "wire io_out_pd_3_isRVC" + - "wire io_out_pd_4_valid" + - "wire io_out_pd_4_isRVC" + - "wire io_out_pd_5_valid" + - "wire io_out_pd_5_isRVC" + - "wire io_out_pd_6_valid" + - "wire io_out_pd_6_isRVC" + - "wire io_out_pd_7_valid" + - "wire io_out_pd_7_isRVC" + - "wire io_out_pd_8_valid" + - "wire io_out_pd_8_isRVC" + - "wire io_out_pd_9_valid" + - "wire io_out_pd_9_isRVC" + - "wire io_out_pd_10_valid" + - "wire io_out_pd_10_isRVC" + - "wire io_out_pd_11_valid" + - "wire io_out_pd_11_isRVC" + - "wire io_out_pd_12_valid" + - "wire io_out_pd_12_isRVC" + - "wire io_out_pd_13_valid" + - "wire io_out_pd_13_isRVC" + - "wire io_out_pd_14_valid" + - "wire io_out_pd_14_isRVC" + - "wire io_out_pd_15_valid" + - "wire io_out_pd_15_isRVC" + - "wire io_out_hasHalfValid_2" + - "wire io_out_hasHalfValid_3" + - "wire io_out_hasHalfValid_4" + - "wire io_out_hasHalfValid_5" + - "wire io_out_hasHalfValid_6" + - "wire io_out_hasHalfValid_7" + - "wire io_out_hasHalfValid_8" + - "wire io_out_hasHalfValid_9" + - "wire io_out_hasHalfValid_10" + - "wire io_out_hasHalfValid_11" + - "wire io_out_hasHalfValid_12" + - "wire io_out_hasHalfValid_13" + - "wire io_out_hasHalfValid_14" + - "wire io_out_hasHalfValid_15" + - "reg [31:0] io_out_instr_0" + - "reg [31:0] io_out_instr_1" + - "reg [31:0] io_out_instr_2" + - "reg [31:0] io_out_instr_3" + - "reg [31:0] io_out_instr_4" + - "reg [31:0] io_out_instr_5" + - "reg [31:0] io_out_instr_6" + - "reg [31:0] io_out_instr_7" + - "reg [31:0] io_out_instr_8" + - "reg [31:0] io_out_instr_9" + - "reg [31:0] io_out_instr_10" + - "reg [31:0] io_out_instr_11" + - "reg [31:0] io_out_instr_12" + - "reg [31:0] io_out_instr_13" + - "reg [31:0] io_out_instr_14" + - "reg [31:0] io_out_instr_15" + - "reg [63:0] io_out_jumpOffset_0" + - "reg [63:0] io_out_jumpOffset_1" + - "reg [63:0] io_out_jumpOffset_2" + - "reg [63:0] io_out_jumpOffset_3" + - "reg [63:0] io_out_jumpOffset_4" + - "reg [63:0] io_out_jumpOffset_5" + - "reg [63:0] io_out_jumpOffset_6" + - "reg [63:0] io_out_jumpOffset_7" + - "reg [63:0] io_out_jumpOffset_8" + - "reg [63:0] io_out_jumpOffset_9" + - "reg [63:0] io_out_jumpOffset_10" + - "reg [63:0] io_out_jumpOffset_11" + - "reg [63:0] io_out_jumpOffset_12" + - "reg [63:0] io_out_jumpOffset_13" + - "reg [63:0] io_out_jumpOffset_14" + - "reg [63:0] io_out_jumpOffset_15" + - f3Predecoder: + - "reg [1:0] io_out_pd_0_brType" + - "wire io_out_pd_0_isCall" + - "wire io_out_pd_0_isRet" + - "reg [1:0] io_out_pd_1_brType" + - "wire io_out_pd_1_isCall" + - "wire io_out_pd_1_isRet" + - "reg [1:0] io_out_pd_2_brType" + - "wire io_out_pd_2_isCall" + - "wire io_out_pd_2_isRet" + - "reg [1:0] io_out_pd_3_brType" + - "wire io_out_pd_3_isCall" + - "wire io_out_pd_3_isRet" + - "reg [1:0] io_out_pd_4_brType" + - "wire io_out_pd_4_isCall" + - "wire io_out_pd_4_isRet" + - "reg [1:0] io_out_pd_5_brType" + - "wire io_out_pd_5_isCall" + - "wire io_out_pd_5_isRet" + - "reg [1:0] io_out_pd_6_brType" + - "wire io_out_pd_6_isCall" + - "wire io_out_pd_6_isRet" + - "reg [1:0] io_out_pd_7_brType" + - "wire io_out_pd_7_isCall" + - "wire io_out_pd_7_isRet" + - "reg [1:0] io_out_pd_8_brType" + - "wire io_out_pd_8_isCall" + - "wire io_out_pd_8_isRet" + - "reg [1:0] io_out_pd_9_brType" + - "wire io_out_pd_9_isCall" + - "wire io_out_pd_9_isRet" + - "reg [1:0] io_out_pd_10_brType" + - "wire io_out_pd_10_isCall" + - "wire io_out_pd_10_isRet" + - "reg [1:0] io_out_pd_11_brType" + - "wire io_out_pd_11_isCall" + - "wire io_out_pd_11_isRet" + - "reg [1:0] io_out_pd_12_brType" + - "wire io_out_pd_12_isCall" + - "wire io_out_pd_12_isRet" + - "reg [1:0] io_out_pd_13_brType" + - "wire io_out_pd_13_isCall" + - "wire io_out_pd_13_isRet" + - "reg [1:0] io_out_pd_14_brType" + - "wire io_out_pd_14_isCall" + - "wire io_out_pd_14_isRet" + - "reg [1:0] io_out_pd_15_brType" + - "wire io_out_pd_15_isCall" + - "wire io_out_pd_15_isRet" + + - predChecker: + - "wire io_in_instrValid_0" + - "wire io_in_instrValid_1" + - "wire io_in_instrValid_2" + - "wire io_in_instrValid_3" + - "wire io_in_instrValid_4" + - "wire io_in_instrValid_5" + - "wire io_in_instrValid_6" + - "wire io_in_instrValid_7" + - "wire io_in_instrValid_8" + - "wire io_in_instrValid_9" + - "wire io_in_instrValid_10" + - "wire io_in_instrValid_11" + - "wire io_in_instrValid_12" + - "wire io_in_instrValid_13" + - "wire io_in_instrValid_14" + - "wire io_in_instrValid_15" + - "wire io_out_stage1Out_fixedRange_0" + - "wire io_out_stage1Out_fixedRange_1" + - "wire io_out_stage1Out_fixedRange_2" + - "wire io_out_stage1Out_fixedRange_3" + - "wire io_out_stage1Out_fixedRange_4" + - "wire io_out_stage1Out_fixedRange_5" + - "wire io_out_stage1Out_fixedRange_6" + - "wire io_out_stage1Out_fixedRange_7" + - "wire io_out_stage1Out_fixedRange_8" + - "wire io_out_stage1Out_fixedRange_9" + - "wire io_out_stage1Out_fixedRange_10" + - "wire io_out_stage1Out_fixedRange_11" + - "wire io_out_stage1Out_fixedRange_12" + - "wire io_out_stage1Out_fixedRange_13" + - "wire io_out_stage1Out_fixedRange_14" + - "wire io_out_stage1Out_fixedRange_15" + - "wire io_out_stage1Out_fixedTaken_0" + - "wire io_out_stage1Out_fixedTaken_1" + - "wire io_out_stage1Out_fixedTaken_2" + - "wire io_out_stage1Out_fixedTaken_3" + - "wire io_out_stage1Out_fixedTaken_4" + - "wire io_out_stage1Out_fixedTaken_5" + - "wire io_out_stage1Out_fixedTaken_6" + - "wire io_out_stage1Out_fixedTaken_7" + - "wire io_out_stage1Out_fixedTaken_8" + - "wire io_out_stage1Out_fixedTaken_9" + - "wire io_out_stage1Out_fixedTaken_10" + - "wire io_out_stage1Out_fixedTaken_11" + - "wire io_out_stage1Out_fixedTaken_12" + - "wire io_out_stage1Out_fixedTaken_13" + - "wire io_out_stage1Out_fixedTaken_14" + - "wire io_out_stage1Out_fixedTaken_15" + - "reg [49:0] io_out_stage2Out_fixedTarget_0" + - "reg [49:0] io_out_stage2Out_fixedTarget_1" + - "reg [49:0] io_out_stage2Out_fixedTarget_2" + - "reg [49:0] io_out_stage2Out_fixedTarget_3" + - "reg [49:0] io_out_stage2Out_fixedTarget_4" + - "reg [49:0] io_out_stage2Out_fixedTarget_5" + - "reg [49:0] io_out_stage2Out_fixedTarget_6" + - "reg [49:0] io_out_stage2Out_fixedTarget_7" + - "reg [49:0] io_out_stage2Out_fixedTarget_8" + - "reg [49:0] io_out_stage2Out_fixedTarget_9" + - "reg [49:0] io_out_stage2Out_fixedTarget_10" + - "reg [49:0] io_out_stage2Out_fixedTarget_11" + - "reg [49:0] io_out_stage2Out_fixedTarget_12" + - "reg [49:0] io_out_stage2Out_fixedTarget_13" + - "reg [49:0] io_out_stage2Out_fixedTarget_14" + - "reg [49:0] io_out_stage2Out_fixedTarget_15" + - "reg [49:0] io_out_stage2Out_jalTarget_0" + - "reg [49:0] io_out_stage2Out_jalTarget_1" + - "reg [49:0] io_out_stage2Out_jalTarget_2" + - "reg [49:0] io_out_stage2Out_jalTarget_3" + - "reg [49:0] io_out_stage2Out_jalTarget_4" + - "reg [49:0] io_out_stage2Out_jalTarget_5" + - "reg [49:0] io_out_stage2Out_jalTarget_6" + - "reg [49:0] io_out_stage2Out_jalTarget_7" + - "reg [49:0] io_out_stage2Out_jalTarget_8" + - "reg [49:0] io_out_stage2Out_jalTarget_9" + - "reg [49:0] io_out_stage2Out_jalTarget_10" + - "reg [49:0] io_out_stage2Out_jalTarget_11" + - "reg [49:0] io_out_stage2Out_jalTarget_12" + - "reg [49:0] io_out_stage2Out_jalTarget_13" + - "reg [49:0] io_out_stage2Out_jalTarget_14" + - "reg [49:0] io_out_stage2Out_jalTarget_15" + - "reg [2:0] io_out_stage2Out_faultType_0_value" + - "reg [2:0] io_out_stage2Out_faultType_1_value" + - "reg [2:0] io_out_stage2Out_faultType_2_value" + - "reg [2:0] io_out_stage2Out_faultType_3_value" + - "reg [2:0] io_out_stage2Out_faultType_4_value" + - "reg [2:0] io_out_stage2Out_faultType_5_value" + - "reg [2:0] io_out_stage2Out_faultType_6_value" + - "reg [2:0] io_out_stage2Out_faultType_7_value" + - "reg [2:0] io_out_stage2Out_faultType_8_value" + - "reg [2:0] io_out_stage2Out_faultType_9_value" + - "reg [2:0] io_out_stage2Out_faultType_10_value" + - "reg [2:0] io_out_stage2Out_faultType_11_value" + - "reg [2:0] io_out_stage2Out_faultType_12_value" + - "reg [2:0] io_out_stage2Out_faultType_13_value" + - "reg [2:0] io_out_stage2Out_faultType_14_value" + - "reg [2:0] io_out_stage2Out_faultType_15_value" diff --git a/ut_frontend/ifu/ifu_top/agent/all_agent.py b/ut_frontend/ifu/ifu_top/agent/all_agent.py index d0b1e0fc..b9164488 100644 --- a/ut_frontend/ifu/ifu_top/agent/all_agent.py +++ b/ut_frontend/ifu/ifu_top/agent/all_agent.py @@ -1,7 +1,26 @@ -from toffee import Agent +from toffee import Agent, driver_method from ..bundle import IFUTopBundle, StageFlushBundle, RobCommitBundle from ..datadef import FTQQuery, ICacheStatusResp, FTQFlushInfo, FTQResp, FrontendTriggerReq, \ - ToIbufferAllRes, ITLBReq, ITLBResp, ToUncache, FromUncache, FTQIdx, PMPResp, RobCommit + ToIbufferAllRes, ITLBReq, ITLBResp, ToUncache, FromUncache, FTQIdx, PMPResp, RobCommit, FTQFlushFromBPU, ExistsIdx,\ + PreDecodeDataDef, F3PreDecodeData, PredCheckerRetData, PredCheckerStage1RetData, PredCheckerStage2RetData, FTQRedirect, \ + FTQFlushFromBPU, NonMMIOSingleReq, NonMMIOResp, MMIOCycleInfo, MMIOReq, MMIOState, MMIOToIbufferFTQ, mmio_data_logger_instance,\ + MMIOStateDataLogger, non_mmio_logger_instance, NonMMIOReqLogger, ClusteredNonMMIOReqs, ICacheResp + +class HistoryStat(): + def __init__(self, + cluster: ClusteredNonMMIOReqs, + resps: list[NonMMIOResp], + tasks: list, + valids: str + ): + self.cluster=cluster + self.resps = resps + self.tasks = tasks + self.valids = valids + + def __repr__(self): + return f"HistoryStat(\ntasks: {self.tasks}\ncluster: {self.cluster}\nresps: {self.resps}\nVALIDs:{self.valids}\n)\n" + class OutsideAgent(Agent): def __init__(self, bundle: IFUTopBundle): @@ -11,32 +30,87 @@ def __init__(self, bundle: IFUTopBundle): self.ftqWbBundle = bundle.io_ftqInter._toFtq_pdWb self.ftqRedirectBundle = bundle.io_ftqInter._fromFtq._redirect self.ftqFlushStgsBundle = bundle.io_ftqInter._fromFtq._flushFromBpu - self.flushResBundle = bundle.internal_flushes + self.flushResBundle = bundle.internal_wires self.icacheInter = self.top._icacheInterCtrl._icacheInter self.frontendTriggerBundle = self.top.io_frontendTrigger self.toIbufferAll = self.top.to_ibuffer_all self.itlbInter = self.top.mmio_needed._iTLBInter self.uncacheInter = self.top.mmio_needed._uncacheInter + self.cur_state = MMIOState.STATE_IDLE + self.last_state = MMIOState.STATE_COMMITED + + self.replay_stats_stack = [] + + # @driver_method() + async def step(self): + await self.top.step() - async def set_icache_ready(self, ready): + def set_icache_ready(self, ready): self.icacheInter._icacheReady.value = ready - async def get_ftq_ready(self): - return self.ftqReqBundle._ready.value + def get_ftq_ready(self) -> bool: + return self.ftqReqBundle._ready.value != 0 - async def query_from_ftq(self, query: FTQQuery): + # @driver_method() + def query_from_ftq(self, query: FTQQuery): self.ftqReqBundle._bits._ftqIdx._flag.value = query.ftqIdx.flag self.ftqReqBundle._bits._ftqIdx._value.value = query.ftqIdx.value + self.ftqReqBundle._bits._ftqOffset._valid.value = query.ftqOffset.exists self.ftqReqBundle._bits._ftqOffset._bits.value = query.ftqOffset.offsetIdx - self.ftqReqBundle._bits._nextlineStart.value = query.nextlineStart self.ftqReqBundle._bits._startAddr.value = query.startAddr self.ftqReqBundle._bits._nextStartAddr.value = query.nextStartAddr + + self.ftqReqBundle._bits._nextlineStart.value = query.nextlineStart + self.ftqReqBundle._valid.value = query.valid + + def get_exception(self) -> list[int]: + return [self.top.internal_wires.f2_exceptions[i].value for i in range(2)] + + def get_exception_vecs(self) -> list[int]: + # f2_exception = [self.top.internal_wires.f2_exceptions[i].value for i in range(2)] + exception_vec = [self.top.internal_wires.f3_exception_vec[i].value for i in range(16)] + return exception_vec + + def get_f3_pcs(self)->list[int]: + return [pc.value for pc in self.top.internal_wires.f3_pcs] - async def from_ftq_flush(self, ftqFlushInfo:FTQFlushInfo): - flush_bpu = ftqFlushInfo.flush_from_bpu - redirect = ftqFlushInfo.redirect + def get_cut_ptrs(self) -> list[int]: + return [cut_ptr.value for cut_ptr in self.top.internal_wires.f2_cut_ptrs] + + + def get_addrs(self)->tuple[int, int]: + return self.top.internal_wires._f3_paddrs_0.value, self.top.internal_wires._f3_gpaddr.value + + def get_cut_instrs(self) -> list[int]: + return [instr.value for instr in self.top.internal_wires.pre_decoder.in_bits_data] + + def get_ranges(self) -> int: + return self.top.internal_wires._f3_instr_range.value + + def get_bpu_flush(self) -> bool: + return self.top.internal_wires._f0_flush_from_bpu_probe.value != 0 + + def get_fires(self): + return {0: self.top.internal_wires._f0_fire.value, 1: self.top.internal_wires._f1_fire.value, 2:self.top.internal_wires._f2_fire.value, 3:self.top.internal_wires._wb_enable.value} + + def get_flushes(self): + return {0: self.top.internal_wires._f0_flush_from_bpu_probe.value, 1: self.top.internal_wires._f2_flush.value, 2:self.top.internal_wires._f2_flush.value, 3:self.top.internal_wires._f3_flush.value} + + def get_readys(self): + return {1:self.top.internal_wires._f1_ready.value, 2: self.top.internal_wires._f2_ready.value, 3: self.top.internal_wires._f3_ready.value} + + def get_valids(self): + return {0:self.top.io_ftqInter._fromFtq._req._ready.value, 1:self.top.internal_wires._f1_valid.value, \ + 2: self.top.internal_wires._f2_valid.value, 3: self.top.internal_wires._f3_valid.value} + + def get_icache_all_resp(self)-> bool: + return self.top.internal_wires._icacheRespAllValid.value + + def from_ftq_flush(self, flush_bpu: FTQFlushFromBPU): + # redirect = ftqFlushInfo.redirect + # flush_bpu = ftqFlushInfo.flush_from_bpu stg_names = ["s2", "s3"] for stg_name in stg_names: stg_bundle: StageFlushBundle = getattr(self.ftqFlushStgsBundle, f"_{stg_name}") @@ -45,13 +119,47 @@ async def from_ftq_flush(self, ftqFlushInfo:FTQFlushInfo): stg_bundle._bits._value.value = stg_redirect.ftqIdx.value stg_bundle._valid.value = stg_redirect.stg_valid + + def ftq_redirect(self, redirect: FTQRedirect): self.ftqRedirectBundle._valid.value = redirect.valid self.ftqRedirectBundle._bits._ftqOffset.value = redirect.ftqOffset self.ftqRedirectBundle._bits._level.value = redirect.redirect_level self.ftqRedirectBundle._bits._ftqIdx._value.value = redirect.ftqIdx.value self.ftqRedirectBundle._bits._ftqIdx._flag.value = redirect.ftqIdx.flag + + def get_predecode_res(self) -> PreDecodeDataDef: + predecode_data = PreDecodeDataDef() + predecode_data.new_instrs=[instr.value for instr in self.top.internal_wires.pre_decoder.out_instrs] + predecode_data.jmp_offsets=[jmp_offset.value for jmp_offset in self.top.internal_wires.pre_decoder.out_jumpOffsets] + predecode_data.rvcs = [rvc.value for rvc in self.top.internal_wires.pre_decoder.out_pd_isRVCs] + predecode_data.valid_starts = [1] + [valid.value for valid in self.top.internal_wires.pre_decoder.out_pd_valids] + predecode_data.half_valid_starts = [0, 1] + [valid.value for valid in self.top.internal_wires.pre_decoder.out_hasHalfValids] + return predecode_data + + def get_f3predecoder_res(self)->F3PreDecodeData: + f3predecode_data = F3PreDecodeData() + f3predecode_data.brTypes = [br_type.value for br_type in self.top.internal_wires.f3_predecoder.out_pd_brTypes] + f3predecode_data.isCalls = [is_call.value for is_call in self.top.internal_wires.f3_predecoder.out_pd_isCalls] + f3predecode_data.isRets = [is_ret.value for is_ret in self.top.internal_wires.f3_predecoder.out_pd_isRets] + + return f3predecode_data - async def collect_res_backto_ftq(self): + def get_pred_checker_stg1_res(self) -> PredCheckerStage1RetData: + pred_check_res = PredCheckerStage1RetData() + pred_check_res.takens = [taken.value for taken in self.top.internal_wires.pred_checker.fixed_takens] + pred_check_res.ranges = [fix_range.value for fix_range in self.top.internal_wires.pred_checker.fixed_ranges] + + return pred_check_res + + def get_pred_checker_stg2_res(self) -> PredCheckerStage2RetData: + pred_check_res = PredCheckerStage2RetData() + pred_check_res.faults = [fault.value for fault in self.top.internal_wires.pred_checker.fault_types] + pred_check_res.fixed_tgts = [fixed_tgt.value for fixed_tgt in self.top.internal_wires.pred_checker.fixed_targets] + pred_check_res.jmp_tgts = [jmp_tgt.value for jmp_tgt in self.top.internal_wires.pred_checker.jal_targets] + + return pred_check_res + + def collect_res_backto_ftq(self)-> FTQResp: response = FTQResp() response.valid = self.ftqWbBundle._valid.value response.cfiOffset_valid = self.ftqWbBundle._bits._cfiOffset_valid.value @@ -59,45 +167,54 @@ async def collect_res_backto_ftq(self): response.ftqIdx.value = self.ftqWbBundle._bits._ftqIdx._value.value response.jalTarget = self.ftqWbBundle._bits._jalTarget.value response.target = self.ftqWbBundle._bits._target.value + + # response.misOffset = await self.get_wb_flush() response.misOffset.exists = self.ftqWbBundle._bits._misOffset._valid.value response.misOffset.offsetIdx = self.ftqWbBundle._bits._misOffset._bits.value for i in range(16): - response.pcs[i] = getattr(self.ftqWbBundle._bits._pc, f"_{i}").value - response.instrRanges[i] = getattr(self.ftqWbBundle._bits._instrRange, f"_{i}").value - response.pds[i].brType = getattr(self.ftqWbBundle._bits._pd, f"_{i}")._brType.value - response.pds[i].isCall = getattr(self.ftqWbBundle._bits._pd, f"_{i}")._isCall.value - response.pds[i].isRet = getattr(self.ftqWbBundle._bits._pd, f"_{i}")._isRet.value - response.pds[i].isRVC = getattr(self.ftqWbBundle._bits._pd, f"_{i}")._isRVC.value - response.pds[i].pdValid = getattr(self.ftqWbBundle._bits._pd, f"_{i}")._valid.value + response.pcs[i] = self.ftqWbBundle._bits._pc[i].value + response.instrRanges[i] = self.ftqWbBundle._bits._instrRange[i].value + response.pds.brTypes[i] = self.ftqWbBundle._bits._pd[i]._brType.value + response.pds.isCalls[i] = self.ftqWbBundle._bits._pd[i]._isCall.value + response.pds.isRets[i] = self.ftqWbBundle._bits._pd[i]._isRet.value + response.pds.isRVCs[i] = self.ftqWbBundle._bits._pd[i]._isRVC.value + response.pds.pdValids[i] = self.ftqWbBundle._bits._pd[i]._valid.value return response - - async def fake_resp(self, icacheStatusResp: ICacheStatusResp): + + # 实际上,核心的指令“数据”在这个周期才给出 + def fake_resp_stat(self, icacheStatusResp: ICacheStatusResp): icache_resp = icacheStatusResp.resp - self.icacheInter._icacheReady.value = icacheStatusResp.ready + self.fake_resp(icache_resp) + + def fake_resp(self, icache_resp: ICacheResp): self.icacheInter._resp._valid.value = icache_resp.icache_valid + # self.icacheInter._icacheReady.value = icacheStatusResp.ready self.icacheInter._resp._bits._isForVSnonLeafPTE.value = icache_resp.VS_non_leaf_PTE self.icacheInter._resp._bits._doubleline.value = icache_resp.double_line self.icacheInter._resp._bits._backendException.value = icache_resp.backend_exception self.icacheInter._resp._bits._gpaddr.value = icache_resp.gpaddr self.icacheInter._resp._bits._data.value = icache_resp.data - self.icacheInter._resp._bits._paddr._0.value = icache_resp.paddr + self.icacheInter._resp._bits._paddr_0.value = icache_resp.paddr + for i in range(2): - getattr(self.icacheInter._resp._bits._itlb_pbmt, f"_{i}").value = icache_resp.itlb_pbmts[i] - getattr(self.icacheInter._resp._bits._exception, f"_{i}").value = icache_resp.exceptions[i] - getattr(self.icacheInter._resp._bits._vaddr, f"_{i}").value = icache_resp.vaddrs[i] - getattr(self.icacheInter._resp._bits._pmp_mmio, f"_{i}").value = icache_resp.pmp_mmios[i] + self.icacheInter._resp._bits._itlb_pbmt[i].value = icache_resp.itlb_pbmts[i] + self.icacheInter._resp._bits._exception[i].value = icache_resp.exceptions[i] + self.icacheInter._resp._bits._vaddr[i].value = icache_resp.vaddrs[i] + self.icacheInter._resp._bits._pmp_mmio[i].value = icache_resp.pmp_mmios[i] + + def set_fs_is_off(self, fs_is_off): + self.top.ctrl.io_csr_fsIsOff.value = fs_is_off + async def get_icache_stop(self): return self.top._icacheInterCtrl._icacheStop.value - - async def set_triggers(self, req: FrontendTriggerReq): for i in range(4): - getattr(self.frontendTriggerBundle._tEnableVec, f"_{i}").value = req.tEnableVec[i] + self.frontendTriggerBundle._tEnableVec[i].value = req.tEnableVec[i] self.frontendTriggerBundle._tUpdate._valid.value = req.bpSetter.valid @@ -110,13 +227,12 @@ async def set_triggers(self, req: FrontendTriggerReq): self.frontendTriggerBundle._tUpdate._bits._tdata._tdata2.value = req.bpSetter.tdata2 self.frontendTriggerBundle._debugMode.value = req.debugMode self.frontendTriggerBundle._triggerCanRaiseBpExp.value = req.triggerCanRaiseBPExp - self.top.ctrl.io_csr_fsIsOff.value = req.fsIsOff - async def set_ibuffer_ready(self, ready:bool): + def set_ibuffer_ready(self, ready:bool): self.toIbufferAll._toIbuffer._ready.value = ready - - async def get_toibuffer_info(self): + + def get_toibuffer_info(self) -> ToIbufferAllRes: res = ToIbufferAllRes() res.toBackendGpaddrMem.gpaddr = self.toIbufferAll._toBackend_gpaddrMem._wdata._gpaddr.value res.toBackendGpaddrMem.isForVSnonLeafPTE = self.toIbufferAll._toBackend_gpaddrMem._wdata._isForVSnonLeafPTE.value @@ -125,30 +241,33 @@ async def get_toibuffer_info(self): res.toIbuffer.valid = self.toIbufferAll._toIbuffer._valid.value for i in range(16): - res.toIbuffer.pds[i].isRVC = getattr(self.toIbufferAll._toIbuffer._bits._pd, f"_{i}")._isRVC.value - res.toIbuffer.pds[i].brType = getattr(self.toIbufferAll._toIbuffer._bits._pd, f"_{i}")._brType.value - res.toIbuffer.triggereds[i] = getattr(self.toIbufferAll._toIbuffer._bits._triggered, f"_{i}").value - res.toIbuffer.isLastInFtqEntrys[i] = getattr(self.toIbufferAll._toIbuffer._bits._isLastInFtqEntry, f"_{i}").value - res.toIbuffer.exceptionTypes[i] = getattr(self.toIbufferAll._toIbuffer._bits._exceptionType, f"_{i}").value - res.toIbuffer.instrs[i] = getattr(self.toIbufferAll._toIbuffer._bits._instrs, f"_{i}").value - res.toIbuffer.foldpcs[i] = getattr(self.toIbufferAll._toIbuffer._bits._foldpc, f"_{i}").value - res.toIbuffer.illegalInstrs[i] = getattr(self.toIbufferAll._toIbuffer._bits._illegalInstr, f"_{i}").value - res.toIbuffer.crossPageIPFFixs[i] = getattr(self.toIbufferAll._toIbuffer._bits._crossPageIPFFix, f"_{i}").value - res.toIbuffer.ftqOffset[i] = getattr(self.toIbufferAll._toIbuffer._bits._ftqOffset, f"_{i}")._valid.value + res.toIbuffer.pds.isRVCs[i] = self.toIbufferAll._toIbuffer._bits._pd[i]._isRVC.value + res.toIbuffer.pds.brTypes[i] = self.toIbufferAll._toIbuffer._bits._pd[i]._brType.value + res.toIbuffer.pds.isCalls[i] = self.toIbufferAll._toIbuffer._bits._pd[i]._isCall.value + res.toIbuffer.pds.isRets[i] = self.toIbufferAll._toIbuffer._bits._pd[i]._isRet.value + + res.toIbuffer.triggereds[i] = self.toIbufferAll._toIbuffer._bits._triggered[i].value + res.toIbuffer.isLastInFtqEntrys[i] = self.toIbufferAll._toIbuffer._bits._isLastInFtqEntry[i].value + res.toIbuffer.exceptionTypes[i] = self.toIbufferAll._toIbuffer._bits._exceptionType[i].value + res.toIbuffer.instrs[i] = self.toIbufferAll._toIbuffer._bits._instrs[i].value + res.toIbuffer.foldpcs[i] = self.toIbufferAll._toIbuffer._bits._foldpc[i].value + res.toIbuffer.illegalInstrs[i] = self.toIbufferAll._toIbuffer._bits._illegalInstr[i].value + res.toIbuffer.crossPageIPFFixs[i] = self.toIbufferAll._toIbuffer._bits._crossPageIPFFix[i].value + res.toIbuffer.ftqOffset[i] = self.toIbufferAll._toIbuffer._bits._ftqOffset[i].value res.toIbuffer.ftqPtr.flag = self.toIbufferAll._toIbuffer._bits._ftqPtr._flag.value - res.toIbuffer.ftqPtr.flag = self.toIbufferAll._toIbuffer._bits._ftqPtr._value.value + res.toIbuffer.ftqPtr.value = self.toIbufferAll._toIbuffer._bits._ftqPtr._value.value - res.toIbuffer.backendException = self.toIbufferAll._toIbuffer._bits._backendException._0.value + res.toIbuffer.backendException = self.toIbufferAll._toIbuffer._bits._backendException_0.value res.toIbuffer.enqEnable = self.toIbufferAll._toIbuffer._bits._enqEnable.value res.toIbuffer.instr_valids = self.toIbufferAll._toIbuffer._bits._valid.value return res - async def set_itlb_req_ready(self, ready:bool): + def set_itlb_req_ready(self, ready:bool): self.itlbInter._req._ready.value = ready - async def fake_get_itlb_req(self): + def fake_get_itlb_req(self) -> ITLBReq: res = ITLBReq() res.vaddr = self.itlbInter._req._bits_vaddr.value res.valid = self.itlbInter._req._valid.value @@ -157,17 +276,17 @@ async def fake_get_itlb_req(self): async def get_itlb_resp_ready(self): return self.itlbInter._resp._ready.value - async def fake_itlb_resp(self, resp: ITLBResp): + def fake_itlb_resp(self, resp: ITLBResp): self.itlbInter._resp._valid.value = resp.valid - self.itlbInter._resp._bits._paddr.value = resp.paddr - self.itlbInter._resp._bits._pbmt.value = resp.pbmt - self.itlbInter._resp._bits._gpaddr.value = resp.gpaddr + self.itlbInter._resp._bits._paddr_0.value = resp.paddr + self.itlbInter._resp._bits._pbmt_0.value = resp.pbmt + self.itlbInter._resp._bits._gpaddr_0.value = resp.gpaddr self.itlbInter._resp._bits._excp._pf_instr.value = resp.excp.pfInstr self.itlbInter._resp._bits._excp._af_instr.value = resp.excp.afInstr self.itlbInter._resp._bits._excp._gpf_instr.value = resp.excp.gpfInstr self.itlbInter._resp._bits._isForVSnonLeafPTE.value = resp.isForVSnonLeafPTE - async def set_touncache_ready(self, ready:bool): + def set_touncache_ready(self, ready:bool): self.uncacheInter._toUncache._ready.value = ready async def get_to_uncache_req(self): @@ -176,7 +295,10 @@ async def get_to_uncache_req(self): to_uncache.valid = self.uncacheInter._toUncache._valid.value return to_uncache - async def fake_from_uncache(self, from_uncache: FromUncache): + def get_to_uncache_addr(self): + return self.uncacheInter._toUncache._bits_addr.value + + def fake_from_uncache(self, from_uncache: FromUncache): self.uncacheInter._fromUncache._bits_data.value = from_uncache.data self.uncacheInter._fromUncache._valid.value = from_uncache.valid @@ -186,21 +308,332 @@ async def receive_mmio_ftq_ptr(self): res.value = self.top.mmio_needed._mmioCommitRead._mmioFtqPtr._value.value return res - async def set_mmio_commited(self, isLastCommit: bool): + def set_mmio_commited(self, isLastCommit: bool): self.top.mmio_needed._mmioCommitRead._mmioLastCommit.value = isLastCommit - async def receive_pmp_req_addr(self): + def receive_pmp_req_addr(self) -> int: return self.top.mmio_needed._pmp._req_bits_addr.value - async def fake_pmp_resp(self, pmp_resp: PMPResp): + def fake_pmp_resp(self, pmp_resp: PMPResp): self.top.mmio_needed._pmp._resp._instr.value = pmp_resp.instr self.top.mmio_needed._pmp._resp._mmio.value = pmp_resp.mmio # list size is constant: 8 - async def fake_rob_commits(self, rob_commits: list[RobCommit]): + def fake_rob_commits(self, rob_commits: list[RobCommit]): for i in range(8): - robCommitBundle: RobCommitBundle = getattr(self.top.mmio_needed._rob_commits, f"_{i}") + robCommitBundle: RobCommitBundle = self.top.mmio_needed._rob_commits[i] robCommitBundle._valid.value = rob_commits[i].valid robCommitBundle._bits._ftqOffset.value = rob_commits[i].ftqOffset robCommitBundle._bits._ftqIdx._flag.value = rob_commits[i].ftqIdx.flag robCommitBundle._bits._ftqIdx._value.value = rob_commits[i].ftqIdx.value + + def get_extended_instrs(self) -> list[int]: + return [instr.value for instr in self.top.to_ibuffer_all._toIbuffer._bits._instrs] + + def get_cur_last_half_valid(self) -> bool: + return self.top.internal_wires._f3_lastHalf_valid.value != 0 + + async def reset(self): + self.top.ctrl.reset.value = 1 + await self.step() + self.top.ctrl.reset.value = 0 + await self.step() + + @driver_method() + async def deal_with_non_mmio_clusters(self, reqs: ClusteredNonMMIOReqs): + """处理一组非MMIO的数据请求 + + Args: + reqs (ClusteredNonMMIOReqs): 非MMIO数据请求 + + Returns: + 计算结果: (返回值目前用不到) + """ + tasks : list[list] = [] + req_cnt = len(reqs.reqs) + resps = [NonMMIOResp() for _ in range(req_cnt)] + next_appear = 0 + valids_stats = "" + + def get_stage0_inputs_item(ftq_query: FTQQuery, flush_from_bpu: FTQFlushFromBPU, icache_ready:bool): + def stage0_inputs(): + self.query_from_ftq(ftq_query) + self.from_ftq_flush(flush_from_bpu) + self.set_icache_ready(icache_ready) + return stage0_inputs + + def get_stage2_inputs(icache_resp: ICacheResp): + def stage2_inputs(): + self.fake_resp(icache_resp) + return stage2_inputs + + def get_stage3_inputs(fs_is_off: bool): + def stage3_inputs(): + self.set_fs_is_off(fs_is_off) + self.set_ibuffer_ready(True) + return stage3_inputs + + def get_collect_stage0_res(resp: NonMMIOResp): + def collect_stage0_res(): + resp.ftq_ready = self.get_ftq_ready() + resp.bpu_flush_res = self.get_bpu_flush() + return collect_stage0_res + + def get_collect_stage2_res(resp: NonMMIOResp): + def collect_stage2_res(): + if (not resp.ftq_ready) or resp.bpu_flush_res: + # f0 is failing, so ignore + return + resp.cut_instrs = self.get_cut_instrs() + resp.predecode_res = self.get_predecode_res() + resp.cut_ptrs = self.get_cut_ptrs() + + resp.icache_all_valid = self.get_icache_all_resp() + resp.exceptions = self.get_exception() + assert resp.icache_all_valid != 0, f"HISTORY:\n\n{self.replay_stats_stack}\nTASKS: {tasks}\ncluster:{reqs}\nVALIDS:{valids_stats}\ncur_resp:{resp}" + return collect_stage2_res + + def get_check_f2_failed(): + def check_f2_failed(): + all_valid = self.get_icache_all_resp() + # 这里只需要检查即可 + assert all_valid == 0, f"TASKS: {tasks}\ncluster:{reqs}\nVALIDS:{valids_stats}\n" + return check_f2_failed + + def get_collect_stage3_res(resp: NonMMIOResp): + def collect_stage3_res(): + if (not resp.ftq_ready) or resp.bpu_flush_res: + # f0 is failing, so ignore + return + resp.exception_vecs = self.get_exception_vecs() + resp.pcs = self.get_f3_pcs() + resp.addrs = self.get_addrs() + resp.ranges = self.get_ranges() + resp.pred_checker_stg1_res = self.get_pred_checker_stg1_res() + resp.f3_predecode_res = self.get_f3predecoder_res() + + resp.to_ibuffer = self.get_toibuffer_info() + return collect_stage3_res + + def get_collect_stage_wb_res(resp: NonMMIOResp): + def collect_stage_wb_res(): + resp.pred_checker_stg2_res = self.get_pred_checker_stg2_res() + + resp.wb_res = self.collect_res_backto_ftq() + resp.last_half_valid = self.get_cur_last_half_valid() + return collect_stage_wb_res + next_f2_appear = 2 + + # 正常安排 + NON_MMIO_TASK_STATE=0 + + for i in range(req_cnt): + req = reqs.reqs[i] + ref = reqs.resps[i] + resp = resps[i] + cur_appear = next_appear + tasks_limit = next_f2_appear + req.get_icache_block_terms() + 4 + # print(f"task limit: {tasks_limit}") + # 这里+1是考虑到收集wb输出的周期要在输入周期的后一个周期 + if tasks_limit > len(tasks): + # 补足到下一个出现者之前 + for _ in range(len(tasks), tasks_limit): + tasks.append([]) + tasks[cur_appear].append(get_stage0_inputs_item(req.ftq_req, req.bpu_flush_info, req.icache_ready)) + tasks[cur_appear+1].append(get_collect_stage0_res(resp)) + cur_appear = next_f2_appear + # 这里跳过了MMIO阻塞的周期 + for j in range(req.get_icache_block_terms()): + tasks[cur_appear].append(get_stage2_inputs(req.invalid_icache_resps[j])) + cur_appear+=1 + tasks[cur_appear].append(get_check_f2_failed()) + + # set stage 2 input + tasks[cur_appear].append(get_stage2_inputs(req.final_icache_resp)) + cur_appear+=1 + if NON_MMIO_TASK_STATE == 0 and ref.pin_flush: + NON_MMIO_TASK_STATE = 1 + next_appear += 1 + next_f2_appear = cur_appear + elif NON_MMIO_TASK_STATE == 1: + # 这个状态下,前一个请求的pin_flush为真,也就是前一个请求冲刷 + if ref.f3_flush: + # 如果这个请求是个新请求,则在这个请求的“收集结果”阶段的上一个阶段,放入下一个新请求 + # 也就是下个请求的开始属于二类请求 + NON_MMIO_TASK_STATE = 0 + # 当前请求执行collect stage3时,就需要放入下一个请求 + next_appear = next_f2_appear + req.get_icache_block_terms() + 2 + else: + NON_MMIO_TASK_STATE = 0 + next_appear = next_f2_appear + req.get_icache_block_terms() + 3 + next_f2_appear = next_appear + 2 + + else: + NON_MMIO_TASK_STATE = 0 + next_appear += 1 + next_f2_appear = cur_appear + tasks[cur_appear].append(get_collect_stage2_res(resp)) + + tasks[cur_appear].append(get_stage3_inputs(req.fs_is_off)) + cur_appear+=1 + tasks[cur_appear].append(get_collect_stage3_res(resp)) + + tasks[cur_appear+1].append(get_collect_stage_wb_res(resp)) + + # 此时获得的tasks_limit就是整个过程执行的周期数,那么需要step这么多次 + for i in range(tasks_limit): + valids_stats = f"{valids_stats}valids:{self.get_valids()}\n" + for task in tasks[i]: + task() + # if i < next_appear - 1: + await self.step() + if len(self.replay_stats_stack) > 5: + self.replay_stats_stack.pop(0) + self.replay_stats_stack.append(HistoryStat(reqs, resps, tasks, valids_stats)) + assert resps == reqs.resps, f"DUTOUT:\n{resps};\n\n\n\n\n STDOUT:\n{reqs.resps}\n\n\n\n\n HISTORY:\n{self.replay_stats_stack}" + return + + + @driver_method() + async def set_up_before_mmio_states(self, mmio_cycle_info: MMIOCycleInfo): + mmio_data_logger_instance.start_new_reqs(mmio_cycle_info) + ftq_query = FTQQuery() + ftq_query.ftqIdx = mmio_cycle_info.ftq_idx + + ftq_query.ftqOffset.exists = True + ftq_query.ftqOffset.offsetIdx = 0 + ftq_query.startAddr = mmio_cycle_info.ftq_start_addr + ftq_query.nextlineStart = ftq_query.startAddr + 64 + + ftq_query.nextStartAddr = mmio_cycle_info.ftq_start_addr + 28 + + + icache_resp = ICacheStatusResp() + icache_resp.resp.backend_exception = False + icache_resp.resp.double_line = (ftq_query.startAddr & 32) != 0 + icache_resp.resp.pmp_mmios = mmio_cycle_info.icache_pmp_mmios + icache_resp.resp.data = 0x1096_1227_1189_1204_1217_1221_1444 # not important for this + icache_resp.resp.vaddrs[0] = ftq_query.startAddr + icache_resp.resp.vaddrs[1] = ftq_query.nextlineStart + icache_resp.resp.exceptions = mmio_cycle_info.exceptions + icache_resp.resp.paddr = mmio_cycle_info.icache_paddr + icache_resp.resp.gpaddr = 0x1798180418121 # not important for now, but in the future? + icache_resp.resp.icache_valid = True + icache_resp.resp.itlb_pbmts = mmio_cycle_info.icache_itlb_pbmts + + # ftq_flush_info = FTQFlushInfo() + # use fixed value to keep it unused + flush_from_bpu = FTQFlushFromBPU() + safe_flag = mmio_cycle_info.ftq_idx.flag if mmio_cycle_info.ftq_idx.value < 63 else (not mmio_cycle_info.ftq_idx.flag) + safe_value = mmio_cycle_info.ftq_idx.value + 1 if safe_flag == mmio_cycle_info.ftq_idx.flag else 0 + + flush_from_bpu.stgs["s2"].stg_valid = False + flush_from_bpu.stgs["s2"].ftqIdx.flag = safe_flag + flush_from_bpu.stgs["s2"].ftqIdx.value = safe_value + flush_from_bpu.stgs["s3"].stg_valid = False + flush_from_bpu.stgs["s3"].ftqIdx.flag = safe_flag + flush_from_bpu.stgs["s3"].ftqIdx.value = safe_value + + # this data structure has no connection with non-mmio-situation, so it won't be used later + ftq_redirect = FTQRedirect() + ftq_redirect.redirect_level = False + ftq_redirect.ftqIdx.flag = True + ftq_redirect.ftqIdx.value = 12 + ftq_redirect.valid = False + ftq_redirect.ftqOffset = 4 + + # done at stage 0 + self.query_from_ftq(ftq_query) + self.from_ftq_flush(flush_from_bpu) + self.set_icache_ready(True) + # await top_agent.ftq_valid_set(True) + + await self.step() + + await self.step() + + # collect stage2 res + + self.fake_resp_stat(icache_resp) + + await self.step() + self.set_fs_is_off(mmio_cycle_info.csr_fs_is_off) + + await self.step() + self.cur_state = self.top.internal_wires._mmio_state.value + + + @driver_method() + async def cmp_single_mmio_req(self, mmio_req: MMIOReq, ref): + res = await self.deal_with_single_mmio_req(mmio_req) + assert res == ref, f"HISTORY:\n {mmio_data_logger_instance.history_reqs}\n\n\n\n\n\n \ + CUR_CYCLE_INFO:\n{mmio_data_logger_instance.cur_cycle_info}\n\n\n \ + CUR_REQS:\n{mmio_data_logger_instance.req_list}\n \ + CUR_REF_STATE: {ref}\nCUR_DUT_STATE: {res}\n" + return res + + async def deal_with_single_mmio_req(self, mmio_req:MMIOReq): + """push mmio state req + + Args: + mmio_req (MMIOReq): one single mmio req that may cause state transfer + + Returns: + current state and some more infos + """ + mmio_data_logger_instance.add_new_req(mmio_req) + self.set_mmio_commited(mmio_req.last_commited) + + self.set_touncache_ready(mmio_req.to_uncache_ready) + self.fake_from_uncache(mmio_req.from_uncache) + + self.set_itlb_req_ready(mmio_req.itlb_req_ready) + + self.fake_itlb_resp(mmio_req.itlb_resp) + + self.fake_pmp_resp(mmio_req.pmp_resp) + + self.fake_rob_commits(mmio_req.rob_commits) + + self.set_ibuffer_ready(mmio_req.to_ibuffer_ready) + await self.step() + self.last_state = self.cur_state + self.cur_state = self.top.internal_wires._mmio_state.value + if self.last_state == MMIOState.STATE_WAIT_RESP.value and self.cur_state == MMIOState.STATE_SEND_TLB.value: + return [self.cur_state, self.fake_get_itlb_req()] + elif (self.last_state == MMIOState.STATE_TLB_RESP.value or self.last_state == MMIOState.STATE_SEND_PMP.value) \ + and self.cur_state == MMIOState.STATE_WAIT_COMMIT.value: + return [self.cur_state, self.top.to_ibuffer_all._toIbuffer._bits._exceptionType[0].value, \ + self.top.to_ibuffer_all._toBackend_gpaddrMem._wdata._gpaddr.value] + elif (self.last_state == MMIOState.STATE_TLB_RESP.value and self.cur_state == MMIOState.STATE_SEND_PMP.value): + return [self.cur_state, self.receive_pmp_req_addr()] + elif ((self.last_state == MMIOState.STATE_IDLE.value or self.last_state == MMIOState.STATE_WAIT_LAST_CMT.value) \ + and self.cur_state == MMIOState.STATE_SEND_REQ.value) or \ + (self.last_state == MMIOState.STATE_SEND_PMP.value and self.cur_state == MMIOState.STATE_RESEND_REQ.value): + return [self.cur_state, self.get_to_uncache_addr()] + elif (self.last_state == MMIOState.STATE_WAIT_RESP.value or self.last_state == MMIOState.STATE_WAIT_RESEND_RESP.value) \ + and self.cur_state == MMIOState.STATE_WAIT_COMMIT.value: + res = MMIOToIbufferFTQ() + res.expd_instr = self.toIbufferAll._toIbuffer._bits._instrs[0].value + res.ill = self.toIbufferAll._toIbuffer._bits._illegalInstr[0].value + res.is_call = self.toIbufferAll._toIbuffer._bits._pd[0]._isCall.value + res.is_ret = self.toIbufferAll._toIbuffer._bits._pd[0]._isRet.value + res.br_type = self.toIbufferAll._toIbuffer._bits._pd[0]._brType.value + res.is_rvc = self.toIbufferAll._toIbuffer._bits._pd[0]._isRVC.value + return [self.cur_state, res] + elif (self.last_state == MMIOState.STATE_WAIT_COMMIT.value and self.cur_state == MMIOState.STATE_COMMITED.value): + return [self.cur_state, self.top.io_ftqInter._toFtq_pdWb._bits._target.value] + return [self.cur_state] + + @driver_method() + async def reset_mmio_state(self): + """redirect mmio state of dut + """ + ftq_redirect = FTQRedirect() + ftq_redirect.redirect_level = True + ftq_redirect.valid = True + self.ftq_redirect(ftq_redirect) + await self.step() + ftq_redirect.valid = False + self.ftq_redirect(ftq_redirect) + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/bundle/__init__.py b/ut_frontend/ifu/ifu_top/bundle/__init__.py index 4525f0f5..3662c89e 100644 --- a/ut_frontend/ifu/ifu_top/bundle/__init__.py +++ b/ut_frontend/ifu/ifu_top/bundle/__init__.py @@ -1,3 +1,3 @@ from .auto_bundle import IFUTopBundle, MMIONeededBundle, \ -PerformanceBundle, ICacheInterCtrlBundle, FTQInterBundle, InternalFlushesBundle, ToIbufferBundle, \ +PerformanceBundle, ICacheInterCtrlBundle, FTQInterBundle, InternalBundle, ToIbufferBundle, \ CtrlBundle, StageFlushBundle, RobCommitBundle diff --git a/ut_frontend/ifu/ifu_top/bundle/auto_bundle.py b/ut_frontend/ifu/ifu_top/bundle/auto_bundle.py index 5af705a6..d72d1191 100644 --- a/ut_frontend/ifu/ifu_top/bundle/auto_bundle.py +++ b/ut_frontend/ifu/ifu_top/bundle/auto_bundle.py @@ -1,7 +1,11 @@ -from toffee import Bundle, Signals, Signal +from toffee import Bundle, Signals, Signal, SignalList, BundleList +# 这个文件使用了toffee仓库内的自动生成脚本,然后进行了一些简单封装 -class _0Bundle(Bundle): - _1, _3, _2, _0 = Signals(4) +class ValidBundle(Bundle): + _valid = Signal() + +class RVBundle(ValidBundle): + _ready = Signal() class _1Bundle(Bundle): _matchType, _action, _tdata2, _select, _chain = Signals(5) @@ -10,21 +14,19 @@ class _2Bundle(Bundle): _tdata = _1Bundle.from_prefix("_tdata") _addr = Signal() -class _3Bundle(Bundle): +class _3Bundle(ValidBundle): _bits = _2Bundle.from_prefix("_bits") - _valid = Signal() class FrontendTriggerBundle(Bundle): - _tEnableVec = _0Bundle.from_prefix("_tEnableVec") + _tEnableVec = SignalList("_tEnableVec_#", 4) _tUpdate = _3Bundle.from_prefix("_tUpdate") _debugMode, _triggerCanRaiseBpExp = Signals(2) class _5Bundle(Bundle): _flag, _value = Signals(2) -class StageFlushBundle(Bundle): +class StageFlushBundle(ValidBundle): _bits = _5Bundle.from_prefix("_bits") - _valid = Signal() class _7Bundle(Bundle): _s3 = StageFlushBundle.from_prefix("_s3") @@ -34,69 +36,45 @@ class _8Bundle(Bundle): _ftqIdx = _5Bundle.from_prefix("_ftqIdx") _level, _ftqOffset = Signals(2) -class _9Bundle(Bundle): +class _9Bundle(ValidBundle): _bits = _8Bundle.from_prefix("_bits") - _valid = Signal() -class _10Bundle(Bundle): - _bits, _valid = Signals(2) +class _10Bundle(ValidBundle): + _bits = Signal() class _11Bundle(Bundle): _ftqOffset = _10Bundle.from_prefix("_ftqOffset") _ftqIdx = _5Bundle.from_prefix("_ftqIdx") _nextlineStart, _startAddr, _nextStartAddr = Signals(3) -class _12Bundle(Bundle): +class _12Bundle(RVBundle): _bits = _11Bundle.from_prefix("_bits") - _ready, _valid = Signals(2) class _13Bundle(Bundle): _redirect = _9Bundle.from_prefix("_redirect") _flushFromBpu = _7Bundle.from_prefix("_flushFromBpu") _req = _12Bundle.from_prefix("_req") -class _14Bundle(Bundle): - _13, _11, _8, _14, _1, _3, _5, _7, _10, _2, _15, _12, _4, _0, _9, _6 = Signals(16) - -class _15Bundle(Bundle): - _isCall, _brType, _isRet, _isRVC, _valid = Signals(5) - -class _16Bundle(Bundle): - _12 = _15Bundle.from_prefix("_12") - _7 = _15Bundle.from_prefix("_7") - _8 = _15Bundle.from_prefix("_8") - _14 = _15Bundle.from_prefix("_14") - _3 = _15Bundle.from_prefix("_3") - _1 = _15Bundle.from_prefix("_1") - _4 = _15Bundle.from_prefix("_4") - _5 = _15Bundle.from_prefix("_5") - _10 = _15Bundle.from_prefix("_10") - _6 = _15Bundle.from_prefix("_6") - _15 = _15Bundle.from_prefix("_15") - _13 = _15Bundle.from_prefix("_13") - _2 = _15Bundle.from_prefix("_2") - _0 = _15Bundle.from_prefix("_0") - _9 = _15Bundle.from_prefix("_9") - _11 = _15Bundle.from_prefix("_11") +class _15Bundle(ValidBundle): + _isCall, _brType, _isRet, _isRVC = Signals(4) class _17Bundle(Bundle): - _pd = _16Bundle.from_prefix("_pd") + _pd = BundleList(_15Bundle, "_pd_#", 16) _misOffset = _10Bundle.from_prefix("_misOffset") - _pc = _14Bundle.from_prefix("_pc") - _instrRange = _14Bundle.from_prefix("_instrRange") + _pc = SignalList("_pc_#", 16) + _instrRange = SignalList("_instrRange_#", 16) _ftqIdx = _5Bundle.from_prefix("_ftqIdx") _jalTarget, _cfiOffset_valid, _target = Signals(3) -class _18Bundle(Bundle): +class _18Bundle(ValidBundle): _bits = _17Bundle.from_prefix("_bits") - _valid = Signal() class FTQInterBundle(Bundle): _fromFtq = _13Bundle.from_prefix("_fromFtq") _toFtq_pdWb = _18Bundle.from_prefix("_toFtq_pdWb") -class _20Bundle(Bundle): - _bits_vaddr, _valid, _ready = Signals(3) +class _20Bundle(RVBundle): + _bits_vaddr = Signal() class _21Bundle(Bundle): _gpf_instr, _af_instr, _pf_instr = Signals(3) @@ -104,19 +82,15 @@ class _21Bundle(Bundle): # class _22Bundle(Bundle): # _0 = _21Bundle.from_prefix("_0") -class _23Bundle(Bundle): - _0 = Signal() - class _24Bundle(Bundle): - _paddr = _23Bundle.from_prefix("_paddr") - _pbmt = _23Bundle.from_prefix("_pbmt") - _gpaddr = _23Bundle.from_prefix("_gpaddr") + _paddr_0 = Signal() + _pbmt_0 = Signal() + _gpaddr_0 = Signal() _excp = _21Bundle.from_prefix("_excp_0") _isForVSnonLeafPTE = Signal() -class _25Bundle(Bundle): +class _25Bundle(RVBundle): _bits = _24Bundle.from_prefix("_bits") - _valid, _ready = Signals(2) class ITLBInferBundle(Bundle): _req = _20Bundle.from_prefix("_req") @@ -126,27 +100,23 @@ class _27Bundle(Bundle): _1, _0 = Signals(2) class _28Bundle(Bundle): - _itlb_pbmt = _27Bundle.from_prefix("_itlb_pbmt") - _exception = _27Bundle.from_prefix("_exception") - _vaddr = _27Bundle.from_prefix("_vaddr") - _pmp_mmio = _27Bundle.from_prefix("_pmp_mmio") - _paddr = _23Bundle.from_prefix("_paddr") + _itlb_pbmt = SignalList("_itlb_pbmt_#", 2) + _exception = SignalList("_exception_#", 2) + _vaddr = SignalList("_vaddr_#", 2) + _pmp_mmio = SignalList("_pmp_mmio_#", 2) + _paddr_0 = Signal() _isForVSnonLeafPTE, _data, _backendException, _doubleline, _gpaddr = Signals(5) -class _29Bundle(Bundle): +class _29Bundle(ValidBundle): _bits = _28Bundle.from_prefix("_bits") - _valid = Signal() class ICacheInterBundle(Bundle): _resp = _29Bundle.from_prefix("_resp") _icacheReady = Signal() -class _31Bundle(Bundle): - _1 = Signal() - class _32Bundle(Bundle): - _miss = _31Bundle.from_prefix("_miss") - _hit = _31Bundle.from_prefix("_hit") + _miss_1 = Signal() + _hit_1 = Signal() class _33Bundle(Bundle): _0 = _32Bundle.from_prefix("_0") @@ -160,7 +130,7 @@ class _35Bundle(Bundle): class _36Bundle(Bundle): _miss = _33Bundle.from_prefix("_miss") _only = _35Bundle.from_prefix("_only") - _bank_hit = _31Bundle.from_prefix("_bank_hit") + _bank_hit_1 = Signal() _hit = Signal() class _37Bundle(Bundle): @@ -196,19 +166,18 @@ class _42Bundle(Bundle): _ftqIdx = _5Bundle.from_prefix("_ftqIdx") _ftqOffset = Signal() -class RobCommitBundle(Bundle): +class RobCommitBundle(ValidBundle): _bits = _42Bundle.from_prefix("_bits") - _valid = Signal() -class _44Bundle(Bundle): - _7 = RobCommitBundle.from_prefix("_7") - _0 = RobCommitBundle.from_prefix("_0") - _4 = RobCommitBundle.from_prefix("_4") - _1 = RobCommitBundle.from_prefix("_1") - _6 = RobCommitBundle.from_prefix("_6") - _2 = RobCommitBundle.from_prefix("_2") - _5 = RobCommitBundle.from_prefix("_5") - _3 = RobCommitBundle.from_prefix("_3") +# class _44Bundle(Bundle): +# _7 = RobCommitBundle.from_prefix("_7") +# _0 = RobCommitBundle.from_prefix("_0") +# _4 = RobCommitBundle.from_prefix("_4") +# _1 = RobCommitBundle.from_prefix("_1") +# _6 = RobCommitBundle.from_prefix("_6") +# _2 = RobCommitBundle.from_prefix("_2") +# _5 = RobCommitBundle.from_prefix("_5") +# _3 = RobCommitBundle.from_prefix("_3") class _45Bundle(Bundle): _gpaddr, _isForVSnonLeafPTE = Signals(2) @@ -217,103 +186,51 @@ class _46Bundle(Bundle): _wdata = _45Bundle.from_prefix("_wdata") _waddr, _wen = Signals(2) -class _47Bundle(Bundle): - _valid = Signal() -class _48Bundle(Bundle): - _13 = _47Bundle.from_prefix("_13") - _3 = _47Bundle.from_prefix("_3") - _7 = _47Bundle.from_prefix("_7") - _4 = _47Bundle.from_prefix("_4") - _10 = _47Bundle.from_prefix("_10") - _15 = _47Bundle.from_prefix("_15") - _5 = _47Bundle.from_prefix("_5") - _8 = _47Bundle.from_prefix("_8") - _9 = _47Bundle.from_prefix("_9") - _12 = _47Bundle.from_prefix("_12") - _1 = _47Bundle.from_prefix("_1") - _6 = _47Bundle.from_prefix("_6") - _2 = _47Bundle.from_prefix("_2") - _11 = _47Bundle.from_prefix("_11") - _14 = _47Bundle.from_prefix("_14") - _0 = _47Bundle.from_prefix("_0") class _49Bundle(Bundle): - _isRVC, _brType = Signals(2) - -class _50Bundle(Bundle): - _15 = _49Bundle.from_prefix("_15") - _9 = _49Bundle.from_prefix("_9") - _11 = _49Bundle.from_prefix("_11") - _0 = _49Bundle.from_prefix("_0") - _13 = _49Bundle.from_prefix("_13") - _12 = _49Bundle.from_prefix("_12") - _2 = _49Bundle.from_prefix("_2") - _10 = _49Bundle.from_prefix("_10") - _14 = _49Bundle.from_prefix("_14") - _4 = _49Bundle.from_prefix("_4") - _8 = _49Bundle.from_prefix("_8") - _6 = _49Bundle.from_prefix("_6") - _3 = _49Bundle.from_prefix("_3") - _1 = _49Bundle.from_prefix("_1") - _7 = _49Bundle.from_prefix("_7") - _5 = _49Bundle.from_prefix("_5") - -class _51Bundle(Bundle): - _pd = _50Bundle.from_prefix("_pd") + _isRVC, _brType, _isCall, _isRet = Signals(4) + +class _51Bundle(ValidBundle): + _pd = BundleList(_49Bundle, "_pd_#", 16) + _valids = SignalList("_pd_#_valid", 15, lambda x: str(x+1)) _ftqPtr = _5Bundle.from_prefix("_ftqPtr") - _triggered = _14Bundle.from_prefix("_triggered") - _isLastInFtqEntry = _14Bundle.from_prefix("_isLastInFtqEntry") - _exceptionType = _14Bundle.from_prefix("_exceptionType") - _instrs = _14Bundle.from_prefix("_instrs") - _foldpc = _14Bundle.from_prefix("_foldpc") - _illegalInstr = _14Bundle.from_prefix("_illegalInstr") - _crossPageIPFFix = _14Bundle.from_prefix("_crossPageIPFFix") - _ftqOffset = _48Bundle.from_prefix("_ftqOffset") - _backendException = _23Bundle.from_prefix("_backendException") - _enqEnable, _valid = Signals(2) - -class _52Bundle(Bundle): + _triggered = SignalList("_triggered_#", 16) + _isLastInFtqEntry = SignalList("_isLastInFtqEntry_#", 16) + _exceptionType = SignalList("_exceptionType_#", 16) + _instrs = SignalList("_instrs_#", 16) + _foldpc = SignalList("_foldpc_#", 16) + _illegalInstr = SignalList("_illegalInstr_#", 16) + _crossPageIPFFix = SignalList("_crossPageIPFFix_#", 16) + _ftqOffset = SignalList("_ftqOffset_#_valid", 16) + _backendException_0 = Signal() + _enqEnable = Signal() + +class _52Bundle(RVBundle): _bits = _51Bundle.from_prefix("_bits") - _valid, _ready = Signals(2) -class _53Bundle(Bundle): - _bits_data, _valid = Signals(2) +class _53Bundle(ValidBundle): + _bits_data = Signal() -class _54Bundle(Bundle): - _bits_addr, _valid, _ready = Signals(3) +class _54Bundle(RVBundle): + _bits_addr = Signal() class UncacheInterBundle(Bundle): _toUncache = _54Bundle.from_prefix("_toUncache") _fromUncache = _53Bundle.from_prefix("_fromUncache") -# class _56Bundle(Bundle): -# _icacheInter = ICacheInterBundle.from_prefix("_icacheInter") -# _toIbuffer = _52Bundle.from_prefix("_toIbuffer") -# _perf = _39Bundle.from_prefix("_perf") -# _ftqInter = FTQInterBundle.from_prefix("_ftqInter") -# _rob_commits = _44Bundle.from_prefix("_rob_commits") -# _toBackend_gpaddrMem = _46Bundle.from_prefix("_toBackend_gpaddrMem") -# _frontendTrigger = FrontendTriggerBundle.from_prefix("_frontendTrigger") -# _mmioCommitRead = _37Bundle.from_prefix("_mmioCommitRead") -# _uncacheInter = UncacheInterBundle.from_prefix("_uncacheInter") -# _pmp = PMPBundle.from_prefix("_pmp") -# _iTLBInter = ITLBInferBundle.from_prefix("_iTLBInter") -# _icachePerfInfo = _36Bundle.from_prefix("_icachePerfInfo") -# _icacheStop, _csr_fsIsOff = Signals(2) - class ICacheInterCtrlBundle(Bundle): _icacheStop = Signal() _icacheInter = ICacheInterBundle.from_prefix("_icacheInter") class PerformanceBundle(Bundle): _icachePerfInfo = _36Bundle.from_prefix("_icachePerfInfo") - _perf = _39Bundle.from_prefix("_perf") + _perf = BundleList(_38Bundle, "_perf_#", 13) class MMIONeededBundle(Bundle): _iTLBInter = ITLBInferBundle.from_prefix("_iTLBInter") # done _uncacheInter = UncacheInterBundle.from_prefix("_uncacheInter") # done - _rob_commits = _44Bundle.from_prefix("_rob_commits") + _rob_commits = BundleList(RobCommitBundle, "_rob_commits_#", 8) _mmioCommitRead = _37Bundle.from_prefix("_mmioCommitRead") # done _pmp = PMPBundle.from_prefix("_pmp") # done @@ -325,16 +242,60 @@ class CtrlBundle(Bundle): reset, clock = Signals(2) io_csr_fsIsOff = Signal() -class InternalFlushesBundle(Bundle): +class PredecodeBundle(Bundle): + in_bits_data = SignalList("_io_in_bits_data_#", 17) + out_pd_valids = SignalList("_io_out_pd_#_valid", 15, lambda x: str(x+1)) + out_pd_isRVCs = SignalList("_io_out_pd_#_isRVC", 16) + out_hasHalfValids = SignalList("_io_out_hasHalfValid_#", 14, lambda x: str(x+2)) + out_jumpOffsets = SignalList("_io_out_jumpOffset_#", 16) + out_instrs = SignalList("_io_out_instr_#", 16) + +class F3PredecoderBundle(Bundle): + out_pd_brTypes = SignalList("_io_out_pd_#_brType", 16) + out_pd_isCalls = SignalList("_io_out_pd_#_isCall", 16) + out_pd_isRets = SignalList("_io_out_pd_#_isRet", 16) + +class PredCheckerBundle(Bundle): + instr_valids = SignalList("_io_in_instrValid_#", 16) + fixed_ranges = SignalList("_io_out_stage1Out_fixedRange_#", 16) + fixed_takens = SignalList("_io_out_stage1Out_fixedTaken_#", 16) + fixed_targets = SignalList("_io_out_stage2Out_fixedTarget_#", 16) + jal_targets = SignalList("_io_out_stage2Out_jalTarget_#", 16) + fault_types = SignalList("_io_out_stage2Out_faultType_#_value", 16) + +class InternalBundle(Bundle): _f2_flush, _f3_flush = Signals(2) + _f1_fire, _f1_valid = Signals(2) + _f0_flush_from_bpu_probe = Signal() + _f1_ready = Signal() + _f2_ready = Signal() + _f2_fire = Signal() + _f0_fire, _wb_enable = Signals(2) + _f2_valid = Signal() + _f3_valid = Signal() + _f3_ready = Signal() + _icacheRespAllValid = Signal() + f3_exception_vec = SignalList("_f3_exception_vec_#", 16) + f2_exceptions = SignalList("_f2_exception_#", 2) + f3_pcs = SignalList("_f3_pc_#", 16) + f2_cut_ptrs = SignalList("_f2_cut_ptr_#", 17) + _f3_paddrs_0, _f3_gpaddr = Signals(2) + _f3_lastHalf_valid = Signal() + + _f3_instr_range = Signal() + pre_decoder = PredecodeBundle.from_prefix("_preDecoder") + pred_checker = PredCheckerBundle.from_prefix("_predChecker") + f3_predecoder = F3PredecoderBundle.from_prefix("_f3Predecoder") + + _mmio_state = Signal() + _is_first_instr = Signal() + class IFUTopBundle(Bundle): - # io = _56Bundle.from_prefix("io") ctrl = CtrlBundle.from_prefix("") io_ftqInter = FTQInterBundle.from_prefix("io_ftqInter") #done _icacheInterCtrl = ICacheInterCtrlBundle.from_prefix("io") #done to_ibuffer_all = ToIbufferBundle.from_prefix("io") # done mmio_needed = MMIONeededBundle.from_prefix("io") - performance = PerformanceBundle.from_prefix("io") # not need io_frontendTrigger = FrontendTriggerBundle.from_prefix("io_frontendTrigger") # done - internal_flushes = InternalFlushesBundle.from_prefix("NewIFU") # done \ No newline at end of file + internal_wires = InternalBundle.from_prefix("NewIFU") # done \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/commons.py b/ut_frontend/ifu/ifu_top/commons.py new file mode 100644 index 00000000..11b38110 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/commons.py @@ -0,0 +1,37 @@ +PREDICT_WIDTH = 16 +# BLOCK_BYTES=64 +BLOCK_OFF_BITS=6 +PRED_ERR = 1 +LAST_HALF_ERR = 2 +from random import randint + +def randbool(rate=50): + return randint(0, 99) < rate + +def is_next_line(addr1, addr2): + return bool(((addr1 >> BLOCK_OFF_BITS) & 1) ^ ((addr2 >> BLOCK_OFF_BITS) & 1)) + +def is_last_in_line(pc): + return (pc & 0x3F) == 0b111110 + +def calc_double_line(start_addr): + return (start_addr >> (BLOCK_OFF_BITS -1 )) & 1 + +def calc_blk_length(start, next_start): + block_length = (next_start - start) // 2 + return max(0, min(block_length, PREDICT_WIDTH)) + +def calc_cut_ptr(startAddr): + idx_pos = (startAddr >> 1) & 0x1F + cuts = [idx_pos + i for i in range(PREDICT_WIDTH + 1)] + return cuts + +def check_icache_resp_all_valid(icache_resp_valid, vaddrs, icache_doubleline, req_doubleline, start, next_start): + if not icache_resp_valid: + return False + if vaddrs[0] != start: + return False + + if not req_doubleline: + return True + return icache_resp_valid and (icache_doubleline and vaddrs[1] == next_start) \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/datadef/__init__.py b/ut_frontend/ifu/ifu_top/datadef/__init__.py index f67032b7..06378dc3 100644 --- a/ut_frontend/ifu/ifu_top/datadef/__init__.py +++ b/ut_frontend/ifu/ifu_top/datadef/__init__.py @@ -1,5 +1,9 @@ -from .ftq_datadef import FTQQuery, FTQFlushInfo, FTQResp, FTQIdx -from .icache_datadef import ICacheStatusResp +from .ftq_datadef import FTQQuery, FTQFlushInfo, FTQResp, FTQIdx, FTQFlushFromBPU, FTQFlushFromBPUStg, ExistsIdx, FTQRedirect +from .icache_datadef import ICacheStatusResp, ICacheResp from .frontend_trigger_datadef import FrontendTriggerReq from .ibuffer_datadef import ToIbufferAllRes -from .mmio_related import ITLBReq, ITLBResp, ToUncache, FromUncache, PMPResp, RobCommit \ No newline at end of file +from .mmio_related import ITLBReq, ITLBResp, ToUncache, FromUncache, PMPResp, \ + RobCommit, MMIOReq, ExceptionType, MMIOToIbufferFTQ, MMIOCycleInfo, MMIOState, PbmtAssist, \ + mmio_data_logger_instance, MMIOStateDataLogger +from .sub_modules_def import PreDecodeDataDef, F3PreDecodeData, PredCheckerRetData, PredCheckerStage1RetData, PredCheckerStage2RetData +from .req_datadef import NonMMIOSingleReq, NonMMIOResp, non_mmio_logger_instance, NonMMIOReqLogger, ClusteredNonMMIOReqs \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/datadef/frontend_trigger_datadef.py b/ut_frontend/ifu/ifu_top/datadef/frontend_trigger_datadef.py index df06782d..da39a9f6 100644 --- a/ut_frontend/ifu/ifu_top/datadef/frontend_trigger_datadef.py +++ b/ut_frontend/ifu/ifu_top/datadef/frontend_trigger_datadef.py @@ -1,18 +1,19 @@ class BreakpointSetter(): - matchType = 0 - action = 0 - tdata2 = 0 - select = False - chain = False - addr = 0 - valid = False + def __init__(self): + self.matchType = 0 + self.action = 0 + self.tdata2 = 0 + self.select = False + self.chain = False + self.addr = 0 + self.valid = False class FrontendTriggerReq(): - bpSetter = BreakpointSetter() - tEnableVec = [False, False, False, False] - debugMode = False - triggerCanRaiseBPExp = False - fsIsOff = False + def __init__(self): + self.bpSetter = BreakpointSetter() + self.tEnableVec = [False, False, False, False] + self.debugMode = False + self.triggerCanRaiseBPExp = False diff --git a/ut_frontend/ifu/ifu_top/datadef/ftq_datadef.py b/ut_frontend/ifu/ifu_top/datadef/ftq_datadef.py index 9545b2f7..61cc4577 100644 --- a/ut_frontend/ifu/ifu_top/datadef/ftq_datadef.py +++ b/ut_frontend/ifu/ifu_top/datadef/ftq_datadef.py @@ -1,9 +1,12 @@ from toffee import Agent -from ..bundle import FTQInterBundle, InternalFlushesBundle, StageFlushBundle +from ..bundle import FTQInterBundle, InternalBundle, StageFlushBundle +from ..commons import randbool +from random import randint -class FTQIdx(object): - flag = False - value = 0 # different flags with greater value will also be flushed +class FTQIdx: + def __init__(self): + self.flag = False + self.value = 0 # different flags with greater value will also be flushed def __eq__(self, value): if type(value) != FTQIdx: @@ -11,58 +14,204 @@ def __eq__(self, value): return self.flag == value.flag and self.value == value.value def __lt__(self, value): - - return self.flag ^ value.flag ^ self.value < value.value + + return self.flag ^ value.flag ^ (self.value < value.value) def __gt__(self, value): - return self.flag ^ value.flag ^ self.value < value.value + return self.flag ^ value.flag ^ (self.value > value.value) + + def __str__(self): + return f"[flag: {self.flag}; value: {self.value}]\n" + + def __repr__(self): + return f"[flag: {self.flag}; value: {self.value}]\n" + + def randomize(self): + self.flag = randbool() + self.value = randint(0, 63) + + def inc(self): + self.value += 1 + if self.value > 63: + self.flag = not self.flag + self.value = 0 class ExistsIdx(object): - offsetIdx = 0 - exists = False + def __init__(self): + self.offsetIdx = 0 + self.exists = False + + def __str__(self): + return f"exist: {self.exists}\noffIdx: {self.offsetIdx}\n" + + def __eq__(self, value): + if type(value) != ExistsIdx: + return False + + if self.exists != value.exists: + return False + + if not self.exists: + return True + + return self.offsetIdx == value.offsetIdx + + def randomize(self): + self.exists = randbool(rate=40) + self.offsetIdx = randint(0, 15) class FTQQuery(): - ftqIdx: FTQIdx = FTQIdx() # position of the prediction block in the ftq, - ftqOffset: ExistsIdx = ExistsIdx() # if jump instruction exists and where is it - nextlineStart = 0 # addr of the next cacheline - startAddr = 0 # addr of the instruction block - nextStartAddr = 0 # addr of the next prediction block - -class toIbufferPd(): - isCall = False - isRet = False - isRVC = False - pdValid = True # if the predecode info is valid - brType = 0 # 0 for non cfi instruction + def __init__(self): + self.valid = True # 同拍子 + self.ftqIdx: FTQIdx = FTQIdx() # position of the prediction block in the ftq, + self.ftqOffset: ExistsIdx = ExistsIdx() # if jump instruction exists and where is it + self.nextlineStart = 0 # addr of the next cacheline + self.startAddr = 0 # addr of the instruction block + self.nextStartAddr = 0 # addr of the next prediction block + + def __str__(self): + return ( + f"{FTQQuery.__name__}(\n" + f" valid={self.valid},\n" + f" ftqIdx={self.ftqIdx},\n" + f" ftqOffset={self.ftqOffset},\n" + f" nextlineStart={self.nextlineStart},\n" + f" startAddr={self.startAddr},\n" + f" nextStartAddr={self.nextStartAddr}\n" + f")" + ) + + def set_start_addr(self, start_addr): + self.startAddr = start_addr + self.nextlineStart = (self.startAddr + 64) & ((1 << 50)-1) + + def randomize(self, ftq_idx=None): + if ftq_idx == None: + self.ftqIdx.randomize() + else: + self.ftqIdx = ftq_idx + self.ftqOffset.randomize() + self.set_start_addr((randint(0, (1 << 49) - 1 ) << 1) & ((1 << 50)-1)) + + self.nextStartAddr = (randint(0, (1 << 49) - 1 ) << 1) & ((1 << 50)-1) if self.ftqOffset.exists \ + else (self.startAddr + randint(1, 16) * 2) & ((1 << 50)-1) + + +class ResPd(): + def __init__(self): + + self.isCalls = [False for i in range(16)] + self.isRets = [False for i in range(16)] + self.isRVCs = [False for i in range(16)] + self.pdValids = [True for i in range(16)] # if the instr is valid in the block + self.brTypes = [0 for i in range(16)] # 0 for non cfi + + def __str__(self): + return f"calls: {self.isCalls}\nrets: {self.isRets}\nrvcs: {self.isRVCs}\nvalids: {self.pdValids}\nbrTypes: {self.brTypes}" + + def __eq__(self, value): + if type(value) != ResPd: + return False + return self.isCalls == value.isCalls and self.isRets == value.isRets and self.isRVCs == value.isRVCs and self.pdValids == value.pdValids and self.brTypes == value.brTypes class FTQResp(): - valid = True # indicate whether response is usable - pds = [toIbufferPd() for i in range(16)] # indicate the predecode info for 16 concated instructions - misOffset = ExistsIdx() # indicates if a mis branch predicate instr exists and which is it - pcs = [0 for i in range(16)] # pcs for 16 concated instructions - instrRanges = [True for i in range(16)] # indicate whether each instruction is in the prediction block - ftqIdx: FTQIdx = FTQIdx() # the index of the prediction block in ftq - jalTarget = 0 - target = 0 # jump target of the first jump instruction; next pc of the pc of last valid instruction in the prediction block - cfiOffset_valid = False # if there exists cfi instruction in the prediction block + def __init__(self): + self.valid = True # indicate whether response is usable + self.pds = ResPd() # indicate the predecode info for 16 concated instructions + self.misOffset = ExistsIdx() # indicates if a mis branch predicate instr exists and which is it + self.pcs = [0 for i in range(16)] # pcs for 16 concated instructions + self.instrRanges = [True for i in range(16)] # indicate whether each instruction is in the prediction block + self.ftqIdx: FTQIdx = FTQIdx() # the index of the prediction block in ftq + self.jalTarget = 0 + self.target = 0 # jump target of the first jump instruction; next pc of the pc of last valid instruction in the prediction block + self.cfiOffset_valid = False # if there exists cfi instruction in the prediction block + + def __str__(self): + return f"valid: {self.valid}\npds: {self.pds}\nmisOffset:\n{self.misOffset}\npcs: {self.pcs}\ninstrRanges: {self.instrRanges}\nftqIdx:{self.ftqIdx}\njalTarget: {self.jalTarget}\ntarget: {self.target}\ncfioffset_valid: {self.cfiOffset_valid}" + + def __eq__(self, value): + if not isinstance(value, FTQResp): + print(f"Type mismatch: {type(value)} != FTQResp") + return False + + checks = [ + ("valid", self.valid, value.valid), + ("pds", self.pds, value.pds), + ("misOffset", self.misOffset, value.misOffset), + ("pcs", self.pcs, value.pcs), + ("instrRanges", self.instrRanges, value.instrRanges), + ("ftqIdx", self.ftqIdx, value.ftqIdx), + ("jalTarget", self.jalTarget, value.jalTarget), + ("target", self.target, value.target), + ("cfiOffset_valid", self.cfiOffset_valid, value.cfiOffset_valid), + ] + + for name, a, b in checks: + if a != b: + print(f"Mismatch in {name}: {a!r} != {b!r}") + return False + + return True class FTQRedirect(): - ftqIdx: FTQIdx = FTQIdx() - valid = True - redirect_level = False - ftqOffset = 0 + def __init__(self): + + self.ftqIdx: FTQIdx = FTQIdx() + self.valid = False + self.redirect_level = False + self.ftqOffset = 0 class FTQFlushFromBPUStg(): - stg_valid = True - ftqIdx: FTQIdx = FTQIdx() + def __init__(self): + self.stg_valid = False + self.ftqIdx: FTQIdx = FTQIdx() + + def randomize(self, idx: FTQIdx): + if_flush = randbool(rate=25) + if if_flush: + self.stg_valid = True + reverse_flag = False if idx.value == 63 else True if idx.value == 0 \ + else randbool() + self.ftqIdx.flag = not idx.flag if reverse_flag else idx.flag + self.ftqIdx.value = randint(idx.value + 1, 63) if reverse_flag else randint(0, idx.value) + + def __str__(self): + return ( + f"{self.__class__.__name__}(\n" + f" stg_valid={self.stg_valid},\n" + f" ftqIdx={self.ftqIdx}\n" + f")" + ) class FTQFlushFromBPU(): - stgs = {"s2": FTQFlushFromBPUStg(), "s3":FTQFlushFromBPUStg()} + def __init__(self): + self.stgs : dict[str, FTQFlushFromBPUStg]= {"s2": FTQFlushFromBPUStg(), "s3":FTQFlushFromBPUStg()} + + def randomize(self, idx: FTQIdx): + self.stgs["s2"].randomize(idx) + self.stgs["s3"].randomize(idx) + # self.stgs["s3"].stg_valid = False + + def clear_init(self): + self.stgs["s2"].stg_valid = False + self.stgs["s3"].stg_valid = False + + def __str__(self): + stgs_str = ",\n".join([f" {k}={v}" for k, v in self.stgs.items()]) + return ( + f"{self.__class__.__name__}(\n" + f"{stgs_str}\n" + f")" + ) + + class InternalFlushInfos(): - internal_flushes = [False for i in range(4)] + def __init__(self): + self.internal_flushes = [False for i in range(4)] class FTQFlushInfo(): - flush_from_bpu = FTQFlushFromBPU() - redirect = FTQRedirect() + def __init__(self): + self.flush_from_bpu = FTQFlushFromBPU() + self.redirect = FTQRedirect() diff --git a/ut_frontend/ifu/ifu_top/datadef/ibuffer_datadef.py b/ut_frontend/ifu/ifu_top/datadef/ibuffer_datadef.py index 2454d301..3180c67c 100644 --- a/ut_frontend/ifu/ifu_top/datadef/ibuffer_datadef.py +++ b/ut_frontend/ifu/ifu_top/datadef/ibuffer_datadef.py @@ -1,32 +1,118 @@ -from .ftq_datadef import FTQIdx +from .ftq_datadef import FTQIdx, ResPd +class ToIbuffer(): + def __init__(self): + self.valid = False + self.pds = ResPd() + self.ftqPtr = FTQIdx() + self.triggereds = [0 for i in range(16)] + self.isLastInFtqEntrys = [False for i in range(16)] + self.exceptionTypes = [0 for i in range(16)] + self.instrs = [0 for i in range(16)] + self.foldpcs = [0 for i in range(16)] + self.illegalInstrs = [False for i in range(16)] + self.crossPageIPFFixs = [False for i in range(16)] + self.ftqOffset = [False for i in range(16)] + self.backendException = False + self.enqEnable = 0 + self.instr_valids = 0 -class PDInfo(): - isRVC = False - brType = 0 + def __str__(self): + return ( + "ToIbuffer(\n" + f" valid={self.valid},\n" + f" pds={self.pds},\n" + f" ftqPtr={self.ftqPtr},\n" + f" instrs={self.instrs},\n" + f" foldpcs={self.foldpcs},\n" + f" backendException={self.backendException},\n" + f" enqEnable={self.enqEnable},\n" + f" instr_valids={self.instr_valids},\n" + f" exceptionTypes={self.exceptionTypes}\n" + f" illegals={self.illegalInstrs}\n" + ")" + ) -class ToIbuffer(): - valid = False - pds = [PDInfo() for i in range(16)] - ftqPtr = FTQIdx() - triggereds = [0 for i in range(16)] - isLastInFtqEntrys = [False for i in range(16)] - exceptionTypes = [0 for i in range(16)] - instrs = [0 for i in range(16)] - foldpcs = [0 for i in range(16)] - illegalInstrs = [False for i in range(16)] - crossPageIPFFixs = [False for i in range(16)] - ftqOffset = [False for i in range(16)] - backendException = False - enqEnable = 0 - instr_valids = 0 + + def __eq__(self, other): + if not isinstance(other, ToIbuffer): + print(f"[≠] type mismatch: self is ToIbuffer, other is {type(other)}") + return False + + diffs = [] + + # --- 基本数组长度一致性检查 --- + def _len(name): + return len(getattr(self, name)), len(getattr(other, name)) + + for arr in ["instrs", "illegalInstrs", "exceptionTypes"]: + ls, lo = _len(arr) + if ls != lo: + diffs.append(f"Mismatch in length: {arr}: {ls} != {lo}") + + # 若长度不等,先按最短长度比对,避免 IndexError + n_instrs = min(len(self.instrs), len(other.instrs)) + n_illegal = min(len(self.illegalInstrs), len(other.illegalInstrs)) + + # --- 指令逐项比对(跳过 illegal==2 的槽位)--- + for i in range(min(n_instrs, n_illegal)): + il1 = self.illegalInstrs[i] + il2 = other.illegalInstrs[i] + if il1 == 2 or il2 == 2: + continue + if il1 != il2: + diffs.append(f"illegalInstrs[{i}]: {il1} != {il2}") + v1, v2 = self.instrs[i], other.instrs[i] + if v1 != v2: + # 以 16 进制展示更直观(按需可改) + diffs.append(f"instrs[{i}]: {v1:#06x} != {v2:#06x}") + + # --- 逐字段比对 --- + scalar_fields = [ + "valid", "pds", "ftqPtr", "foldpcs", + "backendException", "enqEnable", "instr_valids", "exceptionTypes","instrValids" + ] + for name in scalar_fields: + a = getattr(self, name, None) + b = getattr(other, name, None) + if a != b: + diffs.append(f"{name}: {a} != {b}") + + if diffs: + print("[ToIbuffer diff]") + for d in diffs: + print(" -", d) + return False + + return True class ToBackendGpaddrMem(): - waddr = 0 - wen = False - gpaddr = 0 - isForVSnonLeafPTE = False + def __init__(self): + self.waddr = 0 + self.wen = False + self.gpaddr = 0 + self.isForVSnonLeafPTE = False + + def __eq__(self, value): + if type(value) != ToBackendGpaddrMem: + return False + if self.wen != value.wen: + return False + if not self.wen: + return True + return self.gpaddr == value.gpaddr and self.waddr == value.waddr class ToIbufferAllRes(): - toBackendGpaddrMem = ToBackendGpaddrMem() - toIbuffer = ToIbuffer() \ No newline at end of file + def __init__(self): + self.toBackendGpaddrMem = ToBackendGpaddrMem() + self.toIbuffer = ToIbuffer() + + def __eq__(self, value): + if type(value) != ToIbufferAllRes: + return False + + # TODO: add comparation of toBackendGpaddrMem + return self.toIbuffer == value.toIbuffer + + def __str__(self): + return f"toIbuffer: {self.toIbuffer}" \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/datadef/icache_datadef.py b/ut_frontend/ifu/ifu_top/datadef/icache_datadef.py index 51e29aad..8855dd10 100644 --- a/ut_frontend/ifu/ifu_top/datadef/icache_datadef.py +++ b/ut_frontend/ifu/ifu_top/datadef/icache_datadef.py @@ -1,20 +1,132 @@ from toffee import Agent from ..bundle import ICacheInterCtrlBundle +from ..commons import randbool, calc_double_line, PREDICT_WIDTH, calc_cut_ptr, calc_blk_length +from random import randint +from .ftq_datadef import FTQQuery +from ..instr_utils import construct_instrs_with_jump_idx, rebuild_cacheline_from_parts class ICacheResp(): - itlb_pbmts = [0, 0] - exceptions = [0, 0] - vaddrs = [0, 0] - pmp_mmios = [False, False] - paddr = 0 - VS_non_leaf_PTE = False - data = 0 - backend_exception = False - double_line = False - gpaddr = 0 - icache_valid = True + def __init__(self, + itlb_pbmts=None, + pmp_mmios=None, + exceptions=None, + vaddrs=None, + paddr=0, + VS_non_leaf_PTE=True, + data=0, + backend_exception=False, + double_line=False, + gpaddr=0, + icache_valid=True, + ftq_req: FTQQuery = None, + init_as_valid= False + ): + self.itlb_pbmts = itlb_pbmts if itlb_pbmts is not None else [0, 0] + self.pmp_mmios = pmp_mmios if pmp_mmios is not None else [False, False] + self.exceptions = exceptions if exceptions is not None else [0, 0] + self.vaddrs = vaddrs if vaddrs is not None else [0, 0] + + self.paddr = paddr + self.VS_non_leaf_PTE = VS_non_leaf_PTE + self.data = data + self.backend_exception = backend_exception + self.double_line = double_line + self.gpaddr = gpaddr + self.icache_valid = icache_valid + if init_as_valid and ftq_req is not None: + self.init_as_valid(ftq_req) + + def __str__(self): + return ( + f"{self.__class__.__name__}(\n" + f" itlb_pbmts={self.itlb_pbmts},\n" + f" pmp_mmios={self.pmp_mmios},\n" + f" exceptions={self.exceptions},\n" + f" vaddrs={self.vaddrs},\n" + f" paddr={self.paddr},\n" + f" VS_non_leaf_PTE={self.VS_non_leaf_PTE},\n" + f" data={self.data},\n" + f" backend_exception={self.backend_exception},\n" + f" double_line={self.double_line},\n" + f" gpaddr={self.gpaddr},\n" + f" icache_valid={self.icache_valid}\n" + f")" + ) + + def init_as_valid(self, ftq_req: FTQQuery): + self.icache_valid = True + self.vaddrs[0] = ftq_req.startAddr + self.vaddrs[1] = ftq_req.nextlineStart + self.double_line = calc_double_line(ftq_req.startAddr) + + def randomize(self, ftq_req: FTQQuery, valid=True, last_finished=True): + # itlb pbmt and pmp_mmio won't be changed + self.exceptions = [0 if randbool(rate=80) else randint(1, 3) for _ in range(2)] + self.init_as_valid(ftq_req) + self.paddr = 0x18151192 # not useful when encountering non mmioX + self.gpaddr = randint(0, (1 << 56) -1) + self.backend_exception = randbool(rate=70) + self.data = 0 + + if not valid: + cond = randint(1, 15) + if (cond & 1) > 0: + self.icache_valid = False + + if (cond & 2) > 0: + self.vaddrs[0] = ftq_req.startAddr+32 + + if (cond & 4) > 0: + self.vaddrs[1] = ftq_req.nextlineStart+32 + + if (cond & 8) > 0: + self.double_line = not self.double_line + return + + # 有效数据才需要计算 + data_construction = randint(0, 99) + end_idx = ftq_req.ftqOffset.offsetIdx if ftq_req.ftqOffset.exists else PREDICT_WIDTH - 1 + real_jump_type = randint(1, 3) if ftq_req.ftqOffset.exists else 0 + if data_construction < 40: + # 无预测错误,也即ret,jalr,jal错误不存在,也不存在target, valid, non cfi问题 + instrs = construct_instrs_with_jump_idx(end_idx,cfi_res=real_jump_type, imm=ftq_req.nextStartAddr, last_finished=last_finished) + elif data_construction < 50: + # in this case, construct random of jal err + # by constructing jump instr before the real jump idx + instrs = construct_instrs_with_jump_idx(randint(0, max(0, end_idx-1)), cfi_res=2,last_finished=last_finished) + elif data_construction < 60: + # ret err + instrs = construct_instrs_with_jump_idx(randint(0, max(0, end_idx-1)), cfi_res=4,last_finished=last_finished) + elif data_construction < 70: + # jalr err + instrs = construct_instrs_with_jump_idx(randint(0, max(0, end_idx-1)), cfi_res=3,last_finished=last_finished) + elif data_construction < 80: + # invalid prediction + instrs = construct_instrs_with_jump_idx(randint(min(PREDICT_WIDTH - 1, end_idx+1), PREDICT_WIDTH-1), last_finished=last_finished) + elif data_construction < 90: + instrs = construct_instrs_with_jump_idx(end_idx, invalid_jmp_prediction=True, last_finished=last_finished) + else: + # this is a different target err producer + instrs = construct_instrs_with_jump_idx(end_idx, imm=ftq_req.nextStartAddr + 40,last_finished=last_finished) + + self.data = rebuild_cacheline_from_parts(ftq_req.startAddr, instrs) class ICacheStatusResp(): - ready = True - resp = ICacheResp() + def __init__(self): + self.ready = True + self.resp = ICacheResp() + + def randomize(self, ftq_req: FTQQuery): + self.ready = randbool(rate=80) + self.resp.randomize(ftq_req) + + def __str__(self): + return ( + f"{self.__class__.__name__}(\n" + f" ready={self.ready},\n" + f" resp={self.resp}\n" + f")" + ) + + diff --git a/ut_frontend/ifu/ifu_top/datadef/mmio_related.py b/ut_frontend/ifu/ifu_top/datadef/mmio_related.py index c97974d9..432ea6ad 100644 --- a/ut_frontend/ifu/ifu_top/datadef/mmio_related.py +++ b/ut_frontend/ifu/ifu_top/datadef/mmio_related.py @@ -1,38 +1,277 @@ from .ftq_datadef import FTQIdx +from enum import IntEnum +from dataclasses import dataclass +from random import randint +from ..commons import calc_double_line, randbool class ToUncache(): - addr = 0 - valid = True + def __init__(self): + self.addr = 0 + self.valid = True class FromUncache(): - data = 0 - valid = True + def __init__(self): + self.data = 0 + self.valid = True + def __repr__(self): + return f"{self.__class__.__name__}(data={self.data}, valid={self.valid})" class ITLBReq(): - vaddr = 0 - valid = True + def __init__(self): + self.vaddr = 0 + self.valid = True + def __eq__(self, value): + if type(value) != ITLBReq: + return False + if self.valid != value.valid: + return False + if not self.valid: + return True + return self.vaddr == value.vaddr + + def __repr__(self): + return f"vaddr: {self.vaddr}\nvalid: {self.valid}\n" + class Excp(): - gpfInstr = False - afInstr = False - pfInstr = False + def __init__(self): + self.pfInstr = False + self.gpfInstr = False + self.afInstr = False + + def set_excp_one_hot(self, num): + self.pfInstr = False + self.gpfInstr = False + self.afInstr = False + if num == 1: + self.pfInstr = True + elif num == 2: + self.gpfInstr = True + elif num == 3: + self.afInstr = True + + def __repr__(self): + return (f"{self.__class__.__name__}(" + f"pfInstr={self.pfInstr}, " + f"gpfInstr={self.gpfInstr}, " + f"afInstr={self.afInstr})") + +class ExceptionType(): + NONE = 0 + PF = 1 + GPF = 2 + AF = 3 class ITLBResp(): - valid = True - paddr = 0 - pbmt = 0 - gpaddr = 0 - excp = Excp() - isForVSnonLeafPTE = False + def __init__(self): + self.valid = True + self.paddr = 0 + self.pbmt = 0 + self.gpaddr = 0 + self.excp = Excp() + self.isForVSnonLeafPTE = False + + def get_excp_code(self): + return ExceptionType.PF if self.excp.pfInstr else ExceptionType.GPF if self.excp.gpfInstr else ExceptionType.AF if self.excp.afInstr else ExceptionType.NONE + + def __repr__(self): + return (f"{self.__class__.__name__}(" + f"valid={self.valid}, " + f"paddr={self.paddr}, " + f"pbmt={self.pbmt}, " + f"gpaddr={self.gpaddr}, " + f"excp={self.excp}") class PMPResp(): - instr = 0 - mmio = True + def __init__(self): + self.instr = False + self.mmio = True + def __repr__(self): + return (f"{self.__class__.__name__}(" + f"instr={self.instr}, " + f"mmio={self.mmio})") class RobCommit(): - valid = False - ftqIdx = FTQIdx() - ftqOffset = 0 \ No newline at end of file + def __init__(self): + self.valid = False + self.ftqIdx = FTQIdx() + self.ftqOffset = 0 + + def __repr__(self): + return (f"{self.__class__.__name__}(" + f"valid={self.valid}, " + f"ftqIdx={self.ftqIdx}, " + f"ftqOffset={self.ftqOffset})") + +class MMIOCycleInfo(): + def __init__(self): + self.exceptions = [False, False] + self.ftq_start_addr = 0 + self.ftq_idx: FTQIdx = FTQIdx() + self.csr_fs_is_off = False + self.icache_paddr = 0 + self.icache_pmp_mmios = [0, 0] + self.icache_itlb_pbmts = [0, 0] + + def __repr__(self): + return (f"{self.__class__.__name__}(" + f"exceptions={self.exceptions}, " + f"ftq_start_addr={self.ftq_start_addr}, " + f"ftq_idx={self.ftq_idx}, " + f"csr_fs_is_off={self.csr_fs_is_off}, " + f"icache_paddr={self.icache_paddr}, " + f"icache_pmp_mmios={self.icache_pmp_mmios}, " + f"icache_itlb_pbmts={self.icache_itlb_pbmts})") + + def workable_randomize(self): + """generate random data that will enter mmio state machine + """ + self.ftq_start_addr = randint(0, (1 << 50) -1) + self.csr_fs_is_off = randbool() + self.ftq_idx.flag = randbool() + self.ftq_idx.value = randint(0, 15) + into_mmio_space_type = randint(0, 2) + if into_mmio_space_type == 0: + self.icache_pmp_mmios[0] = True + elif into_mmio_space_type == 1: + self.icache_itlb_pbmts[0] = PbmtAssist.NC + else: + self.icache_itlb_pbmts[0] = PbmtAssist.IO + if calc_double_line(self.ftq_start_addr): + self.icache_pmp_mmios[1] = self.icache_pmp_mmios[0] + self.icache_itlb_pbmts[1] = self.icache_itlb_pbmts[0] + addr_type = randint(0, 7) + self.icache_paddr = randint(0, (1 << 48) - 1) + if addr_type < 3: + self.icache_paddr |= 6 + +class MMIOReq(): + def __init__(self): + self.last_commited = False + self.to_uncache_ready = False + self.from_uncache: FromUncache = FromUncache() + self.itlb_req_ready = False + self.itlb_resp: ITLBResp = ITLBResp() + self.pmp_resp: PMPResp = PMPResp() + self.rob_commits: list[RobCommit] = [RobCommit() for i in range(8)] + self.to_ibuffer_ready = True + + def randomize_with_cycles(self, cycle_info: MMIOCycleInfo): + """randomize mmio req according to the ctrl info of a cycle + + Args: + cycle_info (MMIOCycleInfo): control info of a mmio cycle from start to commited + """ + self.last_commited = randbool(rate=70) + self.to_uncache_ready = randbool(rate=70) + self.from_uncache.valid = randbool(rate=70) + self.from_uncache.data = randint(0, (1 << 32) - 1) + self.itlb_req_ready = randbool(rate=70) + self.itlb_resp.valid = randbool(rate=70) + self.itlb_resp.pbmt = cycle_info.icache_itlb_pbmts[0] if randbool(rate=80) else cycle_info.icache_itlb_pbmts[0] + 1 + self.itlb_resp.paddr = randint(0, (1 << 48) - 1) + self.itlb_resp.gpaddr = randint(0, (1 << 56) -1) + self.itlb_resp.excp.set_excp_one_hot(randint(0, 3)) + self.pmp_resp.instr = randbool(rate=30) + self.pmp_resp.mmio = cycle_info.icache_pmp_mmios[0] if randbool(rate=70) else not cycle_info.icache_pmp_mmios[0] + construct_commit = randbool(rate=80) + if construct_commit: + pos = randint(0, 7) + self.rob_commits[pos].valid = True + self.rob_commits[pos].ftqIdx = cycle_info.ftq_idx + self.rob_commits[pos].ftqOffset = 0 if randbool(rate=80) else randint(1, 15) + else: + self.rob_commits = [RobCommit() for i in range(8)] + self.to_ibuffer_ready = True + + def __repr__(self): + return (f"{self.__class__.__name__}(" + f"last_commited={self.last_commited}, " + f"to_uncache_ready={self.to_uncache_ready}, " + f"from_uncache={self.from_uncache}, " + f"itlb_req_ready={self.itlb_req_ready}, " + f"itlb_resp={self.itlb_resp}, " + f"pmp_resp={self.pmp_resp}, " + f"rob_commits={self.rob_commits}, " + f"to_ibuffer_ready={self.to_ibuffer_ready})") + +import copy + +class MMIOStateDataLogger(): + + def __init__(self): + self.cur_cycle_info: MMIOCycleInfo = MMIOCycleInfo() + self.req_list: list[MMIOReq] = [] + self.history_reqs : list[list[MMIOReq]] = [] + self.used = False + + def start_new_reqs(self, cycle_info: MMIOCycleInfo): + if len(self.history_reqs) > 5: + self.history_reqs.pop(0) + self.history_reqs.append((copy.deepcopy(self.cur_cycle_info), copy.deepcopy(self.req_list))) + if self.cur_cycle_info != cycle_info: + self.cur_cycle_info = cycle_info + self.req_list = [] + + def add_new_req(self, mmio_req: MMIOReq): + self.req_list.append(mmio_req) + + + +mmio_data_logger_instance = MMIOStateDataLogger() + + +class MMIOToIbufferFTQ(): + def __init__(self): + self.br_type = 0 + self.is_rvc = False + self.is_call = False + self.is_ret = False + self.expd_instr = 0 + self.ill = 0 + + def __repr__(self): + return f"""br_type: {self.br_type} +rvc: {self.is_rvc} +call: {self.is_call} +ret: {self.is_ret} +expd_instr: {self.expd_instr} +ill: {self.ill}""" + + def cmp_ill_instr(self, ill, instr): + if ill == 2 or self.ill == 2: + return True + return self.ill == ill and self.expd_instr == instr + + def __eq__(self, value): + if type(value) != MMIOToIbufferFTQ: + return False + # if self.ill == 2 or + return self.br_type == value.br_type and self.is_rvc == value.is_rvc and self.is_call == value.is_call \ + and self.is_ret == value.is_ret and self.cmp_ill_instr(value.ill, value.expd_instr) + +class MMIOState(IntEnum): + STATE_IDLE = 0 + STATE_WAIT_LAST_CMT = 1 + STATE_SEND_REQ = 2 + STATE_WAIT_RESP = 3 + STATE_SEND_TLB = 4 + STATE_TLB_RESP = 5 + STATE_SEND_PMP = 6 + STATE_RESEND_REQ = 7 + STATE_WAIT_RESEND_RESP = 8 + STATE_WAIT_COMMIT = 9 + STATE_COMMITED = 10 + +class PbmtAssist(): + NC = 1 + IO = 2 + + def is_uncache(self, num): + return num == self.NC or num == self.IO + + + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/datadef/req_datadef.py b/ut_frontend/ifu/ifu_top/datadef/req_datadef.py new file mode 100644 index 00000000..c9215d63 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/datadef/req_datadef.py @@ -0,0 +1,226 @@ +from .ftq_datadef import FTQQuery, FTQFlushFromBPU, FTQResp +from .icache_datadef import ICacheStatusResp, ICacheResp +from .sub_modules_def import PreDecodeDataDef, F3PreDecodeData, PredCheckerStage1RetData, PredCheckerStage2RetData +from .ibuffer_datadef import ToIbufferAllRes +from ..commons import randbool, check_icache_resp_all_valid, calc_double_line +import random + +def log_based_random(initial_roof=0.5, decay_rate=0.5, max_val=6): + """ + 用一个随机数计算val值的通用函数 + + 参数: + - initial_roof: 初始屋顶值,默认0.5 + - decay_rate: 衰减率,默认0.5 + - max_val: val的最大值,默认10 + + 返回值: + - val: 计算得到的整数值 + + 逻辑分析: + 1. 第k次成功(val增加到k)的概率是:initial_roof * decay_rate^(k-1) + 2. 连续成功k次的概率是:(initial_roof)^k * decay_rate^(k*(k-1)/2) + 3. 可以通过将[0,1)区间按概率分布划分为不同范围来直接确定val + + 如果为了性能考虑,后续可以对特别大的max_val添加对数函数支持 + """ + if initial_roof <= 0 or decay_rate <= 0 or decay_rate >= 1: + raise ValueError("initial_roof应大于0,decay_rate应在(0,1)之间") + + r = random.random() # 生成[0,1)的随机数 + + # 计算累积概率分布 + new_roof = initial_roof + cnt = 0 + for _ in range(max_val): + if r >= new_roof: + break + cnt += 1 + new_roof *= decay_rate + + # 如果循环结束仍未返回,说明所有可能都成功了,返回最大值 + return cnt + + +class ICacheValidRelated(): + def __init__(self): + icache_resp_valid = True + vaddrs = [0, 0] + icache_doubleline = False + start = 0 + next_start = 0 + # icache_resp_valid, vaddrs, icache_doubleline, req_doubleline, start, next_start + pass + + # def + +class NonMMIOSingleReq(): + def __init__(self): + # f0 stage inputs + self.icache_ready = True + self.ftq_req = FTQQuery() + self.bpu_flush_info = FTQFlushFromBPU() + + # stage 2 inputs + self.invalid_icache_resps: list[ICacheResp]=[] + self.final_icache_resp = ICacheResp() + + # stage 3 inputs + self.fs_is_off = True + + # other info for controlling, which may be add for future features + + def get_icache_block_terms(self): + return len(self.invalid_icache_resps) + + def randomize(self, randomly_choosing_finished=False, finished=True, ftq_idx=None): + """gen random data + + Args: + randomly_choosing_finished (bool, optional): choose the param finished randomly. Defaults to False. + finished (bool, optional): whether the last instr is finished or cross prediction block. Defaults to True. + ftq_idx (_type_, optional): ftq idx of FTQQuery. Defaults to None. + """ + icache_block_terms = log_based_random() + self.fs_is_off = randbool() + self.ftq_req.randomize(ftq_idx=ftq_idx) + + self.bpu_flush_info.randomize(self.ftq_req.ftqIdx) + + # here we init the invalid icache resps + self.invalid_icache_resps = [] + for _ in range(icache_block_terms): + res = ICacheResp() + res.randomize(self.ftq_req, False) + self.final_icache_resp.randomize(self.ftq_req, True, last_finished=finished if not randomly_choosing_finished else random.randint(0, 99) < 70) + + + def __repr__(self): + return f"NonMMIOSingleReq(\nfs_is_off: {self.fs_is_off}\nftq_req: {self.ftq_req}\n\ + final_icache_resp: {self.final_icache_resp}\nbpu_flush_info: {self.bpu_flush_info}\n\ + icache_ready: {self.icache_ready})\nicache_blocks: {self.get_icache_block_terms()}\nicache_block_resps:{self.invalid_icache_resps}" + +class ClusteredNonMMIOReqs(): + def __init__(self): + self.reqs: list[NonMMIOSingleReq] = [] + self.resps: list[NonMMIOResp] = [] + + def randomize(self): + num_reqs = log_based_random(initial_roof=0.4, max_val=7) + self.reqs = [] + for _ in range(num_reqs - 1): + req = NonMMIOSingleReq() + req.randomize(finished=False) + self.reqs.append(req) + req = NonMMIOSingleReq() + req.randomize(finished=True) + self.reqs.append(req) + + def __repr__(self): + return f"ClusteredNonMMIOReqs(\nreqs: {self.reqs}\nref_resps: {self.resps}\n)\n" + +class NonMMIOResp(): + def __init__(self): + # ret of stage 0 + self.ftq_ready = False + self.bpu_flush_res = False + + # ret of stage 2 + self.cut_instrs = [] + self.predecode_res : PreDecodeDataDef = PreDecodeDataDef() + self.cut_ptrs = [] + self.icache_all_valid = True + + + # ret of stage 3 + self.exceptions = [] + self.ranges = 0 + self.pcs = [] + self.addrs = () + self.f3_predecode_res: F3PreDecodeData = F3PreDecodeData() + self.exception_vecs = [] + + # self.expd_instrs = [] + + self.pred_checker_stg1_res: PredCheckerStage1RetData= PredCheckerStage1RetData() + self.to_ibuffer : ToIbufferAllRes = ToIbufferAllRes() + + # ret of wb + self.wb_res : FTQResp = FTQResp() + self.last_half_valid = False + self.pred_checker_stg2_res: PredCheckerStage2RetData = PredCheckerStage2RetData() + + # assistant info, no usage + self.pin_flush = False + self.f3_flush = False + + def __repr__(self): + return f"""NonMMIOResp( + ftq_ready: {self.ftq_ready} + bpu_flush_res: {self.bpu_flush_res} + cut_instrs: {self.cut_instrs} + predecode_res: {self.predecode_res} + cut_ptrs: {self.cut_ptrs} + exception_vecs: {self.exception_vecs} + pcs: {self.pcs} + addrs: {self.addrs} + icache_all_valid: {self.icache_all_valid} + ranges: {self.ranges} + f3_predecode_res: {self.f3_predecode_res} + pred_checker_stg1_res: {self.pred_checker_stg1_res} + pred_checker_stg2_res: {self.pred_checker_stg2_res} + wb_res: {self.wb_res} + last_half_valid: {self.last_half_valid} + to_ibuffer: {self.to_ibuffer} +)""" + + def __eq__(self, value): + if not isinstance(value, NonMMIOResp): + print(f"Type mismatch: {type(value)} != NonMMIOResp") + return False + + if not self.ftq_ready or self.bpu_flush_res: + return self.ftq_ready == value.ftq_ready and self.bpu_flush_res == value.bpu_flush_res + + checks = [ + ("ftq_ready", self.ftq_ready, value.ftq_ready), + ("bpu_flush_res", self.bpu_flush_res, value.bpu_flush_res), + ("cut_instrs", self.cut_instrs, value.cut_instrs), + ("predecode_res", self.predecode_res, value.predecode_res), + ("cut_ptrs", self.cut_ptrs, value.cut_ptrs), + ("exception_vecs", self.exception_vecs, value.exception_vecs), + ("pcs", self.pcs, value.pcs), + ("addrs", self.addrs, value.addrs), + ("icache_all_valid", self.icache_all_valid, value.icache_all_valid), + ("ranges", self.ranges, value.ranges), + ("f3_predecode_res", self.f3_predecode_res, value.f3_predecode_res), + ("pred_checker_stg1_res", self.pred_checker_stg1_res, value.pred_checker_stg1_res), + ("pred_checker_stg2_res", self.pred_checker_stg2_res, value.pred_checker_stg2_res), + ("wb_res", self.wb_res, value.wb_res), + ("last_half_valid", self.last_half_valid, value.last_half_valid), + ("to_ibuffer", self.to_ibuffer, value.to_ibuffer), + ] + + for name, a, b in checks: + if a != b: + print(f"Mismatch in {name}: {str(a)} != {str(b)}") + return False + + return True + +class NonMMIOReqLogger(): + + def __init__(self): + self.before = None + self.cur = None + + def save(self, mmio_req: NonMMIOSingleReq): + self.before = self.cur + self.cur = mmio_req + + def __repr__(self): + return f'''{self.__class__.__name__}: +before: {self.before} +cur: {self.cur}''' + +non_mmio_logger_instance = NonMMIOReqLogger() \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/datadef/sub_modules_def.py b/ut_frontend/ifu/ifu_top/datadef/sub_modules_def.py new file mode 100644 index 00000000..096dbee2 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/datadef/sub_modules_def.py @@ -0,0 +1,94 @@ +class PreDecodeDataDef(): + def __init__(self): + + self.new_instrs = [] + self.jmp_offsets = [] + self.rvcs = [] + self.valid_starts = [] + self.half_valid_starts = [] + + def __str__(self): + res = f"new instrs: {self.new_instrs}\njump offsets: {self.jmp_offsets}\nrvcs: {self.rvcs}\nvalid_starts: {self.valid_starts}\nhalf_valid_starts: {self.half_valid_starts}\n" + return res + + def __eq__(self, value): + if type(self) != type(value): + return False + return self.new_instrs == value.new_instrs and self.jmp_offsets == value.jmp_offsets and self.rvcs == value.rvcs \ + and self.valid_starts == value.valid_starts and self.half_valid_starts == value.half_valid_starts + + +class F3PreDecodeData(): + + def __init__(self): + self.brTypes = [] + self.isCalls = [] + self.isRets = [] + + def __str__(self): + return f"brTypes: {self.brTypes}\nisCalls: {self.isCalls}\nisRets: {self.isRets}" + + def __eq__(self, value): + if type(value) != F3PreDecodeData: + return False + return self.brTypes == value.brTypes and self.isCalls == value.isCalls and self.isRets == value.isRets + +class PredCheckerRetData(): + def __init__(self): + self.ranges = [0] * 16 + self.takens = [True] * 16 + self.fixed_tgts = [0] * 16 + self.jmp_tgts = [0] * 16 + self.faults = [0] * 16 + + # assistant vars + self.miss_pred = [False] * 16 + self.fixed_length = 0 + self.taken_occurs = False + + def __str__(self): + return f"fixed_ranges: {self.ranges}\nfixed_takens: {self.takens}\nfixed_targets: {self.fixed_tgts}\njump_targets: {self.jmp_tgts}\nfault_type: {self.faults}" + + def __eq__(self, value): + if type(value) != PredCheckerRetData: + return False + + return self.ranges == value.ranges and self.takens == value.takens and self.fixed_tgts == value.fixed_tgts and self.jmp_tgts == value.jmp_tgts and self.faults == value.faults + +class PredCheckerStage1RetData(): + + def __init__(self): + self.ranges = [0] * 16 + self.takens = [True] * 16 + + # assistant vars + self.fixed_length = 0 + self.taken_occurs = False + + def __str__(self): + return f"fixed_ranges: {self.ranges}\nfixed_takens: {self.takens}\n" + + def __eq__(self, value): + if type(value) != PredCheckerStage1RetData: + return False + + return self.ranges == value.ranges and self.takens == value.takens + +class PredCheckerStage2RetData(): + def __init__(self): + self.fixed_tgts = [0] * 16 + self.jmp_tgts = [0] * 16 + self.faults = [0] * 16 + + # assistant vars + self.miss_pred = [False] * 16 + + + def __str__(self): + return f"fixed_targets: {self.fixed_tgts}\njump_targets: {self.jmp_tgts}\nfault_type: {self.faults}" + + def __eq__(self, value): + if type(value) != PredCheckerStage2RetData: + return False + + return self.fixed_tgts == value.fixed_tgts and self.jmp_tgts == value.jmp_tgts and self.faults == value.faults \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/__init__.py b/ut_frontend/ifu/ifu_top/env/__init__.py index 08af889c..78c867fa 100644 --- a/ut_frontend/ifu/ifu_top/env/__init__.py +++ b/ut_frontend/ifu/ifu_top/env/__init__.py @@ -1 +1,2 @@ -from .ifu_top_env import IFUTopEnv \ No newline at end of file +from .ifu_top_env import IFUTopEnv +from .ifu_req_receiver_ref import IFUReceiverModel \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/ifu_icache_receiver.py b/ut_frontend/ifu/ifu_top/env/ifu_icache_receiver.py new file mode 100644 index 00000000..c5a3b417 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/ifu_icache_receiver.py @@ -0,0 +1,28 @@ +from ..commons import PREDICT_WIDTH, is_next_line, is_last_in_line + +class IFUICacheReceiverRef(): + def __init__(self): + pass + + def gen_exceptions(self,none_mmio_exceptions, mmio_exceptions): + f2_exception = [] + for i in range(2): + f2_exception.append(none_mmio_exceptions[i] if none_mmio_exceptions[i] != 0 else mmio_exceptions[i]) + return f2_exception + + def gen_exceptions_each_instr(self, non_mmio_exceptions, mmio_exceptions, pcs, rvcs, start_addr, double_line): + cross_page_vec = [] + exception_each_instr = [] + f2_exception = self.gen_exceptions(non_mmio_exceptions, mmio_exceptions) + # 生成每条指令的异常情况 + for i in range(PREDICT_WIDTH): + if not is_next_line(pcs[i], start_addr): + exception_each_instr.append(f2_exception[0]) + else: + exception_each_instr.append(f2_exception[1] if double_line else 0) + + enable_next = is_last_in_line(pcs[i]) and not rvcs[i] and double_line and non_mmio_exceptions[0] == 0 + cross_page_vec.append(non_mmio_exceptions[1] if enable_next else 0) + return exception_each_instr, cross_page_vec + + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py b/ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py new file mode 100644 index 00000000..fc96a203 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py @@ -0,0 +1,172 @@ +# from .ifu_top_ctrl_ref import IFUTopCtrl +from ..datadef import MMIOReq, ExceptionType, FTQIdx, ITLBReq, MMIOToIbufferFTQ, MMIOCycleInfo, MMIOState, PbmtAssist +from ..instr_utils import is_rvc, if_call, if_ret, get_cfi_type +from .rvc_expander_ref import rvc_expand_ref + +class IFUMMIOCtrler(): + def __init__(self): + super().__init__() + self.pbmt_assist = PbmtAssist() + self.cur_state = MMIOState.STATE_IDLE + self.next_mmio_state = MMIOState.STATE_IDLE + self.first_instr = True + self.low_instr = 0 + self.high_instr = 0 + self.mmio_rvc = False + self.need_resend = False + self.mmio_exception = 0 + self.resend_paddr = 0 + self.resend_gpaddr = 0 + self.exceptions = [False, False] + self.commit_res = MMIOToIbufferFTQ() + + + def calc_f2_mmio_exception(self, double_line, pmps, pbmts): + f2_mmio_exception = [0, 0] + if not double_line: + return f2_mmio_exception + + if pmps[0] != pmps[1] or pbmts[0] != pbmts[1]: + f2_mmio_exception[1] = 3 + return f2_mmio_exception + + def setup_before_mmio_states(self, cycle_req: MMIOCycleInfo, final_exceptions): + self.cycle_req = cycle_req + self.exceptions = final_exceptions + + def reset_all_state(self): + self.reset_state() + self.cur_state = MMIOState.STATE_IDLE + + def reset_state(self): + self.next_mmio_state = MMIOState.STATE_IDLE + self.need_resend = False + self.mmio_rvc = False + self.resend_paddr = 0 + self.resend_gpaddr = 0 + self.mmio_exception = ExceptionType.NONE + + def push_state(self, req: MMIOReq): + last_state = self.cur_state + self.cur_state = self.next_mmio_state + if self.next_mmio_state == MMIOState.STATE_IDLE: + is_mmio_space = (self.cycle_req.icache_pmp_mmios[0] or self.pbmt_assist.is_uncache(self.cycle_req.icache_itlb_pbmts[0])) and (self.exceptions[0] + self.exceptions[1] == 0) + if is_mmio_space: + if self.cycle_req.icache_itlb_pbmts[0] == PbmtAssist.NC: + self.next_mmio_state = MMIOState.STATE_SEND_REQ + # return self.next_mmio_state, self.cycle_req.icache_paddr + else: + self.next_mmio_state = MMIOState.STATE_WAIT_LAST_CMT + # self.from_icache_itlb_pbmt = req.itlb_pbmt + # self.from_icache_pmp_mmio = req.pmp_mmio + elif self.next_mmio_state == MMIOState.STATE_WAIT_LAST_CMT: + if self.first_instr or req.last_commited: + + self.next_mmio_state = MMIOState.STATE_SEND_REQ + # return self.next_mmio_state, self.cycle_req.icache_paddr + # else: + # if req.last_commit: + # self.mmio_state = MMIOState.STATE_SEND_REQ + + elif self.next_mmio_state == MMIOState.STATE_SEND_REQ: + if req.to_uncache_ready: + self.next_mmio_state = MMIOState.STATE_WAIT_RESP + elif self.next_mmio_state == MMIOState.STATE_WAIT_RESP: + self.high_instr = (req.from_uncache.data >> 16) & ((1 << 16) -1) + self.low_instr = req.from_uncache.data & ((1 << 16) -1) + self.commit_res = self.calc_instr_and_infos() + if req.from_uncache.valid: + self.mmio_rvc = (req.from_uncache.data & 3) != 3 + self.need_resend = (not self.mmio_rvc) and ((self.cycle_req.icache_paddr >> 1) & 3) == 3 + + if self.need_resend: + self.next_mmio_state = MMIOState.STATE_SEND_TLB + itlb_req = ITLBReq() + itlb_req.vaddr = self.cycle_req.ftq_start_addr + 2 + # return self.next_mmio_state, itlb_req + else: + self.next_mmio_state = MMIOState.STATE_WAIT_COMMIT + # return self.next_mmio_state, self.get_instr_and_infos() + + elif self.next_mmio_state == MMIOState.STATE_SEND_TLB: + if req.itlb_req_ready: + self.next_mmio_state = MMIOState.STATE_TLB_RESP + + elif self.next_mmio_state == MMIOState.STATE_TLB_RESP: + if req.itlb_resp.valid: + itlb_resp_excp_code = req.itlb_resp.get_excp_code() + self.resend_paddr = req.itlb_resp.paddr + self.resend_gpaddr = req.itlb_resp.gpaddr + if itlb_resp_excp_code != ExceptionType.NONE: + self.mmio_exception = itlb_resp_excp_code + else: + self.mmio_exception = ExceptionType.AF if req.itlb_resp.pbmt != self.cycle_req.icache_itlb_pbmts[0] else ExceptionType.NONE + + if self.mmio_exception != ExceptionType.NONE: + self.next_mmio_state = MMIOState.STATE_WAIT_COMMIT + # return self.next_mmio_state, self.mmio_exception + else: + self.next_mmio_state = MMIOState.STATE_SEND_PMP + # return self.next_mmio_state, self.resend_paddr + + + elif self.next_mmio_state == MMIOState.STATE_SEND_PMP: + self.mmio_exception = ExceptionType.AF if (req.pmp_resp.instr) or self.cycle_req.icache_pmp_mmios[0] != req.pmp_resp.mmio else ExceptionType.NONE + if self.mmio_exception != ExceptionType.NONE: + self.next_mmio_state = MMIOState.STATE_WAIT_COMMIT + # return self.next_mmio_state, self.mmio_exception + else: + self.next_mmio_state = MMIOState.STATE_RESEND_REQ + # return self.next_mmio_state, self.resend_paddr + + elif self.next_mmio_state == MMIOState.STATE_RESEND_REQ: + if req.to_uncache_ready: + self.next_mmio_state = MMIOState.STATE_WAIT_RESEND_RESP + elif self.next_mmio_state == MMIOState.STATE_WAIT_RESEND_RESP: + if req.from_uncache.valid: + self.next_mmio_state = MMIOState.STATE_WAIT_COMMIT + self.high_instr = req.from_uncache.data & ((1 << 16) -1) + self.commit_res = self.calc_instr_and_infos() + # return self.next_mmio_state, self.get_instr_and_infos() + elif self.next_mmio_state == MMIOState.STATE_WAIT_COMMIT: + mmio_commit = any(commit.valid and commit.ftqIdx == self.cycle_req.ftq_idx and commit.ftqOffset == 0 for commit in req.rob_commits) + if mmio_commit or self.cycle_req.icache_itlb_pbmts[0] == PbmtAssist.NC: + self.next_mmio_state = MMIOState.STATE_COMMITED + # return self.next_mmio_state, self.cycle_req.ftq_start_addr + 2 if self.is_rvc else self.cycle_req.ftq_start_addr + 4 + elif self.next_mmio_state == MMIOState.STATE_COMMITED: + self.first_instr = self.first_instr and (not req.to_ibuffer_ready) + self.reset_state() + + if last_state == MMIOState.STATE_WAIT_RESP and self.cur_state == MMIOState.STATE_SEND_TLB: + itlb_req = ITLBReq() + itlb_req.vaddr = self.cycle_req.ftq_start_addr + 2 + return [self.cur_state, itlb_req] + elif (last_state == MMIOState.STATE_TLB_RESP or last_state == MMIOState.STATE_SEND_PMP) \ + and self.cur_state == MMIOState.STATE_WAIT_COMMIT.value: + return [self.cur_state, self.mmio_exception, self.resend_gpaddr] + elif (last_state == MMIOState.STATE_TLB_RESP.value and self.cur_state == MMIOState.STATE_SEND_PMP) \ + or (last_state == MMIOState.STATE_SEND_PMP.value and self.cur_state == MMIOState.STATE_RESEND_REQ): + return [self.cur_state, self.resend_paddr] + elif ((last_state == MMIOState.STATE_IDLE or last_state == MMIOState.STATE_WAIT_LAST_CMT) \ + and self.cur_state == MMIOState.STATE_SEND_REQ): + return [self.cur_state, self.cycle_req.icache_paddr] + elif (last_state == MMIOState.STATE_WAIT_RESP.value or last_state == MMIOState.STATE_WAIT_RESEND_RESP.value) \ + and self.cur_state == MMIOState.STATE_WAIT_COMMIT.value: + return [self.cur_state, self.commit_res] + elif (last_state == MMIOState.STATE_WAIT_COMMIT and self.cur_state == MMIOState.STATE_COMMITED): + return [MMIOState.STATE_COMMITED, self.cycle_req.ftq_start_addr + 2 if self.commit_res.is_rvc else self.cycle_req.ftq_start_addr + 4] + + return [self.cur_state] + + def calc_instr_and_infos(self): + res = MMIOToIbufferFTQ() + new_instr = (self.high_instr << 16) | self.low_instr + res.br_type = get_cfi_type(new_instr) + + res.is_rvc = is_rvc(new_instr) + res.is_call = if_call(new_instr, res.br_type) + res.is_ret = if_ret(new_instr, res.br_type) + res.expd_instr, res.ill = rvc_expand_ref(new_instr, self.cycle_req.csr_fs_is_off) + if res.ill: + res.expd_instr = new_instr + return res diff --git a/ut_frontend/ifu/ifu_top/env/ifu_req_receiver_ref.py b/ut_frontend/ifu/ifu_top/env/ifu_req_receiver_ref.py new file mode 100644 index 00000000..d24763ee --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/ifu_req_receiver_ref.py @@ -0,0 +1,369 @@ + +from toffee import Model, driver_hook +from ..datadef import ExistsIdx, ICacheResp, ICacheStatusResp, FTQIdx, FTQFlushFromBPUStg, \ + FTQResp, ToIbufferAllRes,FTQRedirect, FTQFlushFromBPU, NonMMIOSingleReq, NonMMIOResp, \ + MMIOCycleInfo, MMIOReq, ExceptionType, PredCheckerStage1RetData, NonMMIOReqLogger, ClusteredNonMMIOReqs +from .ifu_mmio_ref import IFUMMIOCtrler +# from .ifu_top_ctrl_ref import IFUTopCtrl +from .predecode_ref import PredecodeRef, F3PredecoderRef +from .pred_checker_ref import PredCheckerRef, TYPE_JAL +from .rvc_expander_ref import rvc_expand_ref +from .ifu_icache_receiver import IFUICacheReceiverRef + +from ..commons import PREDICT_WIDTH, LAST_HALF_ERR, PRED_ERR, is_next_line, is_last_in_line, \ + calc_double_line, calc_blk_length, calc_cut_ptr, check_icache_resp_all_valid +FULL_LAST_IDX = PREDICT_WIDTH - 1 + +def generate_prefix_one_hots(prefix_length, length): + prefix_length = min(max(prefix_length, 0), length) + bits = [1] * prefix_length + [0] * (length - prefix_length) + return bits + +def bool_list_to_int(bits): + result = 0 + for bit in reversed(bits): + result = (result << 1) | bit + return result + + +class IFUReqReceiverRef(): + def __init__(self): + # self.ftq_double_line = False + pass + + def cal_f1_pcs(self, start_addr): + # self.f1_pcs = [start_addr + i * 2 for i in range(PREDICT_WIDTH)] + + return [start_addr + i * 2 for i in range(PREDICT_WIDTH)] + + # 无跳转时的指令距离 + def calc_ftr_ranges(self, valid, cur_start_addr, next_start_addr): + self.start_addr = cur_start_addr + self.next_start_addr = next_start_addr + if valid: + length = PREDICT_WIDTH + else: + length = calc_blk_length(cur_start_addr, next_start_addr) + + return generate_prefix_one_hots(length, PREDICT_WIDTH) + + def calc_jump_ranges(self, valid, ftq_offset): + length = ftq_offset + 1 if valid else PREDICT_WIDTH + return generate_prefix_one_hots(length, PREDICT_WIDTH) + + + def calc_cut_ptr(self, startAddr): + idx_pos = (startAddr >> 1) & 0x1F + # self.cut_ptr = [idx_pos + i for i in range(PREDICT_WIDTH + 1)] + return [idx_pos + i for i in range(PREDICT_WIDTH + 1)] + + def cut_from_cacheline(self, cacheline, cut_ptr): + instrs = [] + for idx in cut_ptr: + assert 0 <= idx < 64 + pos = idx * 16 + instrs.append((cacheline >> pos) & 0xFFFF) + return instrs + +def flush_from_bpu(flush_bpu: FTQFlushFromBPU, cur_ftq_idx: FTQIdx): + for key in flush_bpu.stgs.keys(): + flush_info: FTQFlushFromBPUStg = flush_bpu.stgs[key] + if not flush_info.stg_valid: + continue + if not (flush_info.ftqIdx > cur_ftq_idx): + return True + return False + +def check_last_req_half_valid(ranges, valids, rvcs, takens): + return check_last_valid(ranges, valids, PREDICT_WIDTH-1, rvcs, takens) + + +def check_last_valid(ranges, valids, last_idx, rvcs, takens, mmio=False): + return ranges[last_idx] and valids[last_idx] and (not rvcs[last_idx]) and (not takens[last_idx]) \ + and (not mmio) + +def redirect_flush(miss_pred_idx, valids, last_idx, rvcs, pred_check_stg1_res: PredCheckerStage1RetData, mmio=False): + miss_off = ExistsIdx() + mis_type = 0 + ranges = pred_check_stg1_res.ranges + takens = pred_check_stg1_res.takens + + + exists_last_half_err = check_last_valid(ranges, valids, last_idx, rvcs, takens, mmio=mmio) and last_idx != FULL_LAST_IDX + # print(exists_last_half_err) + last_half_valid = check_last_valid(ranges, valids, PREDICT_WIDTH - 1, rvcs, takens, mmio) + if exists_last_half_err: + miss_off.exists = True + miss_off.offsetIdx = last_idx + mis_type = LAST_HALF_ERR + return miss_off, mis_type + + if 0 <= miss_pred_idx < 16: + miss_off.exists = True + miss_off.offsetIdx = miss_pred_idx + mis_type = PRED_ERR + + return miss_off, mis_type + +AGENT_NAME="top_agent" + + +class IFUReceiverModel(Model): + def __init__(self): + super().__init__() + self.step_funcs = [] + + self.ifu_req_receiver = IFUReqReceiverRef() + self.mmio_ctrl = IFUMMIOCtrler() + # self.top_ctrl = IFUTopCtrl() + self.predecode_ref = PredecodeRef() + self.f3predecoder_ref = F3PredecoderRef() + self.pred_checker_ref = PredCheckerRef() + self.icache_receiver_ref = IFUICacheReceiverRef() + self.last_half_valid = False + + # self.f2_exception = [0, 0] + # exception_each_instr = [0 for _ in range(17)] + self.wb_tgt = 0 + self.last_pin_flush = False + self.ftq_idx = None + + # MMIO在这个周期里做不了,不过可以先存下来 + + def set_fs_is_off(self, fs_is_off): + expds = self.expd_instrs_yield(fs_is_off) + self.step_funcs.append(expds) + + def expd_instrs(self, fs_is_off, new_instrs): + extd_instrs = [] + ills = [] + for instr in new_instrs: + ext_instr, ill = rvc_expand_ref(instr, fsIsOff=fs_is_off) + extd_instrs.append(instr if ill==1 else ext_instr) + ills.append(ill) + return extd_instrs, ills + + def deal_with_non_mmio(self, req: NonMMIOSingleReq, logger: NonMMIOReqLogger): + self.inner_deal_with_non_mmio(req) + + def deal_with_non_mmio_clusters(self, reqs: ClusteredNonMMIOReqs): + res_list = [] + for req in reqs.reqs: + res_list.append(self.inner_deal_with_non_mmio(req)) + return res_list + + def force_flush_non_mmio_stats(self): + self.last_pin_flush = False + + def inner_deal_with_non_mmio(self, req: NonMMIOSingleReq): + res = NonMMIOResp() + + # f0 + res.bpu_flush_res = flush_from_bpu(req.bpu_flush_info, req.ftq_req.ftqIdx) + res.ftq_ready = req.icache_ready + if res.bpu_flush_res or (not res.ftq_ready): + self.last_pin_flush = False + res.last_half_valid = self.last_half_valid + return res + + # entering f1 stage + + # this will be calculated after the f0 prepare stage, where + start_addr_f1 = req.ftq_req.startAddr + # print(f"start addr: {start_addr_f1}") + f1_pcs = self.ifu_req_receiver.cal_f1_pcs(start_addr_f1) + double_line = calc_double_line(start_addr_f1) + cut_ptrs = calc_cut_ptr(start_addr_f1) + + + # works at f2 stage + + next_start_addr_f2 = req.ftq_req.nextStartAddr + offset: ExistsIdx = req.ftq_req.ftqOffset + + ftr_ranges = self.ifu_req_receiver.calc_ftr_ranges(offset.exists, start_addr_f1, next_start_addr_f2) + jump_ranges = self.ifu_req_receiver.calc_jump_ranges(offset.exists, offset.offsetIdx) + instr_ranges = [ftr & jmp for ftr, jmp in zip(ftr_ranges, jump_ranges)] + + # at f2 stage, icache resp comes + res.cut_ptrs = cut_ptrs + cacheline = req.final_icache_resp.data | (req.final_icache_resp.data << 512) + res.cut_instrs = self.ifu_req_receiver.cut_from_cacheline(cacheline, res.cut_ptrs) + res.predecode_res = self.predecode_ref.predecode(res.cut_instrs) + + res.icache_all_valid = check_icache_resp_all_valid(req.final_icache_resp.icache_valid, req.final_icache_resp.vaddrs, req.final_icache_resp.double_line, \ + double_line, req.ftq_req.startAddr, req.ftq_req.nextlineStart) + # if not res.icache_all_valid: + # return res + assert res.icache_all_valid + + # goto f3 stage: + + f2_mmio_exception = self.mmio_ctrl.calc_f2_mmio_exception(req.final_icache_resp.double_line, req.final_icache_resp.pmp_mmios, req.final_icache_resp.itlb_pbmts) + + f2_exception = self.icache_receiver_ref.gen_exceptions(req.final_icache_resp.exceptions, f2_mmio_exception) + + exception_each_instr, cross_page_vec = \ + self.icache_receiver_ref.gen_exceptions_each_instr(req.final_icache_resp.exceptions, \ + f2_mmio_exception, f1_pcs, res.predecode_res.rvcs, req.ftq_req.startAddr, double_line) + + res.exceptions = f2_exception + res.exception_vecs =exception_each_instr + res.pcs = f1_pcs + res.addrs = (req.final_icache_resp.paddr, req.final_icache_resp.gpaddr) + + res.ranges = bool_list_to_int(instr_ranges) + res.f3_predecode_res = self.f3predecoder_ref.f3_predecode(res.predecode_res.new_instrs) + + expd_instrs, ills = self.expd_instrs(req.fs_is_off, res.predecode_res.new_instrs) + integrated_valids = res.predecode_res.half_valid_starts if self.last_half_valid else res.predecode_res.valid_starts + + res.pred_checker_stg1_res, pred_checker_stg2_res = self.pred_checker_ref.pred_check_stgs(res.f3_predecode_res, res.predecode_res, integrated_valids, instr_ranges, \ + offset, f1_pcs, req.ftq_req.nextStartAddr) + + last_half_valid = check_last_req_half_valid(res.pred_checker_stg1_res.ranges, integrated_valids, \ + res.predecode_res.rvcs, res.pred_checker_stg1_res.takens) + flush_f3 = self.last_pin_flush and self.ftq_idx != req.ftq_req.ftqIdx + self.ftq_idx = req.ftq_req.ftqIdx + if self.last_pin_flush: + if self.ftq_idx == req.ftq_req.ftqIdx: + self.last_half_valid = False + else: + self.last_half_valid = last_half_valid and not self.last_half_valid + else: + self.last_half_valid = last_half_valid + # self.last_half_valid = (last_half_valid and not self.last_half_valid) if not flush_f3 else False + # print(f"res: last half valid: {last_half_valid}") + + res.to_ibuffer.toIbuffer.valid = not flush_f3 + last_valid_idx = res.pred_checker_stg1_res.fixed_length-1 + + res.to_ibuffer.toIbuffer.instr_valids = bool_list_to_int(integrated_valids) + + ranges_and_valids = [a & b for (a,b ) in zip(res.pred_checker_stg1_res.ranges, integrated_valids)] + + res.to_ibuffer.toIbuffer.enqEnable = bool_list_to_int(ranges_and_valids) + + res.to_ibuffer.toIbuffer.instrs = expd_instrs + res.to_ibuffer.toIbuffer.illegalInstrs = ills + + res.to_ibuffer.toIbuffer.pds.brTypes = res.f3_predecode_res.brTypes + res.to_ibuffer.toIbuffer.pds.isCalls = res.f3_predecode_res.isCalls + res.to_ibuffer.toIbuffer.pds.isRets = res.f3_predecode_res.isRets + res.to_ibuffer.toIbuffer.pds.isRVCs = res.predecode_res.rvcs + + res.to_ibuffer.toIbuffer.ftqPtr = req.ftq_req.ftqIdx + + res.to_ibuffer.toIbuffer.foldpcs = [xorfold((pc>>1) & ((1 << 49) -1), 10, 49) for pc in f1_pcs] + + res.to_ibuffer.toIbuffer.exceptionTypes = [] + + for i in range(PREDICT_WIDTH): + cur_exception = exception_each_instr[i] + res.to_ibuffer.toIbuffer.exceptionTypes.append(cur_exception if cur_exception != 0 else cross_page_vec[i]) + + res.to_ibuffer.toIbuffer.backendException = req.final_icache_resp.backend_exception + res.to_ibuffer.toBackendGpaddrMem.wen = ExceptionType.GPF in f2_exception + if res.to_ibuffer.toBackendGpaddrMem.wen: + res.to_ibuffer.toBackendGpaddrMem.waddr = req.ftq_req.ftqIdx.value + res.to_ibuffer.toBackendGpaddrMem.gpaddr = req.final_icache_resp.gpaddr + + # entering wb stage, collect res + + res.pred_checker_stg2_res = pred_checker_stg2_res + + res.wb_res.valid = not flush_f3 + + res.wb_res.ftqIdx.flag = req.ftq_req.ftqIdx.flag + res.wb_res.ftqIdx.value = req.ftq_req.ftqIdx.value + + res.wb_res.pcs = f1_pcs + + miss_pred_idx = -1 + for i in range(len(res.pred_checker_stg2_res.miss_pred)): + if res.pred_checker_stg2_res.miss_pred[i] != 0: + miss_pred_idx = i + break + + miss_offset, mis_type = redirect_flush(miss_pred_idx, integrated_valids, last_valid_idx, \ + res.predecode_res.rvcs, res.pred_checker_stg1_res, False) + if miss_pred_idx <0: + miss_pred_idx = len(res.pred_checker_stg2_res.miss_pred) - 1 + res.wb_res.misOffset = miss_offset if res.wb_res.valid else ExistsIdx() + wb_tgt = f1_pcs[last_valid_idx] + 4 if mis_type == LAST_HALF_ERR else res.pred_checker_stg2_res.fixed_tgts[miss_pred_idx] + res.wb_res.target = wb_tgt if res.wb_res.valid else f1_pcs[0] + 4 + if res.wb_res.valid: + res.wb_res.pds.pdValids = integrated_valids + res.wb_res.instrRanges = ranges_and_valids + else: + res.wb_res.instrRanges = [0 for _ in range(16)] + res.wb_res.instrRanges[0] = 1 + res.wb_res.pds.pdValids = res.wb_res.instrRanges + res.wb_res.pds.brTypes = res.f3_predecode_res.brTypes + res.wb_res.pds.isCalls = res.f3_predecode_res.isCalls + res.wb_res.pds.isRets = res.f3_predecode_res.isRets + res.wb_res.pds.isRVCs = res.predecode_res.rvcs + # res.wb_res.pds.pdValids = integrated_valids # if the instr is valid in the block + + first_jmp_idx = len(res.pred_checker_stg2_res.jmp_tgts) -1 + + for i in range(len(res.pred_checker_stg2_res.jmp_tgts)): + if integrated_valids[i] and res.f3_predecode_res.brTypes[i] == TYPE_JAL: + first_jmp_idx = i + break + + res.wb_res.jalTarget = res.pred_checker_stg2_res.jmp_tgts[first_jmp_idx] if res.wb_res.valid else 0 + res.wb_res.cfiOffset_valid = res.pred_checker_stg1_res.taken_occurs if res.wb_res.valid else 0 + + res.last_half_valid = self.last_half_valid + # req + + # 如果这个stage已经是flush阶段,就不触发flush + self.last_pin_flush = res.wb_res.misOffset.exists and not flush_f3 + res.pin_flush = self.last_pin_flush + res.f3_flush = flush_f3 + return res + + # @driver_hook(agent_name=AGENT_NAME) + def set_up_before_mmio_states(self, mmio_cycle_info: MMIOCycleInfo): + double_line = calc_double_line(mmio_cycle_info.ftq_start_addr) + mmio_exceptions = self.mmio_ctrl.calc_f2_mmio_exception(double_line, mmio_cycle_info.icache_pmp_mmios, \ + mmio_cycle_info.icache_itlb_pbmts) + + f2_exception = self.icache_receiver_ref.gen_exceptions(mmio_cycle_info.exceptions, mmio_exceptions) + + self.mmio_ctrl.setup_before_mmio_states(mmio_cycle_info, f2_exception) + self.mmio_ctrl.push_state(MMIOReq()) + + + # @driver_hook(agent_name=AGENT_NAME) + def deal_with_single_mmio_req(self, mmio_req:MMIOReq): + return self.mmio_ctrl.push_state(mmio_req) + + @driver_hook + def reset_mmio_state(self): + self.mmio_ctrl.reset_all_state() + + +def xorfold(x: int, res_width: int, width: int | None = None) -> int: + """ + 把整数 x 按 res_width 分块并按位 XOR 折叠,返回 res_width 位结果。 + - x: 非负整数(对应 UInt) + - res_width: 每块位宽,>0 + - width: x 的逻辑位宽(可选)。如果不写,就用 x.bit_length();如需和硬件一致(保留前导0),请显式传入。 + """ + assert res_width > 0 + if width is None: + width = max(1, x.bit_length()) # Python 对 0 的 bit_length 是 0,这里至少给 1 + + k = (width + res_width - 1) // res_width # 上取整块数 + acc = 0 + mask_block = (1 << res_width) - 1 + + # 逐块提取并 XOR:第 i 块是 [i*res_width, (i+1)*res_width) + for i in range(k): + chunk = (x >> (i * res_width)) & mask_block + acc ^= chunk + + # 结果自然落在 res_width 位 + return acc \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/ifu_top_env.py b/ut_frontend/ifu/ifu_top/env/ifu_top_env.py index 8f824441..a075c977 100644 --- a/ut_frontend/ifu/ifu_top/env/ifu_top_env.py +++ b/ut_frontend/ifu/ifu_top/env/ifu_top_env.py @@ -2,9 +2,13 @@ from ..bundle import IFUTopBundle from toffee import Env from dut.NewIFU import DUTNewIFU +from .ifu_req_receiver_ref import IFUReceiverModel class IFUTopEnv(Env): def __init__(self, dut: DUTNewIFU): super().__init__() top_bundle = IFUTopBundle.from_prefix("").bind(dut) - self.top_agent = OutsideAgent(top_bundle) \ No newline at end of file + self.top_agent = OutsideAgent(top_bundle) + ifu_receiver_model = IFUReceiverModel() + self.attach(ifu_receiver_model) + self.ifu_receiver_model = ifu_receiver_model \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/pred_checker_ref.py b/ut_frontend/ifu/ifu_top/env/pred_checker_ref.py new file mode 100644 index 00000000..421b37c0 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/pred_checker_ref.py @@ -0,0 +1,222 @@ +from ..datadef import F3PreDecodeData, PredCheckerRetData, ExistsIdx, PreDecodeDataDef, PredCheckerStage2RetData, PredCheckerStage1RetData +from typing import Generator +from ..commons import PREDICT_WIDTH + +TYPE_JAL=2 +TYPE_JALR=3 +TYPE_BR = 1 +TYPE_NONE = 0 + + +NO_FAULT=0 +JAL_FAULT=1 +RET_FAULT=2 +TGT_FAULT=3 +NON_CFI_FAULT=4 +INVALID_TAKEN_FAULT=5 +JALR_FAULT=6 + + +def get_first_true(bool_list: list[bool]): + if True not in bool_list: + return len(bool_list) + return bool_list.index(True) + + +class PredCheckerRef(): + def __init__(self): + self.generator_queue: list[Generator] = [] + + def pred_check_stg1(self, f3_pd: F3PreDecodeData, pd: PreDecodeDataDef, instr_valids, instr_ranges, jmp_idx_all: ExistsIdx, pcs, tgt) -> PredCheckerStage1RetData: + generator = self.pred_check_yield(f3_pd, pd, instr_valids, instr_ranges, jmp_idx_all, pcs, tgt) + res = next(generator) + self.generator_queue.append(generator) + return res + + def pred_check_stg2(self, fire) -> PredCheckerStage2RetData: + res = PredCheckerStage2RetData() + if self.generator_queue: + new_gen = self.generator_queue.pop() + res = new_gen.send(fire) + return res + + + def pred_check_yield(self, f3_pd: F3PreDecodeData, pd: PreDecodeDataDef, instr_valids, instr_ranges, jmp_idx_all: ExistsIdx, pcs, tgt): + jal_errs = [False] * 16 + jalr_errs = [False] * 16 + ret_errs = [False] * 16 + tgt_errs = [False] * 16 + non_cfi_errs = [False] * 16 + invalid_errs = [False] * 16 + + decode_jmp_offs = pd.jmp_offsets + rvcs = pd.rvcs + whether_jmp = jmp_idx_all.exists + jmp_idx = jmp_idx_all.offsetIdx + # res = PredCheckerRetData() + + res1 = PredCheckerStage1RetData() + jal_idxs = self.check_num_errs(f3_pd.brTypes, TYPE_JAL) + jal_errs = self.jmp_type_err_check(jal_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + + jalr_init_idxs = self.check_num_errs(f3_pd.brTypes, TYPE_JALR) + ret_idxs = self.check_num_errs(f3_pd.isRets, 1) + jalr_idxs = [jalr and not ret for jalr, ret in zip(jalr_init_idxs, ret_idxs)] + + jalr_errs = self.jmp_type_err_check(jalr_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + ret_errs = self.jmp_type_err_check(ret_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + + true_jmp_stg = min(get_first_true(jal_errs), get_first_true(jalr_errs), get_first_true(ret_errs)) + if true_jmp_stg >= jmp_idx: + res1.ranges = instr_ranges[:] + retake = jmp_idx + else: + res1.ranges = [True] * true_jmp_stg + [False] * (len(instr_ranges) - true_jmp_stg) + retake = true_jmp_stg + + res1.fixed_length = 16 if 0 not in res1.ranges else res1.ranges.index(0) + # above done the stage 1 work: jal/jalr/ret errs and fix range & taken idx + + res1.takens = [res1.ranges[i] and instr_valids[i] and \ + ( f3_pd.brTypes[i] == TYPE_JAL or f3_pd.brTypes[i] == TYPE_JALR or f3_pd.isRets[i] \ + or (whether_jmp and f3_pd.brTypes[i] != TYPE_NONE and i == retake)) \ + for i in range(len(instr_ranges))] + + res1.taken_occurs = (1 in res1.takens) + + + next_fire = yield res1 + + res2 = PredCheckerStage2RetData() + + if next_fire: + if whether_jmp: + jmp_off_range = res1.ranges[jmp_idx] + instr_valid_off = instr_valids[jmp_idx] + non_cfi_errs[jmp_idx] = jmp_off_range and instr_valid_off and f3_pd.brTypes[jmp_idx] == TYPE_NONE + invalid_errs[jmp_idx] = jmp_off_range and not instr_valid_off + jmp_tgt_predecode = decode_jmp_offs[jmp_idx] + pcs[jmp_idx] + tgt_errs[jmp_idx] = jmp_off_range and instr_valid_off and (f3_pd.brTypes[jmp_idx] == TYPE_JAL or f3_pd.brTypes[jmp_idx] == TYPE_BR) and jmp_tgt_predecode != tgt + + # self.fault_type = [] + # self.fixed_tgt = [] + + for i in range(len(jal_errs)): + res2.faults[i] = (JAL_FAULT if jal_errs[i] else \ + JALR_FAULT if jalr_errs[i] else \ + RET_FAULT if ret_errs[i] else \ + TGT_FAULT if tgt_errs[i] else \ + NON_CFI_FAULT if non_cfi_errs[i] else \ + INVALID_TAKEN_FAULT if invalid_errs[i] else \ + NO_FAULT + ) + res2.miss_pred[i] = (res2.faults[i] != NO_FAULT) + cur_jmp_tgt = (pcs[i] + decode_jmp_offs[i]) & (1 << 64) -1 + res2.jmp_tgts[i] = cur_jmp_tgt + if jal_errs[i] or tgt_errs[i]: + res2.fixed_tgts[i] = cur_jmp_tgt + else: + seq_tgt = pcs[i] + (2 if rvcs[i] or not instr_valids[i] else 4) + res2.fixed_tgts[i] = seq_tgt + res2.fixed_tgts[i] &= (1 << 64) -1 + + yield res2 + + + def pred_check_stgs(self, f3_pd: F3PreDecodeData, pd: PreDecodeDataDef, instr_valids, instr_ranges, jmp_idx_all: ExistsIdx, pcs, tgt): + jal_errs = [False] * 16 + jalr_errs = [False] * 16 + ret_errs = [False] * 16 + tgt_errs = [False] * 16 + non_cfi_errs = [False] * 16 + invalid_errs = [False] * 16 + + decode_jmp_offs = pd.jmp_offsets + rvcs = pd.rvcs + whether_jmp = jmp_idx_all.exists + jmp_idx = jmp_idx_all.offsetIdx if whether_jmp else PREDICT_WIDTH + # res = PredCheckerRetData() + + res1 = PredCheckerStage1RetData() + jal_idxs = self.check_num_errs(f3_pd.brTypes, TYPE_JAL) + jal_errs = self.jmp_type_err_check(jal_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + + jalr_init_idxs = self.check_num_errs(f3_pd.brTypes, TYPE_JALR) + ret_idxs = self.check_num_errs(f3_pd.isRets, 1) + jalr_idxs = [jalr and not ret for jalr, ret in zip(jalr_init_idxs, ret_idxs)] + + jalr_errs = self.jmp_type_err_check(jalr_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + # print(f"jalr_errs: {jalr_errs}") + ret_errs = self.jmp_type_err_check(ret_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + + true_jmp_stg = min(get_first_true(jal_errs), get_first_true(jalr_errs), get_first_true(ret_errs)) + # print(f"valid: {whether_jmp}; jmp_idx: {jmp_idx}, true_jmp_stg: {true_jmp_stg}") + + if true_jmp_stg >= jmp_idx: + res1.ranges = instr_ranges[:] + retake = jmp_idx + else: + true_jmp_stg += 1 + res1.ranges = [True] * true_jmp_stg + [False] * (len(instr_ranges) - true_jmp_stg) + retake = true_jmp_stg + # print(f"res1_ranges: {res1.ranges}") + res1.fixed_length = 16 if 0 not in res1.ranges else res1.ranges.index(0) + # above done the stage 1 work: jal/jalr/ret errs and fix range & taken idx + + res1.takens = [res1.ranges[i] and instr_valids[i] and \ + ( f3_pd.brTypes[i] == TYPE_JAL or f3_pd.brTypes[i] == TYPE_JALR or f3_pd.isRets[i] \ + or (whether_jmp and (f3_pd.brTypes[i] != TYPE_NONE) and (i == retake))) \ + for i in range(len(res1.ranges))] + + res1.taken_occurs = (1 in res1.takens) + + + + res2 = PredCheckerStage2RetData() + + if whether_jmp: + jmp_off_range = res1.ranges[jmp_idx] + instr_valid_off = instr_valids[jmp_idx] + non_cfi_errs[jmp_idx] = jmp_off_range and instr_valid_off and f3_pd.brTypes[jmp_idx] == TYPE_NONE + invalid_errs[jmp_idx] = jmp_off_range and not instr_valid_off + jmp_tgt_predecode = decode_jmp_offs[jmp_idx] + pcs[jmp_idx] + tgt_errs[jmp_idx] = jmp_off_range and instr_valid_off and (f3_pd.brTypes[jmp_idx] == TYPE_JAL or f3_pd.brTypes[jmp_idx] == TYPE_BR) and jmp_tgt_predecode != tgt + + # self.fault_type = [] + # self.fixed_tgt = [] + + for i in range(len(jal_errs)): + res2.faults[i] = (JAL_FAULT if jal_errs[i] else \ + JALR_FAULT if jalr_errs[i] else \ + RET_FAULT if ret_errs[i] else \ + TGT_FAULT if tgt_errs[i] else \ + NON_CFI_FAULT if non_cfi_errs[i] else \ + INVALID_TAKEN_FAULT if invalid_errs[i] else \ + NO_FAULT + ) + res2.miss_pred[i] = (res2.faults[i] != NO_FAULT) + cur_jmp_tgt = (pcs[i] + decode_jmp_offs[i]) & ((1 << 50) -1) + res2.jmp_tgts[i] = cur_jmp_tgt + if jal_errs[i] or tgt_errs[i]: + res2.fixed_tgts[i] = cur_jmp_tgt + else: + seq_tgt = pcs[i] + (2 if (rvcs[i] or not instr_valids[i]) else 4) + res2.fixed_tgts[i] = seq_tgt + res2.fixed_tgts[i] &= (1 << 50) -1 + + return res1, res2 + + + # async def agent_pred_check(self, ftqValid, ftqOffBits, instrRange, instrValid, jumpOffset, pc, pds, tgt, fire): + + # returning: whether fault exists and the true position; -1 means no + def check_num_errs(self, num_list:list[int], tgt_val): + return [tgt_val == x for x in num_list] + + def jmp_type_err_check(self, idxs, whether_jmp, jmp_instr_offset, ranges, valids): + return [ranges[i] and valids[i] and idxs[i] and ((not whether_jmp) or ((i < jmp_instr_offset) and whether_jmp)) for i in range(len(idxs))] + + + # def check_empty + + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/predecode_ref.py b/ut_frontend/ifu/ifu_top/env/predecode_ref.py new file mode 100644 index 00000000..a4c18af8 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/predecode_ref.py @@ -0,0 +1,77 @@ +from ..instr_utils import fetch, concat,get_cfi_type, if_call, if_ret, is_rvc +from ..datadef import PreDecodeDataDef, F3PreDecodeData + + + +class F3PredecoderRef(): + + def f3_predecode(self, instrs: list[int]) -> F3PreDecodeData: + ret = F3PreDecodeData() + for i in range(16): + instr = instrs[i] + ret.brTypes.append(get_cfi_type(instr)) + ret.isCalls.append(1 if if_call(instr, ret.brTypes[i]) else 0) + ret.isRets.append(1 if if_ret(instr, ret.brTypes[i]) else 0) + return ret + + +class PredecodeRef(): + def predecode(self, instrs: list[int]) -> PreDecodeDataDef: + ret = PreDecodeDataDef() + + for i in range(16): + ret.new_instrs.append(instrs[i] | (instrs[i+1] << 16)) + ret.rvcs.append(is_rvc(ret.new_instrs[i])) + ret.jmp_offsets.append(self.calc_imm(ret.new_instrs[i])) + ret.valid_starts.append(False) + ret.half_valid_starts.append(False) + + if i == 0: + ret.valid_starts[i] = True + ret.half_valid_starts[i] = False + elif i == 1: + ret.half_valid_starts[i] = True + ret.valid_starts[i] = ret.rvcs[0] + else: + if ret.half_valid_starts[i-1] == True: + ret.half_valid_starts[i] = ret.rvcs[i-1] + else: + ret.half_valid_starts[i] = True + + if ret.valid_starts[i-1] == True: + ret.valid_starts[i] = ret.rvcs[i-1] + else: + ret.valid_starts[i] = True + return ret + + def extend_to_64(self, value, bits): + value &= (1 << bits) - 1 # 截断到 bits 位 + sign_bit = 1 << (bits - 1) + if value & sign_bit: + # 手动补全高位 1,但仍保持非负 Python int(模拟补码) + value |= ((-1) << bits) & ((1 << 64) - 1) + return value + + + def calc_imm(self, instr): + + op = fetch(instr, 0, 1) + funct = fetch(instr, 13, 15) + + if op < 3: # C.J or beq + + if op == 1 and (funct == 6 or funct == 7): # beq + imm = concat(instr, [[12], [5, 6], [2], [10, 11], [3, 4]]) << 1 + return self.extend_to_64(imm, 9) + # if funct == 5: # C.J + return self.extend_to_64(concat(instr, [[12], [8], [9, 10], [6],[7], [2], [11], [3, 5]]) << 1, 12) + + rvi_funct = fetch(instr, 0, 6) + + if rvi_funct == 99: # 1100011 b + return self.extend_to_64(concat(instr, [[31], [7], [25, 30], [8, 11]]) << 1, 13) + + # if rvi_funct == 111: # "1101111 JAL": + return self.extend_to_64(concat(instr, [[31], [12, 19], [20], [21, 30]]) << 1, 21) + + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/rvc_expander_ref.py b/ut_frontend/ifu/ifu_top/env/rvc_expander_ref.py new file mode 100644 index 00000000..fbb58789 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/rvc_expander_ref.py @@ -0,0 +1,329 @@ +# 这个函数用于对rvc 扩展的子功能提供支持,具体分支解释如下: +def rvc_expand_ref(rvc_instr,fsIsOff): + ill = 0 + expanded = rvc_instr + rvc_instr_16bit = rvc_instr & 0xFFFF + opcode = rvc_instr_16bit & 0b11 + if opcode == 0b00: + # C.ADDI4SPN指令 + if (rvc_instr_16bit & 0xE003) == 0x0000: + nzuimm = (((rvc_instr_16bit >> 5) & 0x3C) << 4) | (((rvc_instr_16bit >> 5) & 0x1) << 3) | (((rvc_instr_16bit >> 5) & 0x2) << 1) | (((rvc_instr_16bit >> 5) & 0xC0) >> 2) + rd = 8 + ((rvc_instr_16bit >> 2) & 0x7) + expanded = 0x00000013 | (nzuimm << 20) | (rd << 7) |(2<<15) # ADDI + if nzuimm == 0: + ill = 1 + else : + ill = 0 + return expanded,ill #"C.ADDI4SPN -> ADDI" + # C.FLD + elif (rvc_instr_16bit & 0xE003) == 0x2000: + rd = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x3) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2) + expanded = 0x00000007 | (nzuimm << 20) | (rd << 7) |(0b011 <<12) |(rs1 << 15) # ADDI + if(fsIsOff==True): + ill=1 + else: + ill=0 + return expanded,ill + #C.lw lw的格式形如: | imm[11:0] | rs1 | 010 | rd | 0000011 | + elif (rvc_instr_16bit & 0xE003) == 0x4000: + rd = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x1) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2)| (((rvc_instr_16bit >> 5) & 0x2) << 1) + expanded = 0x00000003 | (nzuimm << 20) | (rd << 7) |(0b010 <<12) |(rs1 << 15) # ADDI + return expanded,ill + #C.ld + elif (rvc_instr_16bit & 0xE003) == 0x6000: + rd2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x3) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2) + expanded = 0x00000003 | (nzuimm << 20) | (rd2 << 7) |(0b011 <<12) |(rs1 << 15) # ADDI + return expanded,ill + #c.lbu/lhu/lh/sb/sh + elif (rvc_instr_16bit & 0xE003) == 0x8000: + rs2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + fun6 = (rvc_instr_16bit>>10)&0x3F + if(fun6==0b100000):#lbu + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) | ((rvc_instr_16bit >> 6) & 0x1) + expanded = 0x00000003 | (rs2 << 7) | (0b100 <<12) | (rs1 << 15) |(nzuimm << 20) # + return expanded,ill + elif(fun6==0b100001)&(((rvc_instr_16bit >> 6) & 0x1)==0):#lhu + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) + expanded = 0x00000003 | (rs2 << 7) | (0b101 <<12) | (rs1 << 15) |(nzuimm << 20) # + return expanded,ill + elif(fun6==0b100001)&(((rvc_instr_16bit >> 6) & 0x1)==1):#lh + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) + expanded = 0x00000003 | (rs2 << 7) | (0b001 <<12) | (rs1 << 15) |(nzuimm << 20) # + return expanded,ill + elif(fun6==0b100010):#sb + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) | ((rvc_instr_16bit >> 6) & 0x1) + expanded = 0x00000023 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b000 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + return expanded,ill + elif((fun6==0b100011)&(((rvc_instr_16bit >> 6) & 0x1)==0)):#sh + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) + expanded = 0x00000023 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b001 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + return expanded,ill + else: + ill=1 + return expanded,ill + #C.fsd + elif (rvc_instr_16bit & 0xE003) == 0xa000: + rs2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x3) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2) + expanded = 0x00000027 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b011 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + if(fsIsOff==True): + ill=1 + else: + ill=0 + return expanded,ill + + #C.sw RVI的SW格式形如:| imm[11:5]| rs2 | rs1 | 010 | imm[4:0] | 0100011 | + elif (rvc_instr_16bit & 0xE003) == 0xC000: + rs2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x1) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2)| (((rvc_instr_16bit >> 5) & 0x2) << 1) + expanded = 0x00000023 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b010 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + return expanded,ill + #C.sd + elif (rvc_instr_16bit & 0xE003) == 0xE000: + rs2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x3) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2) + expanded = 0x00000023 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b011 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + return expanded,ill + else: + return expanded,ill + + elif opcode == 0b01: + #C.addi + if (rvc_instr_16bit & 0xE003) == 0x0001: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm = 0xFE0 | ((rvc_instr_16bit >> 2) & 0x1F) + else: + nzuimm = 0x000 | ((rvc_instr_16bit >> 2) & 0x1F) + expanded = 0x00000013 | (rd << 7) | (0b000 << 12) |(rs1 << 15) | (nzuimm << 20) # + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x2001: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm = 0xFE0 | ((rvc_instr_16bit >> 2) & 0x1F) + else: + nzuimm = 0x000 | ((rvc_instr_16bit >> 2) & 0x1F) + expanded = 0x0000001B | (rd << 7) | (0b000 << 12) |(rs1 << 15) | (nzuimm << 20) # + if (rd == 0): + ill = 1 + + return expanded,ill + + elif (rvc_instr_16bit & 0xE003) == 0x4001: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm = 0xFE0 | ((rvc_instr_16bit >> 2) & 0x1F) + else: + nzuimm = 0x000 | ((rvc_instr_16bit >> 2) & 0x1F) + expanded = 0x00000013 | (rd << 7) | (0b000 << 12) |(0b00000 << 15) | (nzuimm << 20) # + + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x6001: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + if(rd == 2) or (rd == 0): + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm=0xE00|(((rvc_instr_16bit>>2)&0x1)<<5)|(((rvc_instr_16bit>>3)&0x3)<<7)|(((rvc_instr_16bit>>5)&0x1)<<6)|(((rvc_instr_16bit>>6)&0x1)<<4) + else: + nzuimm=0x000|(((rvc_instr_16bit>>2)&0x1)<<5)|(((rvc_instr_16bit>>3)&0x3)<<7)|(((rvc_instr_16bit>>5)&0x1)<<6)|(((rvc_instr_16bit>>6)&0x1)<<4) + if(nzuimm == 0): + ill = 1 + expanded = 0x00000013 | (rd << 7) | (0b000 << 12) |(rd << 15) | (nzuimm << 20) # + else:#lui lui指令的格式形如: | imm[31:12] | rd | 0110111 | + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm = 0xFFFE0000 | (((rvc_instr_16bit >> 2) & 0x1F) << 12) + else: + nzuimm = 0x00000000 | (((rvc_instr_16bit >> 2) & 0x1F) << 12) + expanded = 0x00000037 | (rd << 7) | (nzuimm) + if(nzuimm == 0): + # ill = 1 + ill = 2 # temperarily exclude this condition + expanded = 0x0000007F + + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x8001: + fun6 = (rvc_instr_16bit>>10) & 0x3F + fun2 = (rvc_instr_16bit>>5 ) & 0x3 + fun3 = ((rvc_instr_16bit >> 2) & 0x7) + rd = 8+((rvc_instr_16bit >> 7) & 0x7) + rs2 = 8+((rvc_instr_16bit >> 2) & 0x7) + nzuimm = (rvc_instr_16bit>>2)&0x1F + if(fun6 == 0x20):#SRLI + expanded = 0x00000013 | (rd << 7) |0b101<<12| (rd << 15)| (nzuimm<<20) + return expanded,ill + elif(fun6 == 0x24):#SRLI + expanded = 0x02000013 | (rd << 7) |0b101<<12| (rd << 15)| (nzuimm<<20) + return expanded,ill + elif(fun6 == 0x21):#SRAI + expanded = 0x00000013 | (rd << 7) |0b101<<12| (rd << 15)| (nzuimm<<20)|(0b0100000<<25) + return expanded,ill + elif(fun6 == 0x25):#SRAI + expanded = 0x00000013 | (rd << 7) |0b101<<12| (rd << 15)| (nzuimm<<20)|(0b0100001<<25) + return expanded,ill + elif(fun6 == 0x22):#andi + expanded = 0x00000013 | (rd << 7) |0b111<<12| (rd << 15)| (nzuimm<<20) + return expanded,ill + elif(fun6 == 0x26):#andi + expanded = 0x00000013 | (rd << 7) |0b111<<12| (rd << 15)| (nzuimm<<20)|(0b1111111<<25) + return expanded,ill + elif(fun6 == 0x23):#sub/xor/or/and + if(fun2 == 0b00):#sub + expanded = 0x00000033 | (rd << 7) |0b000<<12| (rd << 15)| (rs2<<20)|(0b0100000<<25) + return expanded,ill + elif(fun2 == 0b01):#xor + expanded = 0x00000033 | (rd << 7) |0b100<<12| (rd << 15)| (rs2<<20)|(0b0000000<<25) + return expanded,ill + elif(fun2 == 0b10):#or + expanded = 0x00000033 | (rd << 7) |0b110<<12| (rd << 15)| (rs2<<20)|(0b0000000<<25) + return expanded,ill + elif(fun2 == 0b11): + expanded = 0x00000033 | (rd << 7) |0b111<<12| (rd << 15)| (rs2<<20)|(0b0000000<<25) + return expanded,ill + elif(fun6 == 0x27):# C.subw/addw/mul/not/zext.b/sext.b/zext.h/sext.h/zext.w/ + if(fun2 == 0b00):#subw + expanded = 0x0000003B | (rd << 7) |0b000<<12| (rd << 15)| (rs2<<20)|(0b0100000<<25) + return expanded,ill + elif(fun2 == 0b01):#addw + expanded = 0x0000003B | (rd << 7) |0b000<<12| (rd << 15)| (rs2<<20)|(0b0000000<<25) + return expanded,ill + elif(fun2 == 0b10):#mul + expanded = 0x00000033 | (rd << 7) |0b000<<12| (rd << 15)| (rs2<<20)|(0b0000001<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b000):#zext.b + expanded = 0x0ff00013 | (rd << 7) |0b111<<12| (rd << 15) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b001):#sext.b + expanded = 0x00000013 | (rd << 7) |0b001<<12| (rd << 15)|(0b00100<<20)|(0b0110000<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b010):#zext.h + expanded = 0x0000003B | (rd << 7) |0b100<<12| (rd << 15)|(0b00000<<20)|(0b0000100<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b011):#sext.h + expanded = 0x00000013 | (rd << 7) |0b001<<12| (rd << 15)|(0b00101<<20)|(0b0110000<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b100):#zext.w + expanded = 0x0000003B | (rd << 7) |0b000<<12| (rd << 15)|(0b00000<<20)|(0b0000100<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b101):#not + expanded = 0xfff00013 | (rd << 7) |0b100<<12| (rd << 15)|(0b00000<<20) + return expanded,ill + else: + ill =2 + return expanded,ill + else: + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xa001: + nzuimm = 0xFFFE0000|(((rvc_instr_16bit>>2)&0x1)<<5)|(((rvc_instr_16bit>>2)&0xe))|(((rvc_instr_16bit>>2)&0x10)<<3)|(((rvc_instr_16bit>>2)&0x20)<<1)|(((rvc_instr_16bit>>2)&0x40)<<4)|(((rvc_instr_16bit>>2)&0x180)<<1)|(((rvc_instr_16bit>>2)&0x200)>>5)|(((rvc_instr_16bit>>2)&0x400)<<1) + if(((rvc_instr_16bit>>2)&0x400)>>10): + expanded = 0x800FF06F|(0b00000 << 7)|((nzuimm &0x7fe) <<20)|((nzuimm &0x800) <<9)# + else: + expanded = 0x0000006F|(0b00000 << 7)|((nzuimm &0x7fe) <<20)|((nzuimm &0x800) <<9)# + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xc001: + rs1 = ((rvc_instr_16bit >> 7) & 0x7)+8 + nzuimm = 0xFF & ((((rvc_instr_16bit>>2)&0x1)<<4)|((((rvc_instr_16bit>>2)&0x6)>>1))|(((rvc_instr_16bit>>2)&0x18)<<2)|(((rvc_instr_16bit>>2)&0x300)>>6)|(((rvc_instr_16bit>>2)&0x400)>>3)) + if((((rvc_instr_16bit>>2)&0x400)>>10)): + expanded = 0xF00000E3|((nzuimm&0x0F)<<8)|rs1<<15|((nzuimm&0xF0)<<21) + else: + expanded = 0x00000063|((nzuimm&0x0F)<<8)|rs1<<15|((nzuimm&0xF0)<<21) + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xe001: + rs1 = ((rvc_instr_16bit >> 7) & 0x7)+8 + nzuimm = 0xFF & ((((rvc_instr_16bit>>2)&0x1)<<4)|((((rvc_instr_16bit>>2)&0x6)>>1))|(((rvc_instr_16bit>>2)&0x18)<<2)|(((rvc_instr_16bit>>2)&0x300)>>6)|(((rvc_instr_16bit>>2)&0x400)>>3)) + if((((rvc_instr_16bit>>2)&0x400)>>10)): + expanded = 0xF00000E3|((nzuimm&0x0F)<<8)|0x001<<12|rs1<<15|((nzuimm&0xF0)<<21) + else: + expanded = 0x00000063|((nzuimm&0x0F)<<8)|0x001<<12|rs1<<15|((nzuimm&0xF0)<<21) + return expanded,ill + elif opcode == 0b10: + if (rvc_instr_16bit & 0xE003) == 0x0002: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + + nzuimm = 0x000 | ((rvc_instr_16bit >> 2) & 0x1F) | ((rvc_instr_16bit >> 7) & 0x20) + expanded = 0x00000013 | (rd << 7) | (0b001 << 12) |(rs1 << 15) | (nzuimm << 20) # + + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x2002:#fldsp + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + + nzuimm = (((rvc_instr_16bit >> 2) & 0x7) <<6) | ((rvc_instr_16bit >> 7) & 0x20) | ((rvc_instr_16bit >> 2) & 0x18) + expanded = 0x00000007 | (rd << 7) | (0b011 << 12) |(0b00010 << 15) | (nzuimm << 20) # + if(fsIsOff==True): + ill=1 + else: + ill=0 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x4002:#lwsp + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + + nzuimm = (((rvc_instr_16bit >> 2) & 0x3) <<6) | ((rvc_instr_16bit >> 7) & 0x20) | ((rvc_instr_16bit >> 2) & 0x1c) + expanded = 0x00000003 | (rd << 7) | (0b010 << 12) |(0b00010 << 15) | (nzuimm << 20) # + if (rd == 0): + ill = 1 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x6002:#ldsp + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + + nzuimm = (((rvc_instr_16bit >> 2) & 0x7) <<6) | ((rvc_instr_16bit >> 7) & 0x20) | ((rvc_instr_16bit >> 2) & 0x18) + expanded = 0x00000003 | (rd << 7) | (0b011 << 12) |(0b00010 << 15) | (nzuimm << 20) # + if (rd == 0): + ill = 1 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x8002:#JR/MV/EBREAK/JALR/ADD + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + rs2 = ((rvc_instr_16bit >> 2) & 0x1F) + funct4 = (rvc_instr_16bit>>12)&0x1 + nzuimm = (((rvc_instr_16bit >> 2) & 0x7) <<6) | ((rvc_instr_16bit >> 7) & 0x20) | ((rvc_instr_16bit >> 2) & 0x18) + if((funct4==0)&(rs2==0)&(rs1!=0)): #JR + expanded = 0x00000067 | (0b00000 << 7) | (0b000 << 12) |(rs1 << 15) # + elif(funct4==0)&(rs2!=0):#&(rs1!=0)):#MV 0000000 rs2 rs1 000 rd 0110011 ADD + expanded = 0x00000013 | (rs1 << 7) | (0b000 << 12) |(rs2 << 15) # + elif((funct4==1)&(rs2==0)&(rs1==0)): #ebreak + expanded = 0b00000000000100000000000001110011 + elif((funct4==1)&(rs2==0)&(rs1!=0)): #jalr + expanded = 0x00000067 | (0b00001 << 7) | (0b000 << 12) |(rs1 << 15) # + elif(funct4==1)&(rs2!=0):#&(rs1!=0)): #add + expanded = 0x00000033 | (rs1 << 7) | (0b000 << 12) |(rs1 << 15) |(rs2 << 20) # + else: + ill = 1 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xa002:#fsdsp + rs2 = ((rvc_instr_16bit >> 2) & 0x1F) + nzuimm = ((rvc_instr_16bit >> 1) & 0x1C0) | ((rvc_instr_16bit >> 7) & 0x38) + expanded = 0x00000027 |((nzuimm & 0x1F)<<7)| (0b011 << 12 ) | (0b00010 << 15)| (rs2 << 20)| ((nzuimm&0xFE0)<< 20) # + if(fsIsOff==True): + ill=1 + else: + ill=0 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xc002:#swsp + rs2 = ((rvc_instr_16bit >> 2) & 0x1F) + nzuimm = ((rvc_instr_16bit >> 1) & 0xC0) | ((rvc_instr_16bit >> 7) & 0x3C) + expanded = 0x00000023 |((nzuimm & 0x1F)<<7)| (0b010 << 12 ) | (0b00010 << 15)| (rs2 << 20)| ((nzuimm&0xFE0)<< 20) # + + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xE002:#sdsp + rs2 = ((rvc_instr_16bit >> 2) & 0x1F) + nzuimm = ((rvc_instr_16bit >> 1) & 0x1C0) | ((rvc_instr_16bit >> 7) & 0x38) + expanded = 0x00000023 |((nzuimm & 0x1F)<<7)| (0b011 << 12 ) | (0b00010 << 15)| (rs2 << 20)| ((nzuimm&0xFE0)<< 20) # + + return expanded,ill + else: + return expanded,ill + else: + return rvc_instr, False \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/instr_utils.py b/ut_frontend/ifu/ifu_top/instr_utils.py new file mode 100644 index 00000000..a5533bf3 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/instr_utils.py @@ -0,0 +1,275 @@ +from .commons import PREDICT_WIDTH, randbool, calc_cut_ptr +from random import randint + +#s fetch [start, end] bits from num +def fetch(num, start, end): + shift_left = (1 << (end - start + 1)) - 1 + return (num >> start) & shift_left + +# pass in offs like [[], [], []], works similar to cat() in chisel +# 靠前的off在高位 +def concat(num, offs): + ret = 0 + for off in offs: + start = off[0] + if len(off) == 1: + end = off[0] + elif len(off) == 2: + end = off[1] + move = end -start + 1 + ret <<= move + mid = fetch(num, start, end) + ret += mid + return ret + +def get_cfi_type(instr): + op = fetch(instr, 0, 1) + if op == 3: # RVI + funct = fetch(instr, 0, 6) + if funct == 99: # branch + return 1 + + if funct == 111: # jal + return 2 + + if funct == 103: # maybe jalr + mids = fetch(instr, 12, 14) + if mids == 0: + return 3 + return 0 + else: # RVC + funct = fetch(instr, 13, 15) + if (funct == 6 or funct == 7) and op == 1:# c.beqz c.bnez + return 1 + + if funct == 5 and op == 1: # c.j + return 2 + + if funct == 4 and op == 2: + rs2 = fetch(instr, 2, 6) + rs1 = fetch(instr, 7, 11) + if rs2 == 0 and rs1 != 0: # c.jalr + return 3 + + if rs2 == 0 and rs1 == 0: # c.ebreak + return 0 + return 0 + + +def if_call(instr, cfi): + if cfi < 2 or cfi > 3: + return False + + op = fetch(instr, 0, 1) + if cfi == 2: + if op == 3: + rd = fetch(instr, 7, 11) + if rd == 1 or rd == 5: + return True + return False + + # cfi == 3 + if op == 2: + return fetch(instr, 12, 12) == 1 + elif op < 2 or op > 3: + return False + + rd = fetch(instr, 7, 11) + return rd == 1 or rd == 5 + +def if_ret(instr, cfi): + if cfi != 3: + return False + + op = fetch(instr, 0, 1) + if op == 3: # rvi + rd = fetch(instr, 7, 11) + rs = fetch(instr, 15, 19) + return rd != 1 and rd !=5 and (rs == 1 or rs ==5) + + if op != 2: + return False + + if fetch(instr, 12, 12) == 1: + return False + + rs = fetch(instr, 7, 11) + + return rs == 1 or rs == 5 + + +def construct_brs(rvc, imm = -1): + if rvc: + if imm == -1: + return (3 << 14 ) | (randint(0, 1) << 13) | (randint(0, (1 << 11) - 1) << 2) | 1 + replay1 = concat(imm, [[8], [3, 4]]) + replay2 = concat(imm, [[6, 7], [1,2], [5]]) + return (randint(6, 7) << 13) | (replay1 << 10) | (randint(0, 7) << 7)|(replay2 << 2 )| 1 + else: + opcode = 0b1100011 + if imm == -1: + return (randint(0, (1 << 25)-1 ) << 7) | opcode + imm_hi = concat(imm, [[12], [5, 10]]) # 1 + 6 = 7 bits + # 低位域 [11:7] <- imm[4:1|11] + imm_lo = concat(imm, [[1, 4], [11]]) # 4 + 1 = 5 bits + + funct3 = 0b000 + opcode = 0b1100011 + + return (imm_hi << 25) \ + | (randint(0, 31) << 20) \ + | (randint(0, 31) << 15) \ + | (funct3 << 12) \ + | (imm_lo << 7) \ + | opcode + +def construct_jal(rvc, imm=-1): + if rvc: + if imm == -1: + return (5 << 13) | (randint(0, (1 << 11) - 1 ) << 2) | 1 + replay = concat(imm, [[11], [4], [8, 9], [10], [6], [7], [1, 3], [5]]) + return (5 << 13) | (replay << 2 ) | 1 + opcode = 0b1101111 + if imm == -1: + return (randint(0, (1 << 25) -1 ) << 7) | opcode + replay = concat(imm, [[20], [1, 10], [11], [12, 19]]) + instr = (imm << 12) | (randint(0, 31) << 7) | opcode + return instr + +def construct_jalr(rvc): + if rvc: + rvc_jalr = (4 << 13) | (randint(0, 63) << 7) | 2 + return rvc_jalr + + return (randint(0, (1 << 17) - 1) << 15) | (randint(0, 31) << 7) | 103 + +def construct_ret(rvc): + rs = 1 if randbool() else 5 + if rvc: + return (8 << 12) | rs << 7 | 2 + rd = 3 + return (randint(0, (1 << 12) - 1) << 20) | ( rs<< 15)| (rd << 7) | 103 + + +def construct_non_cfis(rvc=False, rvc_enabled=False): + while True: + if (rvc_enabled): + if rvc: + instr = (randint(0, (1 << 14) - 1) << 2) | randint(0, 2) + else: + instr = (randint(0, (1 << 14) - 1) << 2) | 3 + else: + instr = randint(0, (1 << 32) - 1) + if get_cfi_type(instr) == 0: + return instr + +def construct_instrs(instr_type, rvc=False, rvc_enabled=False, target_imm = -1): + if (instr_type == 0): return construct_non_cfis(rvc, rvc_enabled) + if (instr_type == 1): return construct_brs(rvc, imm=target_imm) + if (instr_type == 2): return construct_jal(rvc, imm=target_imm) + return construct_jalr(rvc) + +def is_rvc(concate_instr): + return (concate_instr & 3) != 3 + +def construct_instrs_with_jump_idx(end_idx, cfi_res=-1, imm=-1, last_finished=True, invalid_jmp_prediction=False): + """ + params + - end_idx: the last idx of the seq, either end of full 'instrs' or jump idx + - cfi_res: the given instr type, if -1, will select one from (1, 3) + - imm: jump target to gen instr + - last_finished: if the prediction block will end, or leaving the res to next prediction block + - invalid_jmp_prediction: decide if the end idx is a valid instr(will be override by last finished check) + + returns: + - 17 16-bit raw instrs + """ + instrs = [ 0 for _ in range(PREDICT_WIDTH+1)] + seq_end_idx = max(0, min(end_idx, PREDICT_WIDTH-1)) + for i in range(seq_end_idx): + # here we have to ensure that the last instr before jump/15th instr is ended correctly + # or if we want to detect invalid prediction, we have to let jump instr to be higher place of 32bit instr + if i < seq_end_idx - 2: + is_rvc = randbool() + elif i == seq_end_idx - 2: + is_rvc = invalid_jmp_prediction and seq_end_idx != PREDICT_WIDTH -1 + else: + # can not influence last finished + is_rvc = (not invalid_jmp_prediction) or seq_end_idx == PREDICT_WIDTH - 1 + res = construct_non_cfis(rvc=is_rvc, rvc_enabled=True) + instrs[i] = res & ((1 << 16) - 1) + if not is_rvc: + i += 1 + instrs[i] =(res >> 16) & ((1 << 16)-1) + + for i in range(seq_end_idx, PREDICT_WIDTH+1): + if i == seq_end_idx: + cfi_type = randint(1, 3) if cfi_res == -1 else cfi_res + cur_imm = imm + else: + cfi_type = randint(0, 3) + cur_imm = -1 + + # here we ensures cross-prediction-block instr selection + if i == PREDICT_WIDTH - 1: + if last_finished: + is_rvc = True + else: + is_rvc = False + else: + is_rvc = randbool() + res = construct_instrs(cfi_type, rvc=is_rvc, rvc_enabled=True, target_imm=cur_imm) if 0 <= cfi_type < 4 else construct_ret(rvc=is_rvc) + instrs[i] = res & ((1 << 16) - 1) + if not is_rvc: + i += 1 + if i <= PREDICT_WIDTH: + instrs[i] =(res >> 16) & ((1 << 16)-1) + return instrs + +# def rebuild_cacheline_from_parts(next_start, instrs): +# """ +# 用给定的 idx 列表和对应的 16bit 指令值,重建一个 1024bit 缓存行; +# 未提供的槽位用 0 填充。 +# """ +# cut_ptrs = calc_cut_ptr(next_start) +# assert len(cut_ptrs) == len(instrs) +# cacheline = 0 +# for idx, instr in zip(cut_ptrs, instrs): +# assert 0 <= idx < 64 +# assert 0 <= instr <= 0xFFFF +# pos = idx * 16 +# cacheline |= (instr & 0xFFFF) << pos +# print(len(cut_ptrs)) +# print(f"res: {hex(cacheline)}") +# low = cacheline & ((1 << 512) - 1) +# high = (cacheline >> 512) & ((1 << 512) - 1) +# res = low | high +# print(f"cacheline: {hex(res)}") +# return res + +SLOT_BITS=16 +PERIOD_SLOTS=32 + +def rebuild_cacheline_from_parts(start_addr, instrs: list[int]) -> int: + cut_ptrs: list[int] = calc_cut_ptr(start_addr) + """ + 逆向 Cat(512b, 512b)+cut:把 (idx, 16-bit) 写回到一个 512b cacheline(32槽)中。 + 规则:slot = idx % 32;重复命中时后写覆盖(可换策略)。 + 返回:单条 512-bit cacheline 的整数表示。 + """ + assert len(cut_ptrs) == len(instrs) + line = 0 + # 先清空被写入过的位置(可选,但安全) + written_mask = 0 + for idx, ins in zip(cut_ptrs, instrs): + assert 0 <= ins <= 0xFFFF + j = idx % PERIOD_SLOTS + pos = j * SLOT_BITS + # 清空该槽位 + line &= ~((0xFFFF) << pos) + # 写入该槽位 + line |= (ins & 0xFFFF) << pos + written_mask |= 1 << j + # print(f"cacheline_place_instrs: {hex(line)}") + return line + diff --git a/ut_frontend/ifu/ifu_top/test/ckpt_mmio.py b/ut_frontend/ifu/ifu_top/test/ckpt_mmio.py new file mode 100644 index 00000000..b89a7374 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/test/ckpt_mmio.py @@ -0,0 +1,75 @@ +from ..env import IFUTopEnv +from toffee.funcov import CovGroup +from ..datadef import MMIOState +from comm import UT_FCOV + + +mmio_transfer = [ + ("not_in_mmio", MMIOState.STATE_IDLE, MMIOState.STATE_IDLE), + ("not_nc_so_wait", MMIOState.STATE_IDLE, MMIOState.STATE_WAIT_LAST_CMT), + ("nc_so_send", MMIOState.STATE_IDLE, MMIOState.STATE_SEND_REQ), + ("wait_last_finished", MMIOState.STATE_WAIT_LAST_CMT, MMIOState.STATE_SEND_REQ), + ("waiting_for_last", MMIOState.STATE_WAIT_LAST_CMT, MMIOState.STATE_WAIT_LAST_CMT), + ("waiting_sending", MMIOState.STATE_SEND_REQ, MMIOState.STATE_SEND_REQ), + ("send_to_uncache", MMIOState.STATE_SEND_REQ, MMIOState.STATE_WAIT_RESP), + ("waiting_uncache_resp", MMIOState.STATE_WAIT_RESP, MMIOState.STATE_WAIT_RESP), + ("uncache_once_finished", MMIOState.STATE_WAIT_RESP, MMIOState.STATE_WAIT_COMMIT), + ("need_resend", MMIOState.STATE_WAIT_RESP, MMIOState.STATE_SEND_TLB), + ("waiting_sending_tlb", MMIOState.STATE_SEND_TLB, MMIOState.STATE_SEND_TLB), + ("send_to_tlb", MMIOState.STATE_SEND_TLB, MMIOState.STATE_TLB_RESP), + ("waiting_tlb_resp", MMIOState.STATE_TLB_RESP, MMIOState.STATE_TLB_RESP), + ("tlb_resp_exception", MMIOState.STATE_TLB_RESP, MMIOState.STATE_WAIT_COMMIT), + ("able_to_send_pmp", MMIOState.STATE_TLB_RESP, MMIOState.STATE_SEND_PMP), + ("pmp_resp_exception", MMIOState.STATE_SEND_PMP, MMIOState.STATE_WAIT_COMMIT), + ("going_to_resend", MMIOState.STATE_SEND_PMP, MMIOState.STATE_RESEND_REQ), + ("waiting_resend", MMIOState.STATE_RESEND_REQ, MMIOState.STATE_RESEND_REQ), + ("resend_to_uncache", MMIOState.STATE_RESEND_REQ, MMIOState.STATE_WAIT_RESEND_RESP), + ("waiting_uncache_resp", MMIOState.STATE_WAIT_RESEND_RESP, MMIOState.STATE_WAIT_RESEND_RESP), + ("resend_commit", MMIOState.STATE_WAIT_RESEND_RESP, MMIOState.STATE_WAIT_COMMIT), + ("committing", MMIOState.STATE_WAIT_COMMIT, MMIOState.STATE_WAIT_COMMIT), + ("commited", MMIOState.STATE_WAIT_COMMIT, MMIOState.STATE_COMMITED) +] + +def is_provider_start_end_state(start_state, end_state): + def provider_start_end_state(env: IFUTopEnv) -> bool: + return env.top_agent.last_state == start_state and env.top_agent.cur_state == end_state + return provider_start_end_state + +def is_provider_mmio_rvc(rvc): + def provider_mmio_rvc(env: IFUTopEnv) -> bool: + return env.top_agent.cur_state == MMIOState.STATE_WAIT_COMMIT and env.top_agent.toIbufferAll._toIbuffer._bits._pd[0]._isRVC.value == rvc + return provider_mmio_rvc + +def is_provider_first_instr(is_first): + def provider_first_instr(env: IFUTopEnv) -> bool: + return env.top_agent.top.internal_wires._is_first_instr.value == is_first + return provider_first_instr + +def get_coverage_group_mmio(env: IFUTopEnv): + group = CovGroup(UT_FCOV("../UT_IFU_TOP")) + + group.add_watch_point(env, { + "_first_instr_unused": is_provider_first_instr(True), + "_first_instr_used": is_provider_first_instr(False) + }, name="first_instrs") + + group.add_watch_point(env, + {f"_{desc}": is_provider_start_end_state(start, end) for (desc, start, end) in mmio_transfer} + , name="state_transfer") + + group.add_watch_point(env, + {f"_rvc_is_{i}": is_provider_mmio_rvc(i) for i in range(2)} + , name="seq_target") + + # group.mark_function("state_transfer", get_all_test_func_lists) + # group.mark_function("seq_target", test_random_resends) + + return group + + + + + + + + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/test/ckpt_subs.py b/ut_frontend/ifu/ifu_top/test/ckpt_subs.py new file mode 100644 index 00000000..b70a6f73 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/test/ckpt_subs.py @@ -0,0 +1,122 @@ +from ..env import IFUTopEnv +from ..commons import PREDICT_WIDTH +from toffee.funcov import CovGroup + +from comm import UT_FCOV + +def is_provider_instr_concat(pos): + assert 0 <= pos < PREDICT_WIDTH + def provider_instr_concat(env: IFUTopEnv): + return env.top_agent.top.internal_wires.pre_decoder.out_instrs[pos].value != 0 + return provider_instr_concat + +def is_provider_rvc(pos, rvc_val): + assert 0 <= pos < PREDICT_WIDTH + def provider_instr_concat(env: IFUTopEnv): + return env.top_agent.top.internal_wires.pre_decoder.out_pd_isRVCs[pos].value == rvc_val + return provider_instr_concat + +def is_provider_jmp_off_type(pos, rvc_val, j_type): + assert 0 <= pos < PREDICT_WIDTH + assert 1 <=j_type< 3 + def provider_jmp_off_type(env: IFUTopEnv): + return env.top_agent.top.internal_wires.pre_decoder.out_pd_isRVCs[pos].value == rvc_val \ + and env.top_agent.top.internal_wires.f3_predecoder.out_pd_brTypes[pos].value == j_type + return provider_jmp_off_type + +def is_provider_cfi_type(pos, cfi_type): + assert 0 <= pos < PREDICT_WIDTH + assert 0 <=cfi_type <= 3 + def provider_jmp_off_type(env: IFUTopEnv): + return env.top_agent.top.internal_wires.f3_predecoder.out_pd_brTypes[pos].value == cfi_type + return provider_jmp_off_type + +def is_provider_jal_ret(pos, rvc, jal_ret_type): + assert 0 <= pos < PREDICT_WIDTH + def provider_jal_ret(env: IFUTopEnv) -> bool: + is_ret = env.top_agent.top.internal_wires.f3_predecoder.out_pd_isRets[pos].value + is_call = env.top_agent.top.internal_wires.f3_predecoder.out_pd_isCalls[pos].value + if rvc != env.top_agent.top.internal_wires.pre_decoder.out_pd_isRVCs[pos].value: + return False + if jal_ret_type == 0: + return (not is_ret) and (not is_call) + elif jal_ret_type == 1: + return is_ret + else: + return is_call + return provider_jal_ret + + +def is_provider_valids_selection(choice): + def provider_valids_selection(env: IFUTopEnv): + return env.top_agent.toIbufferAll._toIbuffer._bits._valids[0].value == choice + return provider_valids_selection + + +def is_provider_err_check(pos, err_type): + assert 0 <= pos < PREDICT_WIDTH + assert 0 <= err_type <= 6 + def provider_err_check(env: IFUTopEnv) -> bool: + return env.top_agent.top.internal_wires.pred_checker.fault_types[pos].value == err_type + return provider_err_check + +def is_provider_rvc_expand(rvc, ill): + def provider_rvc_expand(env:IFUTopEnv) ->bool: + if not rvc: + for i in range(PREDICT_WIDTH): + if env.top_agent.top.to_ibuffer_all._toIbuffer._bits._pd[i]._isRVC.value == rvc: + return True + return False + for i in range(PREDICT_WIDTH): + if env.top_agent.top.to_ibuffer_all._toIbuffer._bits._pd[0]._isRVC.value == rvc \ + and env.top_agent.toIbufferAll._toIbuffer._bits._illegalInstr[0].value == ill: + return True + return False + # return env.top_agent.top.to_ibuffer_all._toIbuffer._bits._pd[0]._isRVC.value == rvc \ + # and env.top_agent.toIbufferAll._toIbuffer._bits._illegalInstr[0].value == ill + return provider_rvc_expand + +def get_coverage_group_sub_modules(env: IFUTopEnv): + group = CovGroup(UT_FCOV("../UT_IFU_TOP")) + + group.add_watch_point(env, { + f"_instr_{i}": is_provider_instr_concat(i) for i in range(PREDICT_WIDTH) + }, name="predecode_concat") + + group.add_watch_point(env, { + f"_{i}_{bool(j)}": is_provider_rvc(i, bool(j)) for i in range(PREDICT_WIDTH) for j in range(2) + }, name="predecode_rvc") + + group.add_watch_point(env, { + f"_{i}_{'br' if j == 1 else 'j'}_{'c' if bool(k) else 'i'}": is_provider_jmp_off_type(i, bool(k), j) for i in range(PREDICT_WIDTH) for j in range(1, 3) for k in range(2) + }, name="predecode_jmpoff") + + group.add_watch_point(env, { + f"_{i}_{'non_cfi' if j == 0 else 'br' if j == 1 else 'jal' if j == 2 else 'jalr'}": \ + is_provider_cfi_type(i, j) for i in range(PREDICT_WIDTH) for j in range(4) + }, name="predecode_brtype") + + group.add_watch_point(env,{ + f"_{i}_rv{'c' if bool(j) else 'i'}_{'nop' if k==0 else 'ret' if k == 1 else 'jal'}": \ + is_provider_jal_ret(i, j, k) for i in range(PREDICT_WIDTH) for j in range(2) for k in range(3) + }, name="predecode_jal_ret") + + group.add_watch_point(env, { + "_normal": is_provider_valids_selection(True), + "_last_half": is_provider_valids_selection(False) + }, name="starts") + + errs = ["no", "jal", "ret", "target", "not_cfi", "invalid", "jalr"] + + group.add_watch_point(env, { + f"_{i}_fault_{errs[j]}": is_provider_err_check(i, j) for i in range(PREDICT_WIDTH) for j in range(7) + }, name="check_errs") + + group.add_watch_point(env,{ + f"_rvi": is_provider_rvc_expand(False, False), + f"_rvc_ill": is_provider_rvc_expand(True, True), + f"_rvc_normal": is_provider_rvc_expand(True, False) + }, name="rvc_expand") + + return group + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/test/ckpt_tops.py b/ut_frontend/ifu/ifu_top/test/ckpt_tops.py new file mode 100644 index 00000000..9ed4d55d --- /dev/null +++ b/ut_frontend/ifu/ifu_top/test/ckpt_tops.py @@ -0,0 +1,86 @@ +from ..env import IFUTopEnv +from ..commons import PREDICT_WIDTH +from toffee.funcov import CovGroup +from ..bundle import StageFlushBundle + +from comm import UT_FCOV + +def is_provider_cut_ptr_start(num): + assert 0 <= num < 32 + def provider_cut_ptr_start(env: IFUTopEnv): + return env.top_agent.top.internal_wires.f2_cut_ptrs[0].value == num + return provider_cut_ptr_start + +def is_provider_exception_vec(exception_type, pos): + assert 0 <= pos < PREDICT_WIDTH + assert 0 <= exception_type <= 3 + def provider_exception_vec(env: IFUTopEnv): + return env.top_agent.get_exception_vecs()[pos] == exception_type + return provider_exception_vec + +def is_provider_cross_blk(val): + def provider_cross_blk(env: IFUTopEnv): + return env.top_agent.top.internal_wires._f3_lastHalf_valid.value == val + return provider_cross_blk + +def is_provider_gpaddr_fault(val): + def provider_gpaddr_fault(env: IFUTopEnv): + return env.top_agent.toIbufferAll._toBackend_gpaddrMem._wen.value == val + return provider_gpaddr_fault + +def is_provider_from_bpu_flush(val): + def provider_from_bpu_flush(env: IFUTopEnv): + return env.top_agent.top.internal_wires._f0_flush_from_bpu_probe.value == val + return provider_from_bpu_flush + +def is_provider_icache_ready(val): + def provider_icache_ready(env: IFUTopEnv): + return env.top_agent.top._icacheInterCtrl._icacheInter._icacheReady.value == val + return provider_icache_ready + +# def is_provider_from_bpu_flush(stg, cycled): +# # assert 0 <= stg <= 1 +# def provider_from_bpu_flush(env: IFUTopEnv): +# stgs:list[StageFlushBundle] = [env.top_agent.ftqFlushStgsBundle._s2, env.top_agent.ftqFlushStgsBundle._s3] +# stg_flag = stgs[stg]._bits._flag.value +# assert stg_flag +# return provider_from_bpu_flush + +def is_provider_icache_all_valid(val): + def provider_icahce_all_valid(env: IFUTopEnv): + return env.top_agent.top.internal_wires._icacheRespAllValid.value == val + return provider_icahce_all_valid + +def get_coverage_group_tops(env: IFUTopEnv): + group = CovGroup(UT_FCOV("../UT_IFU_TOP")) + + group.add_watch_point(env, { + f"_start_{i}": is_provider_cut_ptr_start(i) for i in range(32) + }, name="cut_ptr") + + group.add_watch_point(env, { + f"_{i}_type{j}": is_provider_exception_vec(j, i) for i in range(PREDICT_WIDTH) for j in range(4) + }, name="exception") + + group.add_watch_point(env, { + f"_{bool(i)}": is_provider_cross_blk(i) for i in range(2) + }, name="cross_blk") + + group.add_watch_point(env, { + f"_{bool(i)}": is_provider_gpaddr_fault(i) for i in range(2) + }, name="gpaddr_fault") + + group.add_watch_point(env, { + f"_{bool(i)}": is_provider_from_bpu_flush(i) for i in range(2) + }, name="bpu_flush") + + group.add_watch_point(env, { + f"_{bool(i)}": is_provider_icache_ready(i) for i in range(2) + }, name="icache_ready") + + group.add_watch_point(env, { + f"_{bool(i)}": is_provider_icache_all_valid(i) for i in range(2) + }, name="icache_all_valid") + + return group + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/test/mmio_states_test.py b/ut_frontend/ifu/ifu_top/test/mmio_states_test.py new file mode 100644 index 00000000..1a8ba291 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/test/mmio_states_test.py @@ -0,0 +1,271 @@ +from .top_test_fixture import ifu_top_env +import toffee_test +from ..agent import OutsideAgent +from ..datadef import MMIOCycleInfo, MMIOReq, PbmtAssist, MMIOState, mmio_data_logger_instance +from ..env import IFUReceiverModel + +async def state_changing_at_very_beginning(cycle_info: MMIOCycleInfo, top_agent:OutsideAgent, ifu_ref:IFUReceiverModel): + await top_agent.set_up_before_mmio_states(cycle_info) + ifu_ref.set_up_before_mmio_states(cycle_info) + req = MMIOReq() + await top_agent.cmp_single_mmio_req(req, ifu_ref.deal_with_single_mmio_req(req)) + +# use this case to check if the state machine will go to next stage with mmio not enabled +@toffee_test.testcase +async def test_mmio_not_into_mmio(ifu_top_env): + top_agent: OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + mmio_cycle = MMIOCycleInfo() + await state_changing_at_very_beginning(mmio_cycle, top_agent, ifu_ref) + ifu_top_env.mmio_group.mark_function("state_transfer", test_mmio_not_into_mmio) + +# 这个案例用来测试一般情况下first_instr会不会跳过 +@toffee_test.testcase +async def test_mmio_itlb_pmp_and_first_instr(ifu_top_env): + top_agent: OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + mmio_cycle: MMIOCycleInfo = MMIOCycleInfo() + mmio_cycle.icache_pmp_mmios[0] = True + await state_changing_at_very_beginning(mmio_cycle, top_agent, ifu_ref) + mmio_req: MMIOReq = MMIOReq() + await top_agent.cmp_single_mmio_req(mmio_req, ifu_ref.deal_with_single_mmio_req(mmio_req)) + ifu_top_env.mmio_group.mark_function("first_instrs", test_mmio_itlb_pmp_and_first_instr) + +# 这个用例用来测试NC状态下会跳过first_instr +@toffee_test.testcase +async def test_mmio_itlb_nc_skip_first(ifu_top_env): + # 测试设置为NC,是否跳过了first instr + top_agent: OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + mmio_cycle: MMIOCycleInfo = MMIOCycleInfo() + mmio_cycle.icache_itlb_pbmts[0] = PbmtAssist.NC + await top_agent.set_up_before_mmio_states(mmio_cycle) + ifu_ref.set_up_before_mmio_states(mmio_cycle) + mmio_req: MMIOReq = MMIOReq() + mmio_req = MMIOReq() + mmio_req.from_uncache.data = 0x7b7e6081 + mmio_req.from_uncache.valid = True + + mmio_req.to_uncache_ready = True + + mmio_req.rob_commits[0].valid = True + mmio_req.rob_commits[0].ftqIdx = mmio_cycle.ftq_idx + for i in range(4): + await top_agent.cmp_single_mmio_req(mmio_req, ifu_ref.deal_with_single_mmio_req(mmio_req)) + + # print(f"state: {await top_agent.deal_with_single_mmio_req(mmio_req, mmio_data_logger_instance)}") + # 添加一条新的MMIO单请求,判定NC对first instr的跳过是否不影响后续指令的判定 + await top_agent.reset_mmio_state() + mmio_cycle.icache_itlb_pbmts[0] = PbmtAssist.IO + await top_agent.set_up_before_mmio_states(mmio_cycle) + ifu_ref.set_up_before_mmio_states(mmio_cycle) + mmio_req.last_commited = True + for _ in range(2): + await top_agent.cmp_single_mmio_req(mmio_req, ifu_ref.deal_with_single_mmio_req(mmio_req)) + ifu_top_env.mmio_group.mark_function("state_transfer", test_mmio_itlb_nc_skip_first) + + +# 这个检查点检查在IO状态下的请求发送情况(类似NC) +@toffee_test.testcase +async def test_mmio_itlb_io_send_uncache_safe(ifu_top_env): + top_agent: OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + mmio_cycle: MMIOCycleInfo = MMIOCycleInfo() + mmio_cycle.icache_itlb_pbmts[0] = PbmtAssist.IO + await state_changing_at_very_beginning(mmio_cycle, top_agent, ifu_ref) + ifu_top_env.mmio_group.mark_function("state_transfer", test_mmio_itlb_io_send_uncache_safe) + + + +async def double_line_diffs(case, top_agent: OutsideAgent, ifu_ref: IFUReceiverModel): + """这个函数包含两个会引发doubleline异常的case + + Args: + case (_type_): 引发异常的case序号,case 0 表示pmp端口状态不等,case 1 表示itlb pbmt两个端口状态不等 + top_agent (OutsideAgent): 用于驱动的agent + ifu_ref (IFUReceiverModel): 功能ref + """ + mmio_cycle: MMIOCycleInfo = MMIOCycleInfo() + if case == 0: + # for this case, check pmp not equal and causing exception + mmio_cycle.icache_pmp_mmios[0] = True + else: + # for this case check itlb pbmt not equal + mmio_cycle.icache_itlb_pbmts[0] = PbmtAssist.IO + mmio_cycle.ftq_start_addr = 0x576 + await state_changing_at_very_beginning(mmio_cycle, top_agent, ifu_ref) + mmio_req : MMIOReq = MMIOReq() + await top_agent.cmp_single_mmio_req(mmio_req, ifu_ref.deal_with_single_mmio_req(mmio_req)) + await top_agent.reset_mmio_state() + + +# 这个用例测试double line情况下的预测错误 +@toffee_test.testcase +async def test_double_line_diffs(ifu_top_env): + top_agent: OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + await double_line_diffs(0, top_agent, ifu_ref) + await double_line_diffs(1, top_agent, ifu_ref) + ifu_top_env.mmio_group.mark_function("state_transfer", test_double_line_diffs) + +def get_resend_default_cfg(ftq_idx) -> MMIOReq: + """这个函数根据ftq_idx生成一个默认的可以重发的配置 + + Args: + ftq_idx (_type_): ftqquery的ftqidx + + Returns: + MMIOReq: 一个请求,作为重发的基础 + """ + mmio_req: MMIOReq = MMIOReq() + mmio_req.from_uncache.valid = True + mmio_req.from_uncache.data = 0x443 + mmio_req.to_uncache_ready = True + mmio_req.rob_commits[1].ftqIdx = ftq_idx + mmio_req.rob_commits[1].valid = True + return mmio_req + +# 测试向TLB发送异常下的处理情况 +async def resend_send_tlb_excps(case, top_agent: OutsideAgent, ifu_ref:IFUReceiverModel): + mmio_cycle: MMIOCycleInfo = MMIOCycleInfo() + mmio_cycle.icache_itlb_pbmts[0] = PbmtAssist.IO + mmio_cycle.icache_paddr = 0x576 + await state_changing_at_very_beginning(mmio_cycle, top_agent, ifu_ref) + mmio_req: MMIOReq = get_resend_default_cfg(mmio_cycle.ftq_idx) + mmio_req.itlb_req_ready = True + mmio_req.itlb_resp.valid = True + if case == 0: + # for this case, check exception due to pbmt not equal + mmio_req.itlb_resp.pbmt = 0 + else: + # check for exception report by itlb + mmio_req.itlb_resp.pbmt = mmio_cycle.icache_itlb_pbmts[0] + # 设置三种异常独热码 + mmio_req.itlb_resp.excp.set_excp_one_hot(case) + for _ in range(6): + await top_agent.cmp_single_mmio_req(mmio_req, ifu_ref.deal_with_single_mmio_req(mmio_req)) + await top_agent.reset_mmio_state() + +# 测试TLB发送遇异常的情况,包含状态不一致,和ITLB报告异常的情况 +@toffee_test.testcase +async def test_resend_send_tlb_exception(ifu_top_env): + top_agent: OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + for i in range(4): + await resend_send_tlb_excps(i, top_agent, ifu_ref) + ifu_top_env.mmio_group.mark_function("state_transfer", test_resend_send_tlb_exception) + +# 测试重发过程中,pmp遇到异常的情况 +async def resend_send_pmp_excp(case, top_agent: OutsideAgent, ifu_ref: IFUReceiverModel): + mmio_cycle: MMIOCycleInfo = MMIOCycleInfo() + mmio_cycle.icache_itlb_pbmts[0] = PbmtAssist.IO + mmio_cycle.icache_paddr = 0x576 + await state_changing_at_very_beginning(mmio_cycle, top_agent, ifu_ref) + mmio_req : MMIOReq = get_resend_default_cfg(mmio_cycle.ftq_idx) + mmio_req.itlb_req_ready = True + mmio_req.itlb_resp.valid = True + mmio_req.itlb_resp.pbmt = mmio_cycle.icache_itlb_pbmts[0] + if case == 0: + # for this case, pmp.mmio is not equal to that of icache + mmio_req.pmp_resp.mmio = not mmio_cycle.icache_pmp_mmios[0] + else: + # for this case, pmp itself respond the exception + mmio_req.pmp_resp.mmio = mmio_cycle.icache_pmp_mmios[0] + mmio_req.pmp_resp.instr = True + for _ in range(7): + await top_agent.cmp_single_mmio_req(mmio_req, ifu_ref.deal_with_single_mmio_req(mmio_req)) + + await top_agent.reset_mmio_state() + +# pmp重发错误问题 +@toffee_test.testcase +async def test_resend_send_pmp_mismatch(ifu_top_env): + top_agent: OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + await resend_send_pmp_excp(0, top_agent, ifu_ref) + await resend_send_pmp_excp(1, top_agent, ifu_ref) + ifu_top_env.mmio_group.mark_function("state_transfer", test_resend_send_pmp_mismatch) + + +import random + +async def rand_run_times(mmio_req: MMIOReq, top_agent: OutsideAgent, ifu_ref: IFUReceiverModel): + for _ in range(random.randint(0, 4)): + await top_agent.cmp_single_mmio_req(mmio_req, ifu_ref.deal_with_single_mmio_req(mmio_req)) + +# 这个测试用例对应一次成功的重发,中间存在若干次停顿 +@toffee_test.testcase +async def test_resend_normal(ifu_top_env): + top_agent: OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + mmio_cycle: MMIOCycleInfo = MMIOCycleInfo() + mmio_cycle.icache_itlb_pbmts[0] = PbmtAssist.IO + mmio_cycle.icache_paddr = 0x576 + await state_changing_at_very_beginning(mmio_cycle, top_agent, ifu_ref) + mmio_req : MMIOReq = get_resend_default_cfg(mmio_cycle.ftq_idx) + mmio_req.itlb_req_ready = True + mmio_req.itlb_resp.valid = True + mmio_req.itlb_resp.pbmt = mmio_cycle.icache_itlb_pbmts[0] + mmio_req.pmp_resp.mmio = mmio_cycle.icache_pmp_mmios[0] + mmio_req.to_uncache_ready = False + mmio_req.from_uncache.valid = False + mmio_req.itlb_req_ready = False + mmio_req.itlb_resp.valid = False + await rand_run_times(mmio_req, top_agent, ifu_ref) + mmio_req.to_uncache_ready = True + await rand_run_times(mmio_req, top_agent, ifu_ref) + mmio_req.from_uncache.valid = True + await rand_run_times(mmio_req, top_agent, ifu_ref) + mmio_req.itlb_req_ready = True + mmio_req.to_uncache_ready = False + mmio_req.from_uncache.valid = False + await rand_run_times(mmio_req, top_agent, ifu_ref) + mmio_req.itlb_resp.valid = True + mmio_req.rob_commits[1].valid = False + + await rand_run_times(mmio_req, top_agent, ifu_ref) + mmio_req.to_uncache_ready = True + await rand_run_times(mmio_req, top_agent, ifu_ref) + mmio_req.from_uncache.valid = True + await rand_run_times(mmio_req, top_agent, ifu_ref) + mmio_req.rob_commits[1].valid = True + for _ in range(2): + await top_agent.cmp_single_mmio_req(mmio_req, ifu_ref.deal_with_single_mmio_req(mmio_req)) + # print(f"first_instr: {top_agent.top.internal_wires._is_first_instr.value}") + ifu_top_env.mmio_group.mark_function("state_transfer", test_resend_normal) + +# 一次状态闭环的重新发送 +async def random_once_req(top_agent: OutsideAgent, ifu_ref: IFUReceiverModel): + cycle_info: MMIOCycleInfo = MMIOCycleInfo() + cycle_info.workable_randomize() + await top_agent.set_up_before_mmio_states(cycle_info) + ifu_ref.set_up_before_mmio_states(cycle_info) + while True: + mmio_req: MMIOReq = MMIOReq() + mmio_req.randomize_with_cycles(cycle_info) + ref_state = ifu_ref.deal_with_single_mmio_req(mmio_req) + res = await top_agent.cmp_single_mmio_req(mmio_req, ref_state) + if res[0] == MMIOState.STATE_COMMITED: + break + await top_agent.reset_mmio_state() + +import time +import random + +@toffee_test.testcase +async def test_random_resends(ifu_top_env): + top_agent: OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + seed = time.time_ns() + random.seed(seed) + # 1771817322909433303 + # 1771206108183421321 + # 1758367223082587666 + # 1758370058889010927 + print("seed =", seed) + for i in range(20000): + if i % 1000 == 0: + print(f"epoch: {i}") + await random_once_req(top_agent, ifu_ref) + ifu_top_env.mmio_group.mark_function("state_transfer", test_random_resends) + ifu_top_env.mmio_group.mark_function("seq_target", test_random_resends) diff --git a/ut_frontend/ifu/ifu_top/test/non_mmio_test.py b/ut_frontend/ifu/ifu_top/test/non_mmio_test.py new file mode 100644 index 00000000..50254a49 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/test/non_mmio_test.py @@ -0,0 +1,299 @@ +import toffee_test +from ..agent import OutsideAgent +from ..instr_utils import construct_non_cfis, rebuild_cacheline_from_parts +from ..datadef import NonMMIOSingleReq, ICacheResp, FTQQuery,FTQIdx, ICacheStatusResp,ClusteredNonMMIOReqs, NonMMIOResp +from ..commons import calc_double_line, randbool +from .top_test_fixture import ifu_top_env +from ..env import IFUReceiverModel + +import random + +# 这部分测试的是top中stage0失败的逻辑,包含两类,一类是ICache没有ready,一类是需要冲刷当前的idx +@toffee_test.testcase +async def test_stage0_failure(ifu_top_env): + top_agent : OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + reqs: ClusteredNonMMIOReqs = ClusteredNonMMIOReqs() + + icache_not_ready_req: NonMMIOSingleReq = NonMMIOSingleReq() + icache_not_ready_req.icache_ready = False + reqs.reqs.append(icache_not_ready_req) + reqs.resps.append(ifu_ref.inner_deal_with_non_mmio(icache_not_ready_req)) + + # reqs.reqs.append(icache_not_ready_req) + choices = [(False, -1), (True, 1)] + + for i in range(8): + bpu_flush_flag_same_req: NonMMIOSingleReq = NonMMIOSingleReq() + bpu_flush_flag_same_req.ftq_req.ftqIdx.flag = ((i%4) // 2== 0) + bpu_flush_flag_same_req.ftq_req.ftqIdx.value = 4 + bpu_flush_flag_same_req.bpu_flush_info.clear_init() + choice = choices[i//4] + stg_name = f"s{i%2 + 2}" + rev_stg_name = f"s{3-i%2}" + bpu_flush_flag_same_req.bpu_flush_info.stgs[stg_name].stg_valid = True + bpu_flush_flag_same_req.bpu_flush_info.stgs[rev_stg_name].stg_valid = False + bpu_flush_flag_same_req.bpu_flush_info.stgs[stg_name].ftqIdx.value = \ + bpu_flush_flag_same_req.ftq_req.ftqIdx.value + choice[1] + bpu_flush_flag_same_req.bpu_flush_info.stgs[stg_name].ftqIdx.flag = \ + bpu_flush_flag_same_req.ftq_req.ftqIdx.flag ^ choice[0] + reqs.reqs.append(bpu_flush_flag_same_req) + reqs.resps.append(ifu_ref.inner_deal_with_non_mmio(bpu_flush_flag_same_req)) + await top_agent.deal_with_non_mmio_clusters(reqs) + ifu_top_env.top_group.mark_function("icache_ready", test_stage0_failure) + ifu_top_env.top_group.mark_function("bpu_flush", test_stage0_failure) + +# 这个函数测试的是ICache阻塞的情况,共分为四种 +@toffee_test.testcase +async def test_icache_not_valid_cases(ifu_top_env): + top_agent : OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + + # 设置一条请求的初始状态 + req_with_icache_invalids: NonMMIOSingleReq = NonMMIOSingleReq() + req_with_icache_invalids.fs_is_off = randbool() + req_with_icache_invalids.ftq_req.set_start_addr(0x1524) + req_with_icache_invalids.ftq_req.nextStartAddr = req_with_icache_invalids.ftq_req.startAddr + 32 + + # 情况1:icache_valid为false + icache_resp_invalid_self: ICacheResp = ICacheResp(ftq_req=req_with_icache_invalids.ftq_req, init_as_valid=True) + icache_resp_invalid_self.icache_valid = False + req_with_icache_invalids.invalid_icache_resps.append(icache_resp_invalid_self) + + # 情况2: vaddr_0和startaddr不匹配 + icache_resp_cur_addr_err: ICacheResp = ICacheResp(ftq_req=req_with_icache_invalids.ftq_req, init_as_valid=True) + icache_resp_cur_addr_err.vaddrs[0]=0x1444 + req_with_icache_invalids.invalid_icache_resps.append(icache_resp_cur_addr_err) + + # 情况3:double line错误 + icache_resp_double_line_err: ICacheResp = ICacheResp(ftq_req=req_with_icache_invalids.ftq_req, init_as_valid=True) + icache_resp_double_line_err.double_line = False + req_with_icache_invalids.invalid_icache_resps.append(icache_resp_double_line_err) + + # 情况4:vaddr_1和nextlineaddr不匹配 + icache_resp_nextline_addr_err: ICacheResp = ICacheResp(ftq_req=req_with_icache_invalids.ftq_req, init_as_valid=True) + icache_resp_nextline_addr_err.vaddrs[1]=0x1444 + req_with_icache_invalids.invalid_icache_resps.append(icache_resp_nextline_addr_err) + + req_with_icache_invalids.final_icache_resp.randomize(req_with_icache_invalids.ftq_req) + + + cluster: ClusteredNonMMIOReqs = ClusteredNonMMIOReqs() + cluster.reqs.append(req_with_icache_invalids) + cluster.resps.append(ifu_ref.inner_deal_with_non_mmio(req_with_icache_invalids)) + req_another: NonMMIOSingleReq = NonMMIOSingleReq() + req_another.randomize() + cluster.reqs.append(req_another) + cluster.resps.append(ifu_ref.inner_deal_with_non_mmio(req_another)) + await top_agent.deal_with_non_mmio_clusters(cluster) + ifu_top_env.top_group.mark_function("icache_all_valid", test_icache_not_valid_cases) + +# 用一个真实的随即例子测试跨预测块的请求 +@toffee_test.testcase +async def test_cross_prediction_block(ifu_top_env): + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + top_agent : OutsideAgent = ifu_top_env.top_agent + req1 : NonMMIOSingleReq = NonMMIOSingleReq() + # first req that may trigger wrong condition + req1.fs_is_off = True + req1.ftq_req.valid = True + req1.ftq_req.ftqIdx.flag = True + req1.ftq_req.ftqIdx.value = 11 + req1.ftq_req.ftqOffset.exists = False + req1.ftq_req.ftqOffset.offsetIdx = 15 + req1.ftq_req.nextlineStart = 754912186775546 + req1.ftq_req.startAddr = 754912186775482 + # req1.ftq_req.nextStartAddr=323036143508946 + req1.ftq_req.nextStartAddr=754912186775514 + + req1.icache_ready = True + + req1.final_icache_resp = ICacheResp( + itlb_pbmts=[0, 0], + pmp_mmios=[False, False], + exceptions=[3, 0], + vaddrs=[754912186775482, 754912186775546], + paddr=404033938, + VS_non_leaf_PTE=True, + data=7283205146466666787615510459749180600166653367786977051173264907390803464150081059653197407328432508533526888081055979451474366030242223105181599048187283, + backend_exception=True, + double_line=1, + gpaddr=29633898249398642, + icache_valid=True + ) + req1.bpu_flush_info.stgs["s2"].stg_valid = False + req1.bpu_flush_info.stgs["s2"].ftqIdx.flag = False + req1.bpu_flush_info.stgs["s2"].ftqIdx.value = 0 + + req1.bpu_flush_info.stgs["s3"].stg_valid = False + req1.bpu_flush_info.stgs["s3"].ftqIdx.flag = False + req1.bpu_flush_info.stgs["s3"].ftqIdx.value = 0 + + # res = await top_agent.deal_with_non_mmio_outer(req1) + + # print(f"last res(to check last half valid): {res}") + # print(f"last half valid: {top_agent.get_cur_last_half_valid()}") + # await top_agent.step() + # print(f"last half valid: {top_agent.get_cur_last_half_valid()}") + # await top_agent.step() + # print(f"last half valid: {top_agent.get_cur_last_half_valid()}") + # await top_agent.step() + # print(f"last half valid: {top_agent.get_cur_last_half_valid()}") + req2 : NonMMIOSingleReq = NonMMIOSingleReq() + req2.ftq_req.valid = True + req2.ftq_req.ftqIdx.flag = False + req2.ftq_req.ftqIdx.value = 15 + req2.ftq_req.ftqOffset.exists = True + req2.ftq_req.ftqOffset.offsetIdx = 4 + req2.ftq_req.nextlineStart=1064050532637454 + req2.ftq_req.startAddr=1064050532637392 + req2.ftq_req.nextStartAddr=159591502088156 + req2.icache_ready = True + + # req2.icache_resp.ready = True + req2.final_icache_resp = ICacheResp( + itlb_pbmts=[0, 0], + pmp_mmios=[False, False], + exceptions=[0, 0], + vaddrs=[1064050532637392, 1064050532637456], + paddr=404033938, + VS_non_leaf_PTE=True, + data=1340603429732904944016761231998006384991999296852212916603272879707747040077396313749805960213710619513794390144730529792, + backend_exception=False, + double_line=0, + gpaddr=28240919629685350, + icache_valid=True + ) + + req2.bpu_flush_info.stgs["s2"].stg_valid = False + # req2.bpu_flush_info.stgs["s2"].ftqIdx.flag = False + # req2.bpu_flush_info.stgs["s2"].ftqIdx.value = 0 + + req2.bpu_flush_info.stgs["s3"].stg_valid = False + # req2.bpu_flush_info.stgs["s3"].ftqIdx.flag = False + # req2.bpu_flush_info.stgs["s3"].ftqIdx.value = 0 + + req2.fs_is_off=True + # await top_agent.deal_with_non_mmio_outer(req2) + + cluster: ClusteredNonMMIOReqs = ClusteredNonMMIOReqs() + cluster.reqs.append(req1) + res1=ifu_ref.inner_deal_with_non_mmio(req1) + cluster.resps.append(res1) + + cluster.reqs.append(req1) + res1=ifu_ref.inner_deal_with_non_mmio(req1) + cluster.resps.append(res1) + + # cluster.reqs.append(req2) + # res2=ifu_ref.inner_deal_with_non_mmio(req2) + # cluster.resps.append(res2) + + + # import copy + # req3= copy.deepcopy(req2) + # req3.ftq_req.ftqIdx.value += 1 + # cluster.reqs.append(req3) + # res3=ifu_ref.inner_deal_with_non_mmio(req3) + # cluster.resps.append(res3) + + # req4= copy.deepcopy(req3) + # req4.ftq_req.ftqIdx.value += 1 + # cluster.reqs.append(req4) + # res4=ifu_ref.inner_deal_with_non_mmio(req4) + # cluster.resps.append(res4) + await top_agent.deal_with_non_mmio_clusters(cluster) + ifu_top_env.top_group.mark_function("cross_blk", test_cross_prediction_block) + + +# 支持多次随机的跨预测块方法 +# 1. 如果发现当前last_half存在且不产生flush信号,可以直接结束生成 +# 2. 如果发现当前last_half存在且无flush,若后一个非last_half,可以直接结束生成,否则继续到2的条件下判断 +# 3. 如果当前last_half存在且要求flush,则 +# 3.a: 重复一遍该req(保持ftq不变) +# 3.b:生成一个新的结果,然后再生成下一个结果 +@toffee_test.testcase +async def test_totally_random_res(ifu_top_env): + top_agent : OutsideAgent = ifu_top_env.top_agent + ifu_ref: IFUReceiverModel = ifu_top_env.ifu_receiver_model + TIMES=20000 + import time + seed = time.time_ns() + random.seed(seed) + for i in range(TIMES): + if i % 1000 == 0: + print(f"epoch: {i}") + STATE=0 + cluster: ClusteredNonMMIOReqs = ClusteredNonMMIOReqs() + ifu_ref.force_flush_non_mmio_stats() + ftq_idx: FTQIdx = FTQIdx() + import copy + while(True): + if STATE == 0: + req: NonMMIOSingleReq = NonMMIOSingleReq() + req.randomize(randomly_choosing_finished=True, ftq_idx=copy.deepcopy(ftq_idx)) + res:NonMMIOResp = ifu_ref.inner_deal_with_non_mmio(req) + cluster.reqs.append(req) + cluster.resps.append(res) + # if not res.last_half_valid: + # break + if res.pin_flush: + # 当前未结束,且需要flush, 此时需要重新生成 + STATE = 2 + else: + STATE = 1 + if not res.last_half_valid: + # 只有完全合法的非跨预测块请求才能作为一次调用的结束 + break + elif STATE == 1: + # 上一个请求为last half,且合法 + # 目前几乎等同于state0 + req: NonMMIOSingleReq = NonMMIOSingleReq() + req.randomize(randomly_choosing_finished=True, ftq_idx=copy.deepcopy(ftq_idx)) + res:NonMMIOResp = ifu_ref.inner_deal_with_non_mmio(req) + cluster.reqs.append(req) + cluster.resps.append(res) + if res.pin_flush: + STATE=2 + else: + if not res.last_half_valid: + # 回到状态0是因为跨预测块的请求有两种valid形态,因此这里也不能退出 + STATE=0 + elif STATE==2: + choice = random.randint(0, 99) + if choice < 20: + # 上一个非法,重复请求,直接flush + # res: NonMMIOResp = ifu_ref.inner_deal_with_non_mmio(req) + # cluster.reqs.append(req) + # cluster.resps.append(res) + ifu_ref.last_half_valid = False + break + + # 此时需要新的请求重新辅助生成 + req: NonMMIOSingleReq = NonMMIOSingleReq() + req.randomize(ftq_idx=copy.deepcopy(ftq_idx)) + res:NonMMIOResp = ifu_ref.inner_deal_with_non_mmio(req) + cluster.reqs.append(req) + cluster.resps.append(res) + STATE=0 + if res.last_half_valid: + # 未结束,回到状态1 + STATE=1 + else: + STATE=0 + ftq_idx.inc() + # print(cluster.reqs) + await top_agent.deal_with_non_mmio_clusters(cluster) + + ifu_top_env.top_group.mark_function("gpaddr_fault", test_cross_prediction_block) + ifu_top_env.top_group.mark_function("exception", test_cross_prediction_block) + ifu_top_env.top_group.mark_function("cut_ptr", test_cross_prediction_block) + ifu_top_env.submodules_group.mark_function("predecode_concat", test_cross_prediction_block) + ifu_top_env.submodules_group.mark_function("predecode_rvc", test_cross_prediction_block) + ifu_top_env.submodules_group.mark_function("predecode_jmpoff", test_cross_prediction_block) + ifu_top_env.submodules_group.mark_function("predecode_brtype", test_cross_prediction_block) + ifu_top_env.submodules_group.mark_function("predecode_jal_ret", test_cross_prediction_block) + ifu_top_env.submodules_group.mark_function("starts", test_cross_prediction_block) + ifu_top_env.submodules_group.mark_function("check_errs", test_cross_prediction_block) + ifu_top_env.submodules_group.mark_function("rvc_expand", test_cross_prediction_block) + diff --git a/ut_frontend/ifu/ifu_top/test/smoke_test.py b/ut_frontend/ifu/ifu_top/test/smoke_test.py deleted file mode 100644 index 685e844f..00000000 --- a/ut_frontend/ifu/ifu_top/test/smoke_test.py +++ /dev/null @@ -1,132 +0,0 @@ -import toffee_test -from .top_test_fixture import ifu_top_env -from ..datadef import FTQQuery, ICacheStatusResp, FTQFlushInfo, FromUncache, ITLBResp, PMPResp, RobCommit, FrontendTriggerReq - -import random - -""" 这只是一个测试例子,仅仅展现了5个流水级中各个接口的触发时机, 如果环境有错误欢迎指出 - TBD: 一些后续其他用例的优化例子: - 设置某些ready为False阻塞,测试模块的情况 - 设置工作到一半发一个请求信号 - 跨预测块的请求(至少发两次) - 构造MMIO异常 - 参考模型上,目前我的思路是在agent所有方法上加参考方法,同步调用参考模型,需要统一维护一套状态和上下文环境 - 另外,很多输入数据之间是存在关系的,文档里描述了一些,但可能还有隐含的关系没有写明,欢迎提issue或discussion - 以及,通过修改构建脚本依赖的内部接口文件 scripts/ifu_related/ifu_top_internals.yaml,可以暴露更多的内部 - 信息,比如flush,通过这些io信号之外的内部信号,可以更好地捕捉到模块内部的行为信息""" -@toffee_test.testcase -async def test_smoke1(ifu_top_env): - - # this is just an example, maybe still some hidden relations of the inputs need to be found - ftq_query = FTQQuery() - ftq_query.ftqIdx.flag = False - ftq_query.ftqIdx.value = 2 - - ftq_query.ftqOffset.exists = False - ftq_query.ftqOffset.offsetIdx = 0 - ftq_query.startAddr = 14531204 - ftq_query.nextlineStart = ftq_query.startAddr + 64 - - ftq_query.nextStartAddr = 19191898 - - - icache_resp = ICacheStatusResp() - icache_resp.ready = True - icache_resp.resp.backend_exception = False - icache_resp.resp.double_line = (ftq_query.startAddr & 256) - icache_resp.resp.pmp_mmios[0] = False - icache_resp.resp.pmp_mmios[1] = False - icache_resp.resp.data = 0x1096_1144_1189_1204_1217_1221_1444 - icache_resp.resp.vaddrs[0] = ftq_query.startAddr - icache_resp.resp.vaddrs[1] = ftq_query.nextlineStart - icache_resp.resp.exceptions[0] = False - icache_resp.resp.exceptions[1] = False - icache_resp.resp.paddr = 0x18151192 - icache_resp.resp.gpaddr = 0x1798180418121814 - icache_resp.resp.icache_valid = True - icache_resp.resp.VS_non_leaf_PTE = True - icache_resp.resp.itlb_pbmts[0] = 0 - icache_resp.resp.itlb_pbmts[1] = 0 - - ftq_flush_info = FTQFlushInfo() - ftq_flush_info.flush_from_bpu.stgs["s2"].stg_valid = True - ftq_flush_info.flush_from_bpu.stgs["s2"].ftqIdx.flag = False - ftq_flush_info.flush_from_bpu.stgs["s2"].ftqIdx.value = 4 - ftq_flush_info.flush_from_bpu.stgs["s3"].stg_valid = False - ftq_flush_info.flush_from_bpu.stgs["s3"].ftqIdx.flag = False - ftq_flush_info.flush_from_bpu.stgs["s3"].ftqIdx.value = 6 - - ftq_flush_info.redirect.redirect_level = False - ftq_flush_info.redirect.ftqIdx.flag = True - ftq_flush_info.redirect.ftqIdx.value = 12 - ftq_flush_info.redirect.valid = False - ftq_flush_info.redirect.ftqOffset = 4 - - from_uncache = FromUncache() - from_uncache.data = 0x12313134 - from_uncache.valid = True - - itlb_resp = ITLBResp() - - itlb_resp.valid = True - itlb_resp.excp.afInstr = False - itlb_resp.excp.gpfInstr = False - itlb_resp.excp.pfInstr = False - itlb_resp.gpaddr = 0x12121212 - itlb_resp.isForVSnonLeafPTE = False - itlb_resp.paddr = 0x13461456 - itlb_resp.pbmt = 0 - - pmp_resp = PMPResp() - pmp_resp.instr = 0 - pmp_resp.mmio = True - - rob_commits = [RobCommit() for i in range(8)] - - triggerReq = FrontendTriggerReq() - triggerReq.fsIsOff = True - # done at stage 0 - - await ifu_top_env.top_agent.query_from_ftq(ftq_query) - await ifu_top_env.top_agent.from_ftq_flush(ftq_flush_info) - await ifu_top_env.top_agent.set_icache_ready(icache_resp.ready) - - await ifu_top_env.top_agent.top.step() - - await ifu_top_env.top_agent.get_ftq_ready() - - await ifu_top_env.top_agent.top.step() - # done at stage 2? - await ifu_top_env.top_agent.fake_resp(icache_resp) - await ifu_top_env.top_agent.top.step() - # done at stage3? - await ifu_top_env.top_agent.receive_mmio_ftq_ptr() - await ifu_top_env.top_agent.set_mmio_commited(True) - - await ifu_top_env.top_agent.set_touncache_ready(True) - await ifu_top_env.top_agent.get_to_uncache_req() - await ifu_top_env.top_agent.fake_from_uncache(from_uncache) - - await ifu_top_env.top_agent.set_itlb_req_ready(True) - await ifu_top_env.top_agent.fake_get_itlb_req() - - await ifu_top_env.top_agent.get_itlb_resp_ready() - await ifu_top_env.top_agent.fake_itlb_resp(itlb_resp) - - await ifu_top_env.top_agent.receive_pmp_req_addr() - await ifu_top_env.top_agent.fake_pmp_resp(pmp_resp) - - await ifu_top_env.top_agent.fake_rob_commits(rob_commits) - - await ifu_top_env.top_agent.set_triggers(triggerReq) - - await ifu_top_env.top_agent.set_ibuffer_ready(True) - await ifu_top_env.top_agent.get_toibuffer_info() - - await ifu_top_env.top_agent.get_icache_stop() - await ifu_top_env.top_agent.top.step() - - await ifu_top_env.top_agent.collect_res_backto_ftq() - - - diff --git a/ut_frontend/ifu/ifu_top/test/top_test_fixture.py b/ut_frontend/ifu/ifu_top/test/top_test_fixture.py index cfc1c0f0..39ca2d81 100644 --- a/ut_frontend/ifu/ifu_top/test/top_test_fixture.py +++ b/ut_frontend/ifu/ifu_top/test/top_test_fixture.py @@ -2,6 +2,9 @@ from dut.NewIFU import DUTNewIFU from toffee import start_clock from ..env import IFUTopEnv +from .ckpt_mmio import get_coverage_group_mmio +from .ckpt_subs import get_coverage_group_sub_modules +from .ckpt_tops import get_coverage_group_tops # 可以在这里添加覆盖组 @@ -9,9 +12,25 @@ async def ifu_top_env(toffee_request: toffee_test.ToffeeRequest): import asyncio # version_check() - dut = toffee_request.create_dut(DUTNewIFU) + dut: DUTNewIFU = toffee_request.create_dut(DUTNewIFU, clock_name="clock") start_clock(dut) + dut.reset.value = 1 + dut.Step() + dut.reset.value = 0 + dut.Step() ifu_env_cur = IFUTopEnv(dut) + mmio_group = get_coverage_group_mmio(ifu_env_cur) + sub_modules_group = get_coverage_group_sub_modules(ifu_env_cur) + top_group = get_coverage_group_tops(ifu_env_cur) + + ifu_env_cur.mmio_group = mmio_group + ifu_env_cur.submodules_group = sub_modules_group + ifu_env_cur.top_group = top_group + + toffee_request.add_cov_groups([mmio_group,\ + sub_modules_group,\ + top_group]) + yield ifu_env_cur cur_loop = asyncio.get_event_loop()