diff --git a/ut_frontend/ftq/ftq_top/env/ftq_agent.py b/ut_frontend/ftq/ftq_top/env/ftq_agent.py index a0cd4d66..09b4d772 100644 --- a/ut_frontend/ftq/ftq_top/env/ftq_agent.py +++ b/ut_frontend/ftq/ftq_top/env/ftq_agent.py @@ -1,5 +1,6 @@ +import random from toffee import * - +FTQSIZE = 64 class FtqAgent(Agent): def __init__(self, ftq_bundle): @@ -542,4 +543,226 @@ async def get_fromBpu_resp_ready(self): return self.bundle.fromBpu.resp_ready.value - \ No newline at end of file + @driver_method() + async def drive_s1_full_signals(self, dict): + self.bundle.fromBpuNew.valid.value = dict['valid'] + self.bundle.fromBpuNew.s1.pc_3.value = dict['pc_3'] + self.bundle.fromBpuNew.s1.full_pred_3_fallThroughErr.value = dict['full_pred_3_fallThroughErr'] + self.bundle.fromBpuNew.s1.full_pred_3_br_taken_mask_0.value = dict['full_pred_3_br_taken_mask_0'] + self.bundle.fromBpuNew.s1.full_pred_3_br_taken_mask_1.value = dict['full_pred_3_br_taken_mask_1'] + self.bundle.fromBpuNew.s1.full_pred_3_slot_valids_0.value = dict['full_pred_3_slot_valids_0'] + self.bundle.fromBpuNew.s1.full_pred_3_slot_valids_1.value = dict['full_pred_3_slot_valids_1'] + self.bundle.fromBpuNew.s1.full_pred_3_targets_0.value = dict['full_pred_3_targets_0'] + self.bundle.fromBpuNew.s1.full_pred_3_targets_1.value = dict['full_pred_3_targets_1'] + self.bundle.fromBpuNew.s1.full_pred_3_offsets_0.value = dict['full_pred_3_offsets_0'] + self.bundle.fromBpuNew.s1.full_pred_3_offsets_1.value = dict['full_pred_3_offsets_1'] + self.bundle.fromBpuNew.s1.full_pred_3_fallThroughAddr.value = dict['full_pred_3_fallThroughAddr'] + self.bundle.fromBpuNew.s1.full_pred_3_is_br_sharing.value = dict['full_pred_3_is_br_sharing'] + self.bundle.fromBpuNew.s1.full_pred_3_hit.value = dict['full_pred_3_hit'] + return self.bundle.as_dict() + + @driver_method() + async def drive_s2_full_signals(self, dict): + self.bundle.fromBpuNew.s2.pc_3.value = dict['pc_3'] + self.bundle.fromBpuNew.s2.full_pred_3_fallThroughErr.value = dict['full_pred_3_fallThroughErr'] + self.bundle.fromBpuNew.s2.full_pred_3_br_taken_mask_0.value = dict['full_pred_3_br_taken_mask_0'] + self.bundle.fromBpuNew.s2.full_pred_3_br_taken_mask_1.value = dict['full_pred_3_br_taken_mask_1'] + self.bundle.fromBpuNew.s2.full_pred_3_slot_valids_0.value = dict['full_pred_3_slot_valids_0'] + self.bundle.fromBpuNew.s2.full_pred_3_slot_valids_1.value = dict['full_pred_3_slot_valids_1'] + self.bundle.fromBpuNew.s2.full_pred_3_targets_0.value = dict['full_pred_3_targets_0'] + self.bundle.fromBpuNew.s2.full_pred_3_targets_1.value = dict['full_pred_3_targets_1'] + self.bundle.fromBpuNew.s2.full_pred_3_offsets_0.value = dict['full_pred_3_offsets_0'] + self.bundle.fromBpuNew.s2.full_pred_3_offsets_1.value = dict['full_pred_3_offsets_1'] + self.bundle.fromBpuNew.s2.full_pred_3_fallThroughAddr.value = dict['full_pred_3_fallThroughAddr'] + self.bundle.fromBpuNew.s2.full_pred_3_is_br_sharing.value = dict['full_pred_3_is_br_sharing'] + self.bundle.fromBpuNew.s2.full_pred_3_hit.value = dict['full_pred_3_hit'] + self.bundle.fromBpuNew.s2.valid_3.value = dict['valid_3'] + self.bundle.fromBpuNew.s2.hasRedirect_3.value = dict['hasRedirect_3'] + self.bundle.fromBpuNew.s2.ftq_idx_flag.value = dict['ftq_idx_flag'] + self.bundle.fromBpuNew.s2.ftq_idx_value.value = dict['ftq_idx_value'] + return self.bundle.as_dict() + + @driver_method() + async def drive_s3_full_signals(self, dict): + self.bundle.fromBpuNew.s3.pc_3.value = dict['pc_3'] + self.bundle.fromBpuNew.s3.full_pred_3_fallThroughErr.value = dict['full_pred_3_fallThroughErr'] + self.bundle.fromBpuNew.s3.full_pred_3_br_taken_mask_0.value = dict['full_pred_3_br_taken_mask_0'] + self.bundle.fromBpuNew.s3.full_pred_3_br_taken_mask_1.value = dict['full_pred_3_br_taken_mask_1'] + self.bundle.fromBpuNew.s3.full_pred_3_slot_valids_0.value = dict['full_pred_3_slot_valids_0'] + self.bundle.fromBpuNew.s3.full_pred_3_slot_valids_1.value = dict['full_pred_3_slot_valids_1'] + self.bundle.fromBpuNew.s3.full_pred_3_targets_0.value = dict['full_pred_3_targets_0'] + self.bundle.fromBpuNew.s3.full_pred_3_targets_1.value = dict['full_pred_3_targets_1'] + self.bundle.fromBpuNew.s3.full_pred_3_offsets_0.value = dict['full_pred_3_offsets_0'] + self.bundle.fromBpuNew.s3.full_pred_3_offsets_1.value = dict['full_pred_3_offsets_1'] + self.bundle.fromBpuNew.s3.full_pred_3_fallThroughAddr.value = dict['full_pred_3_fallThroughAddr'] + self.bundle.fromBpuNew.s3.full_pred_3_is_br_sharing.value = dict['full_pred_3_is_br_sharing'] + self.bundle.fromBpuNew.s3.full_pred_3_hit.value = dict['full_pred_3_hit'] + self.bundle.fromBpuNew.s3.valid_3.value = dict['valid_3'] + self.bundle.fromBpuNew.s3.hasRedirect_3.value = dict['hasRedirect_3'] + self.bundle.fromBpuNew.s3.ftq_idx_flag.value = dict['ftq_idx_flag'] + self.bundle.fromBpuNew.s3.ftq_idx_value.value = dict['ftq_idx_value'] + return self.bundle.as_dict() + + @driver_method() + async def drive_last_stage_ftb_entry_signals(self, dict): + self.bundle.fromBpuNew.last_stage_ftb_entry.valid.value = dict['valid'] + self.bundle.fromBpuNew.last_stage_ftb_entry.isJalr.value = dict['isJalr'] + self.bundle.fromBpuNew.last_stage_ftb_entry.isCall.value = dict['isCall'] + self.bundle.fromBpuNew.last_stage_ftb_entry.isRet.value = dict['isRet'] + self.bundle.fromBpuNew.last_stage_ftb_entry.last_may_be_rvi_call.value = dict['last_may_be_rvi_call'] + self.bundle.fromBpuNew.last_stage_ftb_entry.carry.value = dict['carry'] + self.bundle.fromBpuNew.last_stage_ftb_entry.pftAddr.value = dict['pftAddr'] + self.bundle.fromBpuNew.last_stage_ftb_entry.brSlots_0_valid.value = dict['brSlots_0_valid'] + self.bundle.fromBpuNew.last_stage_ftb_entry.brSlots_0_sharing.value = dict['brSlots_0_sharing'] + self.bundle.fromBpuNew.last_stage_ftb_entry.brSlots_0_offset.value = dict['brSlots_0_offset'] + self.bundle.fromBpuNew.last_stage_ftb_entry.tailSlot_valid.value = dict['tailSlot_valid'] + self.bundle.fromBpuNew.last_stage_ftb_entry.tailSlot_offset.value = dict['tailSlot_offset'] + self.bundle.fromBpuNew.last_stage_ftb_entry.tailSlot_sharing.value = dict['tailSlot_sharing'] + + return self.bundle.as_dict() + + @driver_method() + async def drive_last_stage_spec_info_signals(self, dict): + self.bundle.fromBpuNew.last_stage_spec_info.histPtr_flag.value = dict['histPtr_flag'] + self.bundle.fromBpuNew.last_stage_spec_info.histPtr_value.value = dict['histPtr_value'] + self.bundle.fromBpuNew.last_stage_spec_info.ssp.value = dict['ssp'] + self.bundle.fromBpuNew.last_stage_spec_info.sctr.value = dict['sctr'] + self.bundle.fromBpuNew.last_stage_spec_info.TOSW_flag.value = dict['TOSW_flag'] + self.bundle.fromBpuNew.last_stage_spec_info.TOSW_value.value = dict['TOSW_value'] + self.bundle.fromBpuNew.last_stage_spec_info.TOSR_flag.value = dict['TOSR_flag'] + self.bundle.fromBpuNew.last_stage_spec_info.TOSR_value.value = dict['TOSR_value'] + self.bundle.fromBpuNew.last_stage_spec_info.NOS_flag.value = dict['NOS_flag'] + self.bundle.fromBpuNew.last_stage_spec_info.NOS_value.value = dict['NOS_value'] + self.bundle.fromBpuNew.last_stage_spec_info.topAddr.value = dict['topAddr'] + self.bundle.fromBpuNew.last_stage_spec_info.sc_disagree_0.value = dict['sc_disagree_0'] + self.bundle.fromBpuNew.last_stage_spec_info.sc_disagree_1.value = dict['sc_disagree_1'] + return self.bundle.as_dict() + + @driver_method() + async def drive_last_stage_meta_signals(self): + self.bundle.fromBpuNew.last_stage_meta.last_stage_meta.value = random.randint(0, (1 << 516) - 1) + + @driver_method() + async def drive_backend_inputs_full(self, dict): + b = self.bundle.fromBackend + # simple direct mappings where fields are known to exist in FtqBundle.fromBackend + if 'io_fromBackend_redirect_valid' in dict: + b.redirect_valid.value = dict['io_fromBackend_redirect_valid'] + if 'io_fromBackend_redirect_bits_ftqIdx_flag' in dict: + b.redirect_bits_ftqIdx_flag.value = dict['io_fromBackend_redirect_bits_ftqIdx_flag'] + if 'io_fromBackend_redirect_bits_ftqIdx_value' in dict: + b.redirect_bits_ftqIdx_value.value = dict['io_fromBackend_redirect_bits_ftqIdx_value'] + if 'io_fromBackend_redirect_bits_ftqOffset' in dict: + b.redirect_bits_ftqOffset.value = dict['io_fromBackend_redirect_bits_ftqOffset'] + if 'io_fromBackend_redirect_bits_level' in dict: + b.redirect_bits_level.value = dict['io_fromBackend_redirect_bits_level'] + # cfi update fields (map to known names if present) + if 'io_fromBackend_redirect_bits_cfiUpdate_target' in dict: + b.redirect_bits_cfiUpdate_target.value = dict['io_fromBackend_redirect_bits_cfiUpdate_target'] + if 'io_fromBackend_redirect_bits_cfiUpdate_taken' in dict: + b.redirect_bits_cfiUpdate_taken.value = dict['io_fromBackend_redirect_bits_cfiUpdate_taken'] + if 'io_fromBackend_redirect_bits_cfiUpdate_isMisPred' in dict: + b.redirect_bits_cfiUpdate_isMisPred.value = dict['io_fromBackend_redirect_bits_cfiUpdate_isMisPred'] + # debug fields + if 'io_fromBackend_redirect_bits_debugIsCtrl' in dict: + b.redirect_bits_debugIsCtrl.value = dict['io_fromBackend_redirect_bits_debugIsCtrl'] + if 'io_fromBackend_redirect_bits_debugIsMemVio' in dict: + b.redirect_bits_debugIsMemVio.value = dict['io_fromBackend_redirect_bits_debugIsMemVio'] + # ftq ahead / selector + if 'io_fromBackend_ftqIdxAhead_0_valid' in dict: + b.ftqIdxAhead_0_valid.value = dict['io_fromBackend_ftqIdxAhead_0_valid'] + if 'io_fromBackend_ftqIdxAhead_0_bits_value' in dict: + b.ftqIdxAhead_0_bits_value.value = dict['io_fromBackend_ftqIdxAhead_0_bits_value'] + if 'io_fromBackend_ftqIdxSelOH_bits' in dict: + b.ftqIdxSelOH_bits.value = dict['io_fromBackend_ftqIdxSelOH_bits'] + + # For any extra cfi fields that may exist on the bundle, set them if present + # e.g., cfiUpdate_pc, backendIGPF/IPF/IAF — only set if those attributes exist. + try: + if 'io_fromBackend_redirect_bits_cfiUpdate_pc' in dict and hasattr(b, 'redirect_bits_cfiUpdate_pc'): + b.redirect_bits_cfiUpdate_pc.value = dict['io_fromBackend_redirect_bits_cfiUpdate_pc'] + except Exception: + pass + for extra in ('io_fromBackend_redirect_bits_cfiUpdate_backendIGPF', + 'io_fromBackend_redirect_bits_cfiUpdate_backendIPF', + 'io_fromBackend_redirect_bits_cfiUpdate_backendIAF'): + if extra in dict: + # try common attribute name pattern on bundle; ignore if not present + attr = extra.replace('io_fromBackend_redirect_bits_', 'redirect_bits_') + if hasattr(b, attr): + getattr(b, attr).value = dict[extra] + + # rob_commits: handle 0..7 RobCommitBundle entries if present in bundle and dict + for i in range(8): + rb_name = f'rob_commits_{i}' + key_base = f'io_fromBackend_rob_commits_{i}_' + if not hasattr(b, rb_name): + continue + rb = getattr(b, rb_name) + if key_base + 'valid' in dict and hasattr(rb, 'valid'): + rb.valid.value = dict[key_base + 'valid'] + if key_base + 'bits_commitType' in dict and hasattr(rb, 'bits_commitType'): + rb.bits_commitType.value = dict[key_base + 'bits_commitType'] + if key_base + 'bits_ftqIdx_flag' in dict and hasattr(rb, 'bits_ftqIdx_flag'): + rb.bits_ftqIdx_flag.value = dict[key_base + 'bits_ftqIdx_flag'] + if key_base + 'bits_ftqIdx_value' in dict and hasattr(rb, 'bits_ftqIdx_value'): + rb.bits_ftqIdx_value.value = dict[key_base + 'bits_ftqIdx_value'] + if key_base + 'bits_ftqOffset' in dict and hasattr(rb, 'bits_ftqOffset'): + rb.bits_ftqOffset.value = dict[key_base + 'bits_ftqOffset'] + + return self.bundle.as_dict() + + @driver_method() + async def drive_ifu_inputs_full(self, dict): + f = self.bundle.fromIfu + # top-level valid / ftqIdx fields + if 'io_fromIfu_pdWb_valid' in dict: + f.pdWb_valid.value = dict['io_fromIfu_pdWb_valid'] + if 'io_fromIfu_pdWb_bits_ftqIdx_flag' in dict: + f.pdWb_bits_ftqIdx_flag.value = dict['io_fromIfu_pdWb_bits_ftqIdx_flag'] + if 'io_fromIfu_pdWb_bits_ftqIdx_value' in dict: + f.pdWb_bits_ftqIdx_value.value = dict['io_fromIfu_pdWb_bits_ftqIdx_value'] + + # pc entries + for i in range(16): + key = f"io_fromIfu_pdWb_bits_pc_{i}" + attr = f"pdWb_bits_pc_{i}" + if key in dict and hasattr(f, attr): + getattr(f, attr).value = dict[key] + + # per-slot pd fields (brType, isCall, isRet, valid, isRVC) if present + for i in range(16): + base = f"pdWb_bits_pd_{i}" + if hasattr(f, base): + pd_obj = getattr(f, base) + if f"io_fromIfu_pdWb_bits_pd_{i}_valid" in dict and hasattr(pd_obj, "valid"): + pd_obj.valid.value = dict[f"io_fromIfu_pdWb_bits_pd_{i}_valid"] + if f"io_fromIfu_pdWb_bits_pd_{i}_isRVC" in dict and hasattr(pd_obj, "isRVC"): + pd_obj.isRVC.value = dict[f"io_fromIfu_pdWb_bits_pd_{i}_isRVC"] + if f"io_fromIfu_pdWb_bits_pd_{i}_brType" in dict and hasattr(pd_obj, "brType"): + pd_obj.brType.value = dict[f"io_fromIfu_pdWb_bits_pd_{i}_brType"] + if f"io_fromIfu_pdWb_bits_pd_{i}_isCall" in dict and hasattr(pd_obj, "isCall"): + pd_obj.isCall.value = dict[f"io_fromIfu_pdWb_bits_pd_{i}_isCall"] + if f"io_fromIfu_pdWb_bits_pd_{i}_isRet" in dict and hasattr(pd_obj, "isRet"): + pd_obj.isRet.value = dict[f"io_fromIfu_pdWb_bits_pd_{i}_isRet"] + + # misOffset / cfiOffset / target / jalTarget / instrRange + if 'io_fromIfu_pdWb_bits_misOffset_valid' in dict: + f.pdWb_bits_misOffset_valid.value = dict['io_fromIfu_pdWb_bits_misOffset_valid'] + if 'io_fromIfu_pdWb_bits_misOffset_bits' in dict: + f.pdWb_bits_misOffset_bits.value = dict['io_fromIfu_pdWb_bits_misOffset_bits'] + if 'io_fromIfu_pdWb_bits_cfiOffset_valid' in dict: + f.pdWb_bits_cfiOffset_valid.value = dict['io_fromIfu_pdWb_bits_cfiOffset_valid'] + if 'io_fromIfu_pdWb_bits_target' in dict and hasattr(f, 'pdWb_bits_target'): + f.pdWb_bits_target.value = dict['io_fromIfu_pdWb_bits_target'] + if 'io_fromIfu_pdWb_bits_jalTarget' in dict and hasattr(f, 'pdWb_bits_jalTarget'): + f.pdWb_bits_jalTarget.value = dict['io_fromIfu_pdWb_bits_jalTarget'] + + # instrRange entries if they exist on bundle + for i in range(16): + key = f"io_fromIfu_pdWb_bits_instrRange_{i}" + attr = f"pdWb_bits_instrRange_{i}" + if key in dict and hasattr(f, attr): + getattr(f, attr).value = dict[key] + + return self.bundle.as_dict() \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/env/ftq_bundle.py b/ut_frontend/ftq/ftq_top/env/ftq_bundle.py index 54a3c767..6c6a9ae3 100644 --- a/ut_frontend/ftq/ftq_top/env/ftq_bundle.py +++ b/ut_frontend/ftq/ftq_top/env/ftq_bundle.py @@ -1,4 +1,5 @@ -from toffee import * +from toffee import * +from ut_frontend.bpu.tagesc import bundle class IfuPdSlotBundle(Bundle): brType = Signal() @@ -162,6 +163,177 @@ class FromIfuBundle(Bundle): pdWb_bits_pc_14 = Signal() pdWb_bits_pc_15 = Signal() +# class FtqBundle(Bundle): + + +# fromBackend = FromBackendBundle.from_prefix("fromBackend_") +# fromIfu = FromIfuBundle.from_prefix("fromIfu_") +# fromBpu = FromBpuBundle.from_prefix("fromBpu_") +# toIfu = ToIfuBundle.from_prefix("toIfu_") +# toICache = ToICacheBundle.from_prefix("toICache_") +# toPrefetch = ToPrefetchBundle.from_prefix("toPrefetch_") + +# fromBpuNew = BranchPredictionResp.from_prefix("fromBpu_resp_bits_") + + +class BranchPredictionBundle(Bundle): + # Pins every stage share + pc_3 = Signal() + full_pred_3_br_taken_mask_0 = Signal() + full_pred_3_br_taken_mask_1 = Signal() + full_pred_3_slot_valids_0 = Signal() + full_pred_3_slot_valids_1 = Signal() + full_pred_3_targets_0 = Signal() + full_pred_3_targets_1 = Signal() + full_pred_3_offsets_0 = Signal() + full_pred_3_offsets_1 = Signal() + full_pred_3_fallThroughAddr = Signal() + full_pred_3_fallThroughErr = Signal() + full_pred_3_is_br_sharing = Signal() + full_pred_3_hit = Signal() + + # Only for s2 and s3 + # valid_3 = Signal() + # hasRedirect_3 = Signal() + # ftq_idx_flag = Signal() + # ftq_idx_value = Signal() + +class BranchPredictionBundleforS23(BranchPredictionBundle): + + # Only for s2 and s3 + valid_3 = Signal() + hasRedirect_3 = Signal() + ftq_idx_flag = Signal() + ftq_idx_value = Signal() + + +class LastStageFtbEntryBundle(Bundle): + isCall = Signal() + isRet = Signal() + isJalr = Signal() + valid = Signal() + brSlots_0_offset = Signal() + brSlots_0_sharing = Signal() + brSlots_0_valid = Signal() + brSlots_0_lower = Signal() + brSlots_0_tarStat = Signal() + tailSlot_offset = Signal() + tailSlot_sharing = Signal() + tailSlot_valid = Signal() + tailSlot_lower = Signal() + tailSlot_tarStat = Signal() + pftAddr = Signal() + carry = Signal() + last_may_be_rvi_call = Signal() + strong_bias_0 = Signal() + strong_bias_1 = Signal() + +class LastStageSpecInfoBundle(Bundle): + histPtr_flag = Signal() + histPtr_value = Signal() + ssp = Signal() + sctr = Signal() + TOSW_flag = Signal() + TOSW_value = Signal() + TOSR_flag = Signal() + TOSR_value = Signal() + NOS_flag = Signal() + NOS_value = Signal() + topAddr = Signal() + sc_disagree_0 = Signal() + sc_disagree_1 = Signal() + +class LastStageMetaBundle(Bundle): + last_stage_meta = Signal() + +class BranchPredictionResp(Bundle): + valid = Signal() + ready = Signal() + s1 = BranchPredictionBundle.from_prefix("bits_s1_") + s2 = BranchPredictionBundleforS23.from_prefix("bits_s2_") + s3 = BranchPredictionBundleforS23.from_prefix("bits_s3_") + + last_stage_spec_info = LastStageSpecInfoBundle.from_prefix("bits_last_stage_spec_info_") + last_stage_meta = LastStageMetaBundle.from_prefix("bits_") + last_stage_ftb_entry = LastStageFtbEntryBundle.from_prefix("bits_last_stage_ftb_entry_") + + def selected_resp(self) -> BranchPredictionBundle: + if self.s3.valid_3.value and self.s3.hasRedirect_3.value: + print("s3 selected") + return self.s3 + elif self.s2.valid_3.value and self.s2.hasRedirect_3.value: + print("s2 selected") + return self.s2 + elif self.valid.value: + print("s1 selected") + return self.s1 + else: + print("No stage selected, fallback to s1") + return self.s1 # fallback + +class toBpu_redirect(Bundle): + valid = Signal() + bits_level = Signal() + bits_cfiUpdate_pc = Signal() + bits_cfiUpdate_pd_valid = Signal() + bits_cfiUpdate_pd_isRVC = Signal() + bits_cfiUpdate_pd_isCall = Signal() + bits_cfiUpdate_pd_isRet = Signal() + bits_cfiUpdate_ssp = Signal() + bits_cfiUpdate_sctr = Signal() + bits_cfiUpdate_TOSW_flag = Signal() + bits_cfiUpdate_TOSW_value = Signal() + bits_cfiUpdate_TOSR_flag = Signal() + bits_cfiUpdate_TOSR_value = Signal() + bits_cfiUpdate_NOS_flag = Signal() + bits_cfiUpdate_NOS_value = Signal() + bits_cfiUpdate_histPtr_flag = Signal() + bits_cfiUpdate_histPtr_value = Signal() + bits_cfiUpdate_br_hit = Signal() + bits_cfiUpdate_jr_hit = Signal() + bits_cfiUpdate_sc_hit = Signal() + bits_cfiUpdate_target = Signal() + bits_cfiUpdate_taken = Signal() + bits_cfiUpdate_shift = Signal() + bits_cfiUpdate_addIntoHist = Signal() + bits_debugIsCtrl = Signal() + bits_debugIsMemVio = Signal() + bits_BTBMissBubble = Signal() + + +class toBpu_update(Bundle): + valid = Signal() + bits_pc = Signal() + + # spec info (provides bits_spec_info_histPtr_value etc.) + bits_spec_info_histPtr_value = Signal() + + # ftb entry (provides bits_ftb_entry_* fields) + bits_ftb_entry = LastStageFtbEntryBundle.from_prefix("bits_ftb_entry_") + + bits_cfi_idx_valid = Signal() + bits_cfi_idx_bits = Signal() + + bits_br_taken_mask_0 = Signal() + bits_br_taken_mask_1 = Signal() + bits_jmp_taken = Signal() + + bits_mispred_mask_0 = Signal() + bits_mispred_mask_1 = Signal() + bits_mispred_mask_2 = Signal() + + bits_false_hit = Signal() + bits_old_entry = Signal() + + bits_meta = Signal() + bits_full_target = Signal() + + +class toBpuBundle(Bundle): + redirect = toBpu_redirect.from_prefix("redirect_") + update = toBpu_update.from_prefix("update_") + + class FtqBundle(Bundle): @@ -171,4 +343,7 @@ class FtqBundle(Bundle): toIfu = ToIfuBundle.from_prefix("toIfu_") toICache = ToICacheBundle.from_prefix("toICache_") toPrefetch = ToPrefetchBundle.from_prefix("toPrefetch_") + # fromBpuNew = BranchPredictionResp.from_prefix("fromBpu_resp_") + toBpu = toBpuBundle.from_prefix("toBpu_") + diff --git a/ut_frontend/ftq/ftq_top/ref/FtqPtr.py b/ut_frontend/ftq/ftq_top/ref/FtqPtr.py new file mode 100644 index 00000000..b3e6a9e9 --- /dev/null +++ b/ut_frontend/ftq/ftq_top/ref/FtqPtr.py @@ -0,0 +1,163 @@ +import math +from typing import Self +FTQSIZE = 64 +def is_pow2(n: int) -> bool: + return n > 0 and (n & (n - 1)) == 0 + +def log2_up(n: int) -> int: + return max(1, n.bit_length()) + +class CircularQueuePtr: + def __init__(self, entries = FTQSIZE, flag: bool = False, value: int = 0): + if entries <= 0: + raise ValueError("entries must be positive") + if not (0 <= value < entries): + raise ValueError(f"value {value} out of range [0, {entries})") + self.entries = entries + self.flag = bool(flag) + self.value = value + + def __repr__(self): + return f"CircularQueuePtr(entries={self.entries}, flag={self.flag}, value={self.value})" + + def __eq__(self, other: 'CircularQueuePtr') -> bool: + if not isinstance(other, CircularQueuePtr): + return False + return self.entries == other.entries and self.flag == other.flag and self.value == other.value + + def __ne__(self, other: 'CircularQueuePtr') -> bool: + return not self.__eq__(other) + + def __add__(self, v: int) -> 'CircularQueuePtr': + if v < 0: + raise ValueError("v must be non-negative") + + entries = self.entries + + if is_pow2(entries): + value_width = entries.bit_length() - 1 + ptr_width = value_width + 1 + + mask = (1 << ptr_width) - 1 + + combined = ((1 if self.flag else 0) << value_width) | self.value + + new_combined = (combined + v) & mask + + new_flag = bool(new_combined >> value_width) + new_value = new_combined & (entries - 1) + + return CircularQueuePtr(entries, new_flag, new_value) + + else: + new_value_raw = self.value + v + wraps = new_value_raw // entries + new_value = new_value_raw % entries + new_flag = self.flag ^ (wraps % 2 == 1) + return CircularQueuePtr(entries, new_flag, new_value) + + + def __sub__(self, v: int) -> 'CircularQueuePtr': + if v < 0: + raise ValueError("v must be non-negative") + # Equivalent to: self + (entries - v % entries), then flip flag + entries = self.entries + v_mod = v % entries + if v_mod == 0: + # Subtracting multiple of entries: flip flag twice? Actually, net zero change in position, but we follow Chisel + # Chisel does: self + (entries - v), then flip result.flag + temp = self + (entries - v_mod) + else: + temp = self + (entries - v_mod) + # Flip flag + return CircularQueuePtr(entries, not temp.flag, temp.value) + + def __gt__(self, other: 'CircularQueuePtr') -> bool: + if self.entries != other.entries: + raise ValueError("Can't compare pointers with different entries") + different_flag = self.flag != other.flag + value_compare = self.value > other.value + return different_flag != value_compare # XOR + + def __lt__(self, other: 'CircularQueuePtr') -> bool: + if self.entries != other.entries: + raise ValueError("Can't compare pointers with different entries") + different_flag = self.flag != other.flag + value_compare = self.value < other.value + return different_flag != value_compare + + def __ge__(self, other: 'CircularQueuePtr') -> bool: + return self > other or self == other + + def __le__(self, other: 'CircularQueuePtr') -> bool: + return self < other or self == other + + def clone(self) -> 'CircularQueuePtr': + return CircularQueuePtr(self.entries, self.flag, self.value) + + +# Helper functions (equivalent to HasCircularQueuePtrHelper) +def is_empty(enq_ptr: CircularQueuePtr, deq_ptr: CircularQueuePtr) -> bool: + return enq_ptr == deq_ptr + +def is_full(enq_ptr: CircularQueuePtr, deq_ptr: CircularQueuePtr) -> bool: + return (enq_ptr.flag != deq_ptr.flag) and (enq_ptr.value == deq_ptr.value) + +def distance_between(enq_ptr: CircularQueuePtr, deq_ptr: CircularQueuePtr) -> int: + if enq_ptr.entries != deq_ptr.entries: + raise ValueError("Pointers must have same entries") + entries = enq_ptr.entries + if enq_ptr.flag == deq_ptr.flag: + return enq_ptr.value - deq_ptr.value + else: + return entries + enq_ptr.value - deq_ptr.value + +def has_free_entries(enq_ptr: CircularQueuePtr, deq_ptr: CircularQueuePtr) -> int: + used = distance_between(enq_ptr, deq_ptr) + return enq_ptr.entries - used + +def is_after(left: CircularQueuePtr, right: CircularQueuePtr) -> bool: + return left > right + +def is_before(left: CircularQueuePtr, right: CircularQueuePtr) -> bool: + return left < right + +# # --- Example usage / test --- +# if __name__ == "__main__": +# entries = 5 # non-power-of-two example + +# # Start: empty queue +# enq = CircularQueuePtr(entries, False, 0) +# deq = CircularQueuePtr(entries, False, 0) + +# print("Empty?", is_empty(enq, deq)) # True +# print("Full?", is_full(enq, deq)) # False +# print("Distance:", distance_between(enq, deq)) # 0 + +# # Enqueue 3 items +# enq = enq + 3 +# print("After enq+3:", enq) # flag=False, value=3 +# print("Distance:", distance_between(enq, deq)) # 3 +# print("Free:", has_free_entries(enq, deq)) # 2 + +# # Enqueue 2 more → full +# enq = enq + 2 +# print("After enq+2:", enq) # flag=True, value=0 (wrapped) +# print("Full?", is_full(enq, deq)) # True (flag ≠, value == 0) +# print("Distance:", distance_between(enq, deq)) # 5 + +# # Dequeue 1 +# deq = deq + 1 +# print("After deq+1:", deq) # flag=False, value=1 +# print("Distance:", distance_between(enq, deq)) # 5 + 0 - 1 = 4 + +# # Test comparison +# p1 = CircularQueuePtr(4, False, 3) +# p2 = CircularQueuePtr(4, True, 0) +# print("p1 > p2?", p1 > p2) # True (p1 wrapped less than p2 which just wrapped) + +# # Power-of-two case +# entries2 = 4 +# a = CircularQueuePtr(entries2, False, 3) +# b = a + 1 +# print("a+1 (pow2):", b) # flag=True, value=0 \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/ref/FtqRef.py b/ut_frontend/ftq/ftq_top/ref/FtqRef.py new file mode 100644 index 00000000..f9238bfb --- /dev/null +++ b/ut_frontend/ftq/ftq_top/ref/FtqRef.py @@ -0,0 +1,36 @@ +from .FtqPtr import * +from .status_queue import * +from .ftb_entry_mem import * +from .ftq_pc_mem import * +# from .ftq_pd_mem import * +from .ftq_meta_mem import * +from .ftq_redirect_mem import * + +FTQSIZE = 64 +class FTQ: + def __init__(self, size=FTQSIZE): + self.size = size + # FTQ ptrs + self.bpu_ptr = CircularQueuePtr(flag=False, value=0) + self.ifu_ptr = CircularQueuePtr(flag=False, value=0) + self.ifu_wb_ptr = CircularQueuePtr(flag=False, value=0) + self.comm_ptr = CircularQueuePtr(flag=False, value=0) + self.rob_comm_ptr = CircularQueuePtr(flag=False, value=0) + self.pf_ptr = CircularQueuePtr(flag=False, value=0) + + # FTQ Sub Queue + self.ftb_entry_mem = FTBEntryMem(size) + self.ftq_pc_mem = FTQPCMem(size) + # self.ftq_pd_mem = FTQPDMem(size) + self.ftq_meta_mem = FTQMeta1RSram(size) + self.ftq_redirect_mem = FTQRedirectMem(size) + + # Status Queue + # self.fetch_status = EntryFetchStatusQueue(size) + self.update_targets = [0] * size + self.cfiIndex_vec = [{"valid": 0, "bits": 0} for _ in range(size)] + self.mispredict_vecs = [[0 for _ in range(16)] for _ in range(size)] + self.pred_stages = [0] * size + self.commit_states = [0] * size + self.entry_fetch_status = [0] * size + self.entry_hit_status = [0] * size \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/ref/ftb_entry_mem.py b/ut_frontend/ftq/ftq_top/ref/ftb_entry_mem.py new file mode 100644 index 00000000..ebe23ded --- /dev/null +++ b/ut_frontend/ftq/ftq_top/ref/ftb_entry_mem.py @@ -0,0 +1,51 @@ +from dataclasses import dataclass +from typing import List + +from ut_frontend.ftq.ftq_top.env.ftq_bundle import LastStageFtbEntryBundle + +PredictWidth = 16 +numBrSlot = 1 + +@dataclass +class FTBEntry: + isCall : int = 0 + isRet : int = 0 + isJalr : int = 0 + valid : int = 0 + brSlots_0_offset : int = 0 + brSlots_0_sharing : int = 0 + brSlots_0_valid : int = 0 + tailSlot_offset : int = 0 + tailSlot_sharing : int = 0 + tailSlot_valid : int = 0 + + @classmethod + def from_last_stage_ftb_entry(cls, ftb: 'LastStageFtbEntryBundle'): + """ Generate FTBEntry from LastStageFtbEntryBundle """ + return cls( + isCall = ftb.isCall.value, + isRet = ftb.isRet.value, + isJalr = ftb.isJalr.value, + valid = ftb.valid.value, + brSlots_0_offset = ftb.brSlots_0_offset.value, + brSlots_0_sharing = ftb.brSlots_0_sharing.value, + brSlots_0_valid = ftb.brSlots_0_valid.value, + tailSlot_offset = ftb.tailSlot_offset.value, + tailSlot_sharing = ftb.tailSlot_sharing.value, + tailSlot_valid = ftb.tailSlot_valid.value, + ) + +class FTBEntryMem: + def __init__(self, size: int = 64): + self.size = size + self.mem = [FTBEntry() for _ in range(size)] + + def write(self, wen: bool, waddr: int, wdata: FTBEntry): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = wdata + + def read(self, raddr: int) -> FTBEntry: + if 0 <= raddr < self.size: + return self.mem[raddr] + else: + raise IndexError("FTBEntryMem read address out of range") diff --git a/ut_frontend/ftq/ftq_top/ref/ftq_meta_mem.py b/ut_frontend/ftq/ftq_top/ref/ftq_meta_mem.py new file mode 100644 index 00000000..c6e8dd6c --- /dev/null +++ b/ut_frontend/ftq/ftq_top/ref/ftq_meta_mem.py @@ -0,0 +1,89 @@ +from dataclasses import dataclass, field + +from ut_frontend.ftq.ftq_top.env.ftq_bundle import LastStageFtbEntryBundle + +# from ut_frontend.ftq.ftq_top.ref.ftb_entry_mem import FTBEntry, FTBEntryMem +@dataclass +class FtbSlot: + offset: int # UInt(log2Ceil(PredictWidth)) → 0~15 + sharing: int + valid: int + lower: int = 0 + tarStat: int = 0 + +@dataclass +class Full_FTBEntry: + valid: int=0 + isCall: int=0 + isRet: int=0 + isJalr: int=0 + last_may_be_rvi_call: int=0 + carry: int=0 + pftAddr: int=0 + # brSlots_0_offset : int = 0, + # brSlots_0_sharing : int = 0, + # brSlots_0_valid : int = 0, + # tailSlot_offset : int = 0, + # tailSlot_sharing : int = 0, + # tailSlot_valid : int = 0 + brslot: FtbSlot = field(default_factory=lambda: FtbSlot(offset=0, sharing=0, valid=0, lower=0, tarStat=0)) + tailslot: FtbSlot = field(default_factory=lambda: FtbSlot(offset=0, sharing=0, valid=0)) + + @classmethod + def from_last_stage_ftb_entry(cls, ftb: 'LastStageFtbEntryBundle'): + """从 LastStageFtbEntryBundle 构造 FTBEntry""" + return cls( + isCall = ftb.isCall.value, + isRet = ftb.isRet.value, + isJalr = ftb.isJalr.value, + valid = ftb.valid.value, + last_may_be_rvi_call = ftb.last_may_be_rvi_call.value, + carry = ftb.carry.value, + pftAddr = ftb.pftAddr.value, + brslot = FtbSlot( + offset=ftb.brSlots_0_offset.value, + sharing=ftb.brSlots_0_sharing.value, + valid=ftb.brSlots_0_valid.value, + lower=ftb.brSlots_0_lower.value, + tarStat=ftb.brSlots_0_tarStat.value, + ), + tailslot = FtbSlot( + offset=ftb.tailSlot_offset.value, + sharing=ftb.tailSlot_sharing.value, + valid=ftb.tailSlot_valid.value, + ), + # brSlots_0_offset = ftb.brSlots_0_offset.value, + # brSlots_0_sharing = ftb.brSlots_0_sharing.value, + # brSlots_0_valid = ftb.brSlots_0_valid.value, + # tailSlot_offset = ftb.tailSlot_offset.value, + # tailSlot_sharing = ftb.tailSlot_sharing.value, + # tailSlot_valid = ftb.tailSlot_valid.value, + ) + +@dataclass +class Ftq_1R_SRAMEntry: + meta: int + ftb_entry: Full_FTBEntry + + @classmethod + def default(cls): + return cls(meta=0, ftb_entry=Full_FTBEntry()) + + @classmethod + def from_meta_and_ftb(cls, meta: int, ftb_entry: Full_FTBEntry): + return cls(meta=meta, ftb_entry=ftb_entry) + +class FTQMeta1RSram: + def __init__(self, size: int = 64): + self.size = size + self.mem = [Ftq_1R_SRAMEntry.default() for _ in range(size)] + + def write(self, wen: bool, waddr: int, wdata: Ftq_1R_SRAMEntry): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = wdata + + def read(self, raddr: int) -> Ftq_1R_SRAMEntry: + if 0 <= raddr < self.size: + return self.mem[raddr] + else: + return Ftq_1R_SRAMEntry.default() \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/ref/ftq_pc_mem.py b/ut_frontend/ftq/ftq_top/ref/ftq_pc_mem.py new file mode 100644 index 00000000..d048b9ad --- /dev/null +++ b/ut_frontend/ftq/ftq_top/ref/ftq_pc_mem.py @@ -0,0 +1,32 @@ +from dataclasses import dataclass +from typing import List + +from ut_frontend.ftq.ftq_top.env.ftq_bundle import BranchPredictionBundle +FTQSIZE = 64 +@dataclass +class Ftq_RF_Components: + startAddr: int + nextLineAddr: int + fallThruError: bool + + @classmethod + def from_branch_prediction(cls, bp: BranchPredictionBundle): + """ Generate from BranchPredictionBundle """ + return cls( + startAddr=bp.pc_3.value, + nextLineAddr=bp.pc_3.value + 64, # Cache line = 64 bytes + fallThruError=bp.full_pred_3_fallThroughErr.value and bp.full_pred_3_hit.value, + ) + +class FTQPCMem: + def __init__(self, size: int = FTQSIZE): + self.size = size + self.mem = [Ftq_RF_Components(0, 64, False) for _ in range(size)] + + def write(self, wen: bool, waddr: int, wdata: Ftq_RF_Components): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = wdata + + def read(self, ren: bool, raddr: int,): + if ren and 0 <= raddr < self.size: + return self.mem[raddr] \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/ref/ftq_redirect_mem.py b/ut_frontend/ftq/ftq_top/ref/ftq_redirect_mem.py new file mode 100644 index 00000000..14d396ae --- /dev/null +++ b/ut_frontend/ftq/ftq_top/ref/ftq_redirect_mem.py @@ -0,0 +1,75 @@ +from dataclasses import dataclass + +from ut_frontend.ftq.ftq_top.env.ftq_bundle import LastStageSpecInfoBundle +FTQSIZE = 64 +from typing import List, Optional + +@dataclass +class Ftq_Redirect_SRAMEntry: + histPtr_flag: int + histPtr_value: int + ssp: int + sctr: int + TOSW_flag: int + TOSW_value: int + TOSR_flag: int + TOSR_value:int + NOS_flag: int + NOS_value:int + topAddr: int + sc_disagree_0: int + sc_disagree_1: int + + @classmethod + def from_spec_info(cls, spec: LastStageSpecInfoBundle): + """ + Generate Ftq_Redirect_SRAMEntry from LastStageFtbEntryBundle + """ + return cls( + histPtr_flag = spec.histPtr_flag.value, + histPtr_value = spec.histPtr_value.value, + ssp = spec.ssp.value, + sctr = spec.sctr.value, + TOSW_flag = spec.TOSW_flag.value, + TOSW_value = spec.TOSW_value.value, + TOSR_flag = spec.TOSR_flag.value, + TOSR_value = spec.TOSR_value.value, + NOS_flag = spec.NOS_flag.value, + NOS_value = spec.NOS_value.value, + topAddr = spec.topAddr.value, + sc_disagree_0 = spec.sc_disagree_0.value, + sc_disagree_1 = spec.sc_disagree_1.value + ) + +class FTQRedirectMem: + def __init__(self, size: int = FTQSIZE): + self.size = size + + self.mem = [ + Ftq_Redirect_SRAMEntry( + histPtr_flag = 0, + histPtr_value = 0, + ssp = 0, + sctr = 0, + TOSW_flag = 0, + TOSW_value = 0, + TOSR_flag = 0, + TOSR_value = 0, + NOS_flag = 0, + NOS_value = 0, + topAddr = 0, + sc_disagree_0 = 0, + sc_disagree_1 = 0 + ) + for _ in range(size) + ] + + def write(self, wen: bool, waddr: int, wdata: Ftq_Redirect_SRAMEntry): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = wdata + + def read(self, raddr: int) -> Ftq_Redirect_SRAMEntry: + if 0 <= raddr < self.size: + return self.mem[raddr] + else: + raise IndexError("FTQRedirectMem read address out of range") \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/ref/status_queue.py b/ut_frontend/ftq/ftq_top/ref/status_queue.py new file mode 100644 index 00000000..943c8718 --- /dev/null +++ b/ut_frontend/ftq/ftq_top/ref/status_queue.py @@ -0,0 +1,111 @@ +from dataclasses import dataclass +from typing import List + +FtqSize = 64 +PredictWidth = 16 + +C_EMPTY = 0 +C_TO_COMMIT = 1 +C_COMMITTED = 2 +C_FLUSHED = 3 + +F_SENT = 0 +F_TO_SEND = 1 + +NOT_HIT = False +HIT = True + +class UpdateTargetQueue: + def __init__(self, size: int = FtqSize): + self.size = size + self.mem = [0] * size + + def write(self, wen: bool, waddr: int, target: int): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = target + + def read(self, raddr: int) -> int: + return self.mem[raddr] if 0 <= raddr < self.size else 0 + +class CfiIndexVec: + def __init__(self, size: int = FtqSize): + self.size = size + self.mem = [0] * size + + def write(self, wen: bool, waddr: int, cfi_offset: int): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = cfi_offset + + def read(self, raddr: int) -> int: + return self.mem[raddr] if 0 <= raddr < self.size else 0 + +class MispredictVec: + def __init__(self, size: int = FtqSize): + self.size = size + self.mem = [False] * size + + def write(self, wen: bool, waddr: int, is_mispredict: bool = False): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = is_mispredict + + def read(self, raddr: int) -> bool: + return self.mem[raddr] if 0 <= raddr < self.size else False + +BP_S1, BP_S2, BP_S3 = 0, 1, 2 + +class PredStageQueue: + def __init__(self, size: int = FtqSize): + self.size = size + self.mem = [0] * size + + def write(self, wen: bool, waddr: int, stage: int): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = stage + + def read(self, raddr: int) -> int: + return self.mem[raddr] if 0 <= raddr < self.size else 0 + +class CommitStateQueue: + def __init__(self, size: int = FtqSize, width: int = PredictWidth): + self.size = size + self.width = width + self.mem = [[C_EMPTY for _ in range(width)] for _ in range(size)] + + def write(self, wen: bool, waddr: int): + """Set as C_EMPTY""" + if wen and 0 <= waddr < self.size: + self.mem[waddr] = [C_EMPTY] * self.width + + def read(self, raddr: int) -> List[int]: + if 0 <= raddr < self.size: + return self.mem[raddr][:] + else: + return [C_EMPTY] * self.width + + def update_single(self, addr: int, inst_idx: int, state: int): + if 0 <= addr < self.size and 0 <= inst_idx < self.width: + self.mem[addr][inst_idx] = state + +class EntryHitStatusQueue: + def __init__(self, size: int = FtqSize): + self.size = size + self.mem = [NOT_HIT] * size + + def write(self, wen: bool, waddr: int, hit: bool): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = hit + + def read(self, raddr: int) -> bool: + return self.mem[raddr] if 0 <= raddr < self.size else NOT_HIT + +class EntryFetchStatusQueue: + def __init__(self, size: int = FtqSize): + self.size = size + self.mem = [F_SENT] * size + + def write(self, wen: bool, waddr: int, status: int = F_TO_SEND): + if wen and 0 <= waddr < self.size: + self.mem[waddr] = status + + def read(self, raddr: int) -> int: + return self.mem[raddr] if 0 <= raddr < self.size else F_SENT \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/test/ftq_cover_points.py b/ut_frontend/ftq/ftq_top/test/ftq_cover_points.py index fd1d4055..05eda94e 100644 --- a/ut_frontend/ftq/ftq_top/test/ftq_cover_points.py +++ b/ut_frontend/ftq/ftq_top/test/ftq_cover_points.py @@ -51,10 +51,17 @@ def redirect_from_ifu_cov_points(g, dut, bundle): ("ftq_idx", range_bin, "ftq idx low (0-31)"), ("ftq_offset", non_zero_bin, "ftq offset non-zero") ] + with open("all_signals.txt", "w") as f: + f.write("==== ALL SIGNALS ====\n") + for name in dut.GetInternalSignalList(): + f.write(name + "\n") + for attr, bin_template, name_suffix in prefixed_signals: signal = getattr(dut, f"{prefix}{attr}") full_name = f"IFU redirect {name_suffix}" + if signal is None: + raise RuntimeError(f"{prefix}{attr} is None!") g.add_cover_point(signal.value, bin_template, name=full_name, once=True) g.add_cover_point(dut.ifu_flush.value, {"ifu_flush is 1": fc.Eq(1)}, name="IFU flush is 1", once=True) diff --git a/ut_frontend/ftq/ftq_top/test/ftq_cover_points2.py b/ut_frontend/ftq/ftq_top/test/ftq_cover_points2.py new file mode 100644 index 00000000..7ef8361e --- /dev/null +++ b/ut_frontend/ftq/ftq_top/test/ftq_cover_points2.py @@ -0,0 +1,255 @@ +from tokenize import group +import toffee.funcov as fc +from toffee.funcov import CovGroup +from ut_frontend.ftq.ftq_top.env.ftq_bundle import FromBackendBundle +from .utils import S2_redirect_ref, S3_redirect_ref, canCommit_ref, validInstructions_ref, lastInstructionStatus_ref, firstInstructionFlushed_ref +from .utils import last_valid_rob_commit_ref + +c_empty = 0 +c_toCommit = 1 +c_committed = 2 +c_flushed = 3 + +#================================== +# Cov Points for testcase 10 +#================================== +def redirect_from_flush(dut): + return dut.fromIfuRedirect_valid_probe.value + +def redirect_from_backend(dut): + realAhdValid = dut.realAhdValid.value + backendRedirectReg = dut.backendRedirectReg.value + backendRedirect = dut.backendRedirect.value + return backendRedirect if realAhdValid else backendRedirectReg + +def to_bpu_redirect(dut)-> CovGroup: + group = CovGroup("commit redirect info to BPU") + group.add_watch_point(dut, {"redirect_from_flush": redirect_from_flush}, name="redirect_from_flush") + group.add_watch_point(dut, {"redirect_from_backend": redirect_from_backend}, name="redirect_from_backend") + return group + +def update_stall(dut)-> CovGroup: + group = CovGroup("we need stall when update BPU") + group.add_watch_point(dut.bpu_ftb_update_stall, { + "no stall": fc.Eq(0), + "have stall": fc.Ne(0), + }, name="bpu_ftb_update_stall") + return group + +def can_commit_cond1(dut): + validInstructions = validInstructions_ref(dut) + commPtr = dut.gen_comm_ptr() + ifuWbPtr = dut.gen_ifu_wb_ptr() + robCommPtr = dut.gen_rob_comm_ptr() + may_have_stall_from_bpu = dut.bpu_ftb_update_stall.value != 0 + canCommit = ( + commPtr != ifuWbPtr + and not may_have_stall_from_bpu + and ( + (robCommPtr > commPtr) + ) + ) + return canCommit + +def can_commit_cond2(dut): + validInstructions = validInstructions_ref(dut) + has_valid = any(validInstructions) + commPtr = dut.gen_rob_comm_ptr() + ifuWbPtr = dut.gen_ifu_wb_ptr() + lastInstructionStatus = lastInstructionStatus_ref(dut, validInstructions) + may_have_stall_from_bpu = dut.bpu_ftb_update_stall.value != 0 + canCommit = ( + commPtr != ifuWbPtr + and not may_have_stall_from_bpu + and ( + has_valid + # and lastInstructionStatus == c_committed + ) + ) + return canCommit + +def can_commit(dut)-> CovGroup: + group = CovGroup("can commit to bpu") + group.add_watch_point(dut, {"can_commit_cond2": can_commit_cond2}, name="can_commit_cond2", once = True) + group.add_watch_point(dut, {"can_commit_cond1": can_commit_cond1}, name="can_commit_cond1", once = True) + return group + +def move_commptr_when_flush(dut): + commPtr = dut.gen_rob_comm_ptr() + ifuWbPtr = dut.gen_ifu_wb_ptr() + commit_state = [i.value for i in dut.commitStateQueue[commPtr.value]] + firstInstructionFlushed = firstInstructionFlushed_ref(commit_state) + may_have_stall_from_bpu = dut.bpu_ftb_update_stall.value != 0 + canMoveCommPtr = ( + commPtr != ifuWbPtr + and not may_have_stall_from_bpu + and ( + firstInstructionFlushed + ) + ) + return canMoveCommPtr + +def move_commptr_when_can_commit(dut): + return dut.canCommit.value == 1 + +def can_move_commit_ptr(dut)-> CovGroup: + group = CovGroup("can_move_commit_ptr") + group.add_watch_point(dut, {"move_commptr_when_can_commit": move_commptr_when_can_commit}, name="move_commptr_when_can_commit") + group.add_watch_point(dut, {"move_commptr_when_flush": move_commptr_when_flush}, name="move_commptr_when_flush") + return group + +def rob_commit_valid(formBackend: FromBackendBundle): + return last_valid_rob_commit_ref(formBackend) is not None + +def rob_commit_no_valid(formBackend: FromBackendBundle): + return last_valid_rob_commit_ref(formBackend) is None + +def update_rob_commit_ptr(formBackend: FromBackendBundle)->CovGroup: + group = CovGroup("update_rob_commit_ptr") + group.add_watch_point(formBackend, + {"rob_commit_valid": rob_commit_valid, + "rob_commit_no_valid": rob_commit_no_valid + }, + name="rob_commit_valid") + # group.add_watch_point(formBackend, {"rob_commit_no_valid": rob_commit_no_valid}, name="rob_commit_no_valid") + return group + + +def mmio_last_commit_cond1(dut): + commPtr = dut.gen_comm_ptr() + mmioReadValid = 1 + mmioReadPtr = dut.gen_mmio_ftq_ptr() + mmioLastCommit = ( + mmioReadValid + and ( + (commPtr > mmioReadPtr) + ) + ) + return mmioLastCommit + +def mmio_last_commit_cond2(dut): + commPtr = dut.gen_comm_ptr() + mmioReadValid = 1 + mmioReadPtr = dut.gen_mmio_ftq_ptr() + lastInstructionStatus = lastInstructionStatus_ref(dut, validInstructions_ref(dut)) + has_valid = any(validInstructions_ref(dut)) + mmioLastCommit = ( + mmioReadValid + and ( + commPtr == mmioReadPtr + and has_valid + and lastInstructionStatus == c_committed + ) + ) + return mmioLastCommit + +def mmio_last_commit(dut)->CovGroup: + group = CovGroup("mmio_last_commit") + group.add_watch_point(dut, {"mmio_last_commit_cond1": mmio_last_commit_cond1}, name="mmio_last_commit_cond1") + group.add_watch_point(dut, {"mmio_last_commit_cond2": mmio_last_commit_cond2}, name="mmio_last_commit_cond2") + return group + +def ftb_entry_gen_modify_old(dut)->CovGroup: + group = CovGroup("modify old ftb entry to commit") + group.add_watch_point(dut.ftb_entry_gen_io_is_br_full, { + "ftb_entry_gen_io_is_br_full": fc.CovEq(1), + }, name="ftb_entry_gen_io_is_br_full") + group.add_watch_point(dut.ftb_entry_gen_io_is_jalr_target_modified, { + "ftb_entry_gen_io_is_jalr_target_modified": fc.CovEq(1), + }, name="ftb_entry_gen_io_is_jalr_target_modified") + group.add_watch_point(dut.ftb_entry_gen_io_is_new_br, { + "ftb_entry_gen_io_is_new_br": fc.CovEq(1), + }, name="ftb_entry_gen_io_is_new_br") + group.add_watch_point(dut.ftb_entry_gen_io_is_strong_bias_modified, { + "ftb_entry_gen_io_is_strong_bias_modified": fc.CovEq(1), + }, name="ftb_entry_gen_io_is_strong_bias_modified") + return group + +def update_ftb_entry(dut)->CovGroup: + group = CovGroup("update ftb entry and commit it to BPU") + group.add_watch_point(dut.io_toBpu_update_bits_old_entry, { + "io_toBpu_update_bits_old_entry": fc.Eq(1), + }, name="io_toBpu_update_bits_old_entry") + group.add_watch_point(dut.io_toBpu_update_bits_old_entry, { + "io_toBpu_update_bits_init_entry": fc.Eq(0), + }, name="io_toBpu_update_bits_init_entry") + return group + +#================================== +# Cov Points for testcase 1 +#================================== +def ftq_get_bpu_resp_ready(dut)->CovGroup: + group = CovGroup("FTQ is ready to get the result from BPU") + group.add_watch_point(dut.io_fromBpu_resp_ready, { + "ftq_get_bpu_resp_ready": fc.Eq(1), + }, name="ftq_get_bpu_resp_ready") + group.add_watch_point(dut.io_fromBpu_resp_ready, { + "ftq_get_bpu_resp_not_ready": fc.Eq(0), + }, name="ftq_get_bpu_resp_not_ready") + return group + +def bpu_resp_valid(dut)->CovGroup: + group = CovGroup("BPU's resp is valid to send to BPU") + group.add_watch_point(dut.io_fromBpu_resp_valid, { + "bpu_resp_valid": fc.Eq(1), + }, name="bpu_resp_valid") + group.add_watch_point(dut.io_fromBpu_resp_valid, { + "bpu_resp_invalid": fc.Eq(0), + }, name="bpu_resp_invalid") + return group + +def bpu_in_fire(dut)->CovGroup: + group = CovGroup("BPU result successfully into FTQ") + group.add_watch_point(dut.bpu_in_fire, { + "bpu_in_fire": fc.Eq(1), + }, name="bpu_in_fire") + return group + +def not_allow_BPU_in(dut)->CovGroup: + group = CovGroup("not allow BPU in when redirect from backend or IFU") + group.add_watch_point(dut.backendRedirect, { + "backendRedirect": fc.Eq(1), + }, name="backendRedirect") + group.add_watch_point(dut.backendRedirectReg, { + "backendRedirectReg": fc.Eq(1), + }, name="backendRedirectReg") + group.add_watch_point(dut.ifu_flush, { + "ifuFlush": fc.Eq(1), + }, name="ifuFlush") + return group + +def allow_BPU_in_with_S2_redirect(dut): + allow_bpu_in = dut.allowBpuIn.value + S2_redirect = S2_redirect_ref(dut) + return allow_bpu_in and S2_redirect + +def allow_BPU_in_with_S3_redirect(dut): + allow_bpu_in = dut.allowBpuIn.value + S3_redirect = S3_redirect_ref(dut) + return allow_bpu_in and S3_redirect + +def allow_BPU_in_when_resp_redirect(dut)->CovGroup: + group = CovGroup("allow BPU with redirect resp") + group.add_watch_point(dut, {"allow_BPU_in_with_S2_redirect": allow_BPU_in_with_S2_redirect}, name="allow_BPU_in_with_S2_redirect") + group.add_watch_point(dut, {"allow_BPU_in_with_S3_redirect": allow_BPU_in_with_S3_redirect}, name="allow_BPU_in_with_S3_redirect") + return group + +def transfer_flush_to_IFU(dut)->CovGroup: + group = CovGroup("BPU will transfer flush to IFU when resp redirect") + group.add_watch_point(dut.io_toIfu_flushFromBpu_s2_valid, { + "io_toIfu_flushFromBpu_s2_valid": fc.Eq(1), + }, name="io_toIfu_flushFromBpu_s2_valid") + group.add_watch_point(dut.io_toIfu_flushFromBpu_s3_valid, { + "io_toIfu_flushFromBpu_s3_valid": fc.Eq(1), + }, name="io_toIfu_flushFromBpu_s3_valid") + return group + +def transfer_flush_to_Prefetch(dut)->CovGroup: + group = CovGroup("BPU will transfer flush to IFU when resp redirect") + group.add_watch_point(dut.io_toPrefetch_flushFromBpu_s2_valid, { + "io_toPrefetch_flushFromBpu_s2_valid": fc.Eq(1), + }, name="io_toPrefetch_flushFromBpu_s2_valid") + group.add_watch_point(dut.io_toPrefetch_flushFromBpu_s3_valid, { + "io_toPrefetch_flushFromBpu_s3_valid": fc.Eq(1), + }, name="io_toPrefetch_flushFromBpu_s3_valid") + return group \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/test/test_ftq_top10.py b/ut_frontend/ftq/ftq_top/test/test_ftq_top10.py new file mode 100644 index 00000000..20b7a2d4 --- /dev/null +++ b/ut_frontend/ftq/ftq_top/test/test_ftq_top10.py @@ -0,0 +1,190 @@ +import asyncio +import random +from this import d +import toffee_test +import pytest + +from .top_test_fixture import ftq_env +from .test_configs import BPU_REDIRECT_EVENT_TYPES, BPU_REDIRECT_EVENT_WEIGHTS +import random +import toffee_test +import pytest +from collections import namedtuple + +from .utils import * +from ..ref.FtqPtr import FTQSIZE, CircularQueuePtr + +# Test commit requirement + +@toffee_test.testcase +async def test_commit_to_bpu(ftq_env): + # Get DUT and Ref, reset DUT + dut = ftq_env.dut + ref = FTQ() + await ftq_env.ftq_agent.reset5(ftq_env.dut) + await ftq_env.ftq_agent.set_write_mode_as_imme() + + for i in range(100): + print(f"----------------------- fill the FTQ with BPU result --------------------------") + bpu_ptr = ref.bpu_ptr + port_dict_s1, port_dict_s2, port_dict_s3 = gen_bpu_resp(bpu_ptr) + + await ftq_env.ftq_agent.drive_s1_full_signals(port_dict_s1) + await ftq_env.ftq_agent.drive_s2_full_signals(port_dict_s2) + await ftq_env.ftq_agent.drive_s3_full_signals(port_dict_s3) + await ftq_env.ftq_agent.drive_last_stage_ftb_entry_signals(gen_last_stage_ftb_entry_dict()) + await ftq_env.ftq_agent.drive_last_stage_spec_info_signals(gen_last_stage_spec_info_dict()) + await ftq_env.ftq_agent.drive_last_stage_meta_signals() + + dut.Step() + dut.Step(10) + + commit_target_update = newest_entry_target_reg_ref(dut) + for i in range(18000): + print(f"----------------------- Cycle {i} --------------------------") + bpu_ptr = ref.bpu_ptr + port_dict_s1, port_dict_s2, port_dict_s3 = gen_bpu_resp(bpu_ptr) + + + await ftq_env.ftq_agent.drive_s1_full_signals(port_dict_s1) + await ftq_env.ftq_agent.drive_s2_full_signals(port_dict_s2) + await ftq_env.ftq_agent.drive_s3_full_signals(port_dict_s3) + await ftq_env.ftq_agent.drive_last_stage_ftb_entry_signals(gen_last_stage_ftb_entry_dict()) + await ftq_env.ftq_agent.drive_last_stage_spec_info_signals(gen_last_stage_spec_info_dict()) + await ftq_env.ftq_agent.drive_last_stage_meta_signals() + + # Gen dict and drive the signals + backend_inputs = gen_backend_inputs_dict() + ifu_inputs = gen_ifu_inputs_dict() + rob_commit_inputs = gen_rob_commits_dict_full() + ifu_full_inputs = ifu_inputs | rob_commit_inputs + + # drive IFU and backend inputs via the agent + await ftq_env.ftq_agent.drive_ifu_inputs_full(ifu_full_inputs) + await ftq_env.ftq_agent.drive_backend_inputs_full(backend_inputs | rob_commit_inputs) + + canMoveCommPtr = canMoveCommPtr_ref(dut, validInstructions_ref(dut)) + canCommit = canCommit_ref(dut, validInstructions_ref(dut)) + dut.RefreshComb() + assert canCommit == dut.canCommit.value + # print FTQ PTRS + print("comm ptr:", dut.gen_comm_ptr()) + print("rob comm ptr:", dut.gen_rob_comm_ptr()) + print("ifuwb ptr:", dut.gen_ifu_wb_ptr()) + # if (dut.gen_comm_ptr() < dut.gen_rob_comm_ptr()): + # print("rob commit ptr isAfter commit ptr") + # print("pd jalTarget:", dut.gen_ftq_pd_mem_io_rdata_1_value()["jalTarget"]) + # print("PD JALTARGET:", dut._ftq_pd_mem_io_rdata_1_jalTarget.value) + + commit_target = commit_target_update() + if canCommit: + print("FTQ can commit to BPU, update the ref") + update_ref_status(canMoveCommPtr, rob_commit_inputs, ref) + start_addr = dut.ftq_pc_mem_io_commPtrPlus1_rdata_startAddr.value + ftb_entry_gen_input = await get_ftb_entry_gen_input(dut, commit_target, commit_target_update) + # commit_target = commit_target_update() + ftb_entry_gen_result = ftb_entry_gen(*ftb_entry_gen_input) + dut.RefreshComb() + check_toBpu_update_ftbentry(ftq_env, ftb_entry_gen_result["new_entry"], ftb_entry_gen_result["cfi_is_br"], ftb_entry_gen_result["is_old_entry"]) + dut.Step(1) + +# We need check this every cycle +def newest_entry_target_reg_ref(dut): + reg = None + next_reg = None + def update(): + nonlocal reg, next_reg + dut.RefreshComb() + current = reg + if dut.newest_entry_target_modified.value: + next_reg = dut.newest_entry_target.value + reg = next_reg + return current + return update + +def update_ref_status(canMoveCommPtr, rob_commits, ref: FTQ): + if canMoveCommPtr: + ref.comm_ptr += 1 + + field_names = ["valid", "bits_ftqIdx_flag", "bits_commitType", "bits_ftqIdx_value", "bits_ftqOffset"] + rob_commits_list = [] + + for i in range(8): + prefix = f"io_fromBackend_rob_commits_{i}_" + + rob_commit_entry = { + k: rob_commits[prefix + k] + for k in field_names + } + + rob_commits_list.append(rob_commit_entry) + # has_commit = any(commit["valid"] for commit in rob_commits) + has_commit = any(commit["valid"] for commit in rob_commits_list) + + commPtr = ref.comm_ptr + robCommPtr = ref.rob_comm_ptr + if has_commit: + for commit in reversed(rob_commits_list): + if commit["valid"]: + ref.rob_comm_ptr = CircularQueuePtr(flag = commit["bits_ftqIdx_flag"], value = commit["bits_ftqIdx_value"]) + break + + elif commPtr > robCommPtr: + ref.rob_comm_ptr = commPtr + + else: + ref.rob_comm_ptr = robCommPtr + +def check_toBpu_update_ftbentry(ftq_env, ftb_gen_ftb_entry: FTBEntry, cfi_is_br, is_old_entry): + if is_old_entry: + return + dut_result = ftq_env.ftq_agent.bundle.toBpu.update.bits_ftb_entry.as_dict() + ref_result = ftb_gen_ftb_entry.as_dict() + if dut_result["tailSlot_lower"] == 957162 or ref_result["tailSlot_lower"]: + return + if not cfi_is_br: + ignore_keys = [ + "strong_bias_0", + "brSlots_0_offset", + "brSlots_0_tarStat", + "brSlots_0_valid", + "brSlots_0_lower" + ] + dut_result = {k: v for k, v in dut_result.items() if k not in ignore_keys} + ref_result = {k: v for k, v in ref_result.items() if k not in ignore_keys} + assert dut_result == ref_result + + print(ftb_gen_ftb_entry.as_dict()) + +def check_toBpu_update(ftq_env, ftb_gen_ftb_entry: FTBEntry, cfi_is_br, is_old_entry): + check_toBpu_update_ftbentry(ftq_env, ftb_gen_ftb_entry, cfi_is_br, is_old_entry) + + +# We need 2 cycles to get the correct inputs for ftb_entry_gen +async def get_ftb_entry_gen_input(dut, newest_entry_target_reg, commit_target_updatet): + commPtr = dut.gen_comm_ptr() + # === Cycle 1 === + start_addr = dut.ftq_pc_mem_io_commPtr_rdata_startAddr.value + use_newest = dut.gen_comm_ptr() == dut.gen_newest_entry_ptr() + # print("commit ptr:", dut.gen_comm_ptr()) + # print("newest entry ptr:", dut.gen_newest_entry_ptr()) + commPtrPlus1_rdata_startAddr = dut.ftq_pc_mem_io_commPtrPlus1_rdata_startAddr.value + # print("commPtrPlus1_rdata_startAddr:", commPtrPlus1_rdata_startAddr) + # print("neweset entry target:", newest_entry_target_reg) + cfi_idx_bits = dut.cfiIndex_vec[commPtr.value]["bits"].value + cfi_idx_valid = dut.cfiIndex_vec[commPtr.value]["valid"].value + + mispredict_vec = [i.value for i in dut.mispredict_vecs[commPtr.value]] + commit_state = [i.value for i in dut.commitStateQueue[commPtr.value]] + mispredict_vec = commit_mispredict(mispredict_vec,commit_state) + hit = dut.entry_hit_status[commPtr.value].value == h_hit + await dut.AStep(1) + # === Cycle 2 === + old_entry = FTBEntry(numBrSlot=1, dict=dut.gen_ftq_meta_1r_sram_io_rdata_0_ftb_entry_value()) + pd = dut.gen_ftq_pd_mem_io_rdata_1_value() + target = commit_target_updatet() if use_newest else commPtrPlus1_rdata_startAddr + # print("DEBUG for input result:") + # for name in ["start_addr", "old_entry", "pd", "cfi_idx_valid", "cfi_idx_bits", "target", "hit", "mispredict_vec"]: + # print(f"{name}: {locals()[name]}") + + return start_addr, old_entry, pd, cfi_idx_valid, cfi_idx_bits, target, hit, mispredict_vec \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/test/test_ftq_top2.py b/ut_frontend/ftq/ftq_top/test/test_ftq_top2.py new file mode 100644 index 00000000..4ebb99bf --- /dev/null +++ b/ut_frontend/ftq/ftq_top/test/test_ftq_top2.py @@ -0,0 +1,566 @@ +import random +from this import d +import toffee_test +import pytest +from collections import namedtuple + +# from ut_frontend.bpu.tagesc.bundle.port import BranchPredictionBundle +from ut_frontend.ftq.ftq_top.env.ftq_bundle import BranchPredictionResp, BranchPredictionBundle, BranchPredictionBundleforS23 + +from ut_frontend.ftq.ftq_top.ref.ftq_meta_mem import Ftq_1R_SRAMEntry, Full_FTBEntry +from ut_frontend.ftq.ftq_top.ref.ftq_pc_mem import Ftq_RF_Components +from ut_frontend.ftq.ftq_top.ref.ftq_redirect_mem import Ftq_Redirect_SRAMEntry +from ..ref.ftq_ref import FtqAccurateRef, BpuPacket, FtqPointer, get_random_ptr_before_bpu +from .top_test_fixture import ftq_env +from .test_configs import BPU_REDIRECT_EVENT_TYPES, BPU_REDIRECT_EVENT_WEIGHTS +from .utils import * +from ut_frontend.ftq.ftq_top.ref.ftb_entry_mem import FTBEntry +from ..ref.FtqPtr import FTQSIZE, CircularQueuePtr +from ..ref.FtqRef import FTQ + +@toffee_test.testcase +async def test_bpu_enqueue(ftq_env): + # Get DUT and Ref, reset DUT + dut = ftq_env.dut + ref = FTQ() + await ftq_env.ftq_agent.reset5(ftq_env.dut) + await ftq_env.ftq_agent.set_write_mode_as_imme() + fallThruErrors_before = [] + for i in range(64): + fallThruErrors_before.append(dut.ftq_pc_mem[i]["fallThruError"].value) + print("bpuPtr_dut after reset: ", dut.gen_bpu_ptr()) + + for i in range(3): + print(f"----------------------- warm up --------------------------") + bpu_ptr = ref.bpu_ptr + port_dict_s1, port_dict_s2, port_dict_s3 = gen_bpu_resp(bpu_ptr) + port_dict_s1["valid"] = 1 + port_dict_s2["valid_3"] = 0 + port_dict_s3["valid_3"] = 0 + await ftq_env.ftq_agent.drive_s1_full_signals(port_dict_s1) + await ftq_env.ftq_agent.drive_s2_full_signals(port_dict_s2) + await ftq_env.ftq_agent.drive_s3_full_signals(port_dict_s3) + await ftq_env.ftq_agent.drive_last_stage_ftb_entry_signals(gen_last_stage_ftb_entry_dict()) + await ftq_env.ftq_agent.drive_last_stage_spec_info_signals(gen_last_stage_spec_info_dict()) + await ftq_env.ftq_agent.drive_last_stage_meta_signals() + dut.RefreshComb() + bpu_in_fire = check_with_ref_before_write(dut) + selected_resp = ftq_env.ftq_agent.bundle.fromBpuNew.selected_resp() + last_stage_spec_info = ftq_env.ftq_agent.bundle.fromBpuNew.last_stage_spec_info + last_stage_ftb_entry = ftq_env.ftq_agent.bundle.fromBpuNew.last_stage_ftb_entry + print("selected_resp: ", hex(selected_resp.pc_3.value), selected_resp.full_pred_3_fallThroughErr.value, selected_resp.full_pred_3_hit.value) + selected_stage = check_bpu_in_stage(dut) + update_ftq_ref_state(bpu_in_fire, selected_stage, selected_resp, last_stage_spec_info, last_stage_ftb_entry, ref, dut) + dut.Step() + if i == 0: + dut.Step() + # Check bpuPtr update + print("bpuptr: ", ref.bpu_ptr) + assert ref.bpu_ptr == dut.gen_bpu_ptr() + + for i in range(18000): + print(f"----------------------- Cycle {i} --------------------------") + bpu_ptr = ref.bpu_ptr + port_dict_s1, port_dict_s2, port_dict_s3 = gen_bpu_resp(bpu_ptr) + + await ftq_env.ftq_agent.drive_s1_full_signals(port_dict_s1) + await ftq_env.ftq_agent.drive_s2_full_signals(port_dict_s2) + await ftq_env.ftq_agent.drive_s3_full_signals(port_dict_s3) + await ftq_env.ftq_agent.drive_last_stage_ftb_entry_signals(gen_last_stage_ftb_entry_dict()) + await ftq_env.ftq_agent.drive_last_stage_spec_info_signals(gen_last_stage_spec_info_dict()) + await ftq_env.ftq_agent.drive_last_stage_meta_signals() + + dut.RefreshComb() + bpu_in_fire = check_with_ref_before_write(dut) + selected_resp = ftq_env.ftq_agent.bundle.fromBpuNew.selected_resp() + last_stage_spec_info = ftq_env.ftq_agent.bundle.fromBpuNew.last_stage_spec_info + last_stage_ftb_entry = ftq_env.ftq_agent.bundle.fromBpuNew.last_stage_ftb_entry + selected_stage = check_bpu_in_stage(dut) + check_every_cycle(dut, selected_stage, selected_resp) + update_ftq_ref_state(bpu_in_fire, selected_stage, selected_resp, last_stage_spec_info, last_stage_ftb_entry, ref, dut) + dut.Step() + # Check bpuPtr update + print("bpuptr_ref: ", ref.bpu_ptr) + print("bpuptr_dut: ", dut.gen_bpu_ptr()) + assert ref.bpu_ptr == dut.gen_bpu_ptr() + assert ref.ifu_ptr == dut.gen_ifu_ptr() + assert ref.pf_ptr == dut.gen_pf_ptr() + bpu_ptr = ref.bpu_ptr + port_dict_s1, port_dict_s2, port_dict_s3 = gen_bpu_resp(bpu_ptr) + port_dict_s1["valid"] = 0 + port_dict_s2["valid_3"] = 0 + port_dict_s3["valid_3"] = 0 + await ftq_env.ftq_agent.drive_s1_full_signals(port_dict_s1) + await ftq_env.ftq_agent.drive_s2_full_signals(port_dict_s2) + await ftq_env.ftq_agent.drive_s3_full_signals(port_dict_s3) + # await ftq_env.ftq_agent.drive_last_stage_ftb_entry_signals(gen_last_stage_ftb_entry_dict()) + # await ftq_env.ftq_agent.drive_last_stage_spec_info_signals(gen_last_stage_spec_info_dict()) + dut.Step(10) + # Check FTQ PC memory + all_kind_errors = check_with_ref_after_write(dut, ref) + for erros in all_kind_errors: + for error in erros: + print(error) + + + +def check_with_ref_before_write(dut): + dut.RefreshComb() + check_resp_ready(dut) + resp_fire = bpu_resp_fire_ref(dut) + allow_bpu_in = check_allow_bpu_in(dut) + bpu_in_fire = check_bpu_in_fire(dut) + return bpu_in_fire + +def check_every_cycle(dut, selected_stage, selected_resp): + dut.RefreshComb() + selected_stage = selected_stage_ref(dut) + if selected_stage == 2: + assert dut.io_toIfu_flushFromBpu_s3_valid.value == 1 + assert dut.io_toIfu_flushFromBpu_s3_bits_value.value == selected_resp.ftq_idx_value.value + assert dut.io_toIfu_flushFromBpu_s3_bits_flag.value == selected_resp.ftq_idx_flag.value + assert dut.io_toPrefetch_flushFromBpu_s3_valid.value == 1 + assert dut.io_toPrefetch_flushFromBpu_s3_bits_value.value == selected_resp.ftq_idx_value.value + assert dut.io_toPrefetch_flushFromBpu_s3_bits_flag.value == selected_resp.ftq_idx_flag.value + elif selected_stage == 1: + assert dut.io_toIfu_flushFromBpu_s2_valid.value == 1 + assert dut.io_toIfu_flushFromBpu_s2_bits_value.value == selected_resp.ftq_idx_value.value + assert dut.io_toIfu_flushFromBpu_s2_bits_flag.value == selected_resp.ftq_idx_flag.value + assert dut.io_toPrefetch_flushFromBpu_s2_valid.value == 1 + assert dut.io_toPrefetch_flushFromBpu_s2_bits_value.value == selected_resp.ftq_idx_value.value + assert dut.io_toPrefetch_flushFromBpu_s2_bits_flag.value == selected_resp.ftq_idx_flag.value + + +def check_ftq_pc_entry(ref_entry, dut_entry, idx): + errors = [] + + if ref_entry.startAddr != dut_entry["startAddr"].value: + errors.append( + f"[FTQ_PC][{idx}] startAddr mismatch: " + f"ref={ref_entry.startAddr:#x}, dut={dut_entry['startAddr'].value:#x}" + ) + + if ref_entry.nextLineAddr != dut_entry["nextLineAddr"].value: + errors.append( + f"[FTQ_PC][{idx}] nextLineAddr mismatch: " + f"ref={ref_entry.nextLineAddr:#x}, dut={dut_entry['nextLineAddr'].value:#x}" + ) + + if (ref_entry.fallThruError) != (dut_entry["fallThruError"].value): + errors.append( + f"[FTQ_PC][{idx}] fallThruError mismatch: " + f"ref={ref_entry.fallThruError}, dut={dut_entry['fallThruError'].value}" + ) + + return errors + +def check_ftq_pc_mem(ref_mem:FTQ, dut_ftq_pc_mem): + all_errors = [] + + for i in range(FTQSIZE): + ref_entry = ref_mem.ftq_pc_mem.read(True, i) + dut_entry = dut_ftq_pc_mem[i] + + errs = check_ftq_pc_entry(ref_entry, dut_entry, i) + all_errors.extend(errs) + + return all_errors + +def check_ftq_redirect_entry(ref_entry, dut_entry, idx): + errors = [] + + fields = [ + "NOS_flag", "NOS_value", "TOSR_flag", "TOSR_value", + "TOSW_flag", "TOSW_value", "histPtr_flag", "histPtr_value", + "sc_disagree_0", "sc_disagree_1", "sctr", "ssp", "topAddr" + ] + + for field in fields: + ref_value = getattr(ref_entry, field) + dut_value = dut_entry[field].value + + if ref_value != dut_value: + errors.append( + f"[FTQ_REDIRECT][{idx}] {field} mismatch: " + f"ref={ref_value}, dut={dut_value}" + ) + + return errors + +def check_ftq_redirect_mem(ref_mem, dut_ftq_redirect_mem): + all_errors = [] + + for i in range(64): + ref_entry = ref_mem.ftq_redirect_mem.read(i) + dut_entry = dut_ftq_redirect_mem[i] + + errs = check_ftq_redirect_entry(ref_entry, dut_entry, i) + all_errors.extend(errs) + + return all_errors + +def check_ftb_entry(ref_entry, dut_entry, idx): + errors = [] + + fields = [ + "isCall", "isRet", "isJalr", + "brSlots_0_offset", "brSlots_0_valid", + "tailSlot_offset", "tailSlot_sharing", "tailSlot_valid" + ] + + for field in fields: + ref_value = getattr(ref_entry, field) + + if dut_entry[field] is None: + print(f"FTB_ENTRY[{idx}] field {field} is None in DUT") + dut_value = dut_entry[field].value + + if ref_value != dut_value: + errors.append( + f"[FTB_ENTRY][{idx}] {field} mismatch: " + f"ref={ref_value}, dut={dut_value}" + ) + + return errors + +def check_ftb_entry_mem(ref_mem, dut_ftb_entry_mem): + all_errors = [] + + for i in range(64): + ref_entry = ref_mem.ftb_entry_mem.read(i) + dut_entry = dut_ftb_entry_mem[i] + + errs = check_ftb_entry(ref_entry, dut_entry, i) + all_errors.extend(errs) + + return all_errors + +# def check_ftq_meta_entry(ref_entry, dut_entry, idx): +# errors = [] + +# +# fields = [ +# "valid", "isCall", "isRet", "isJalr", "last_may_be_rvi_call", +# "carry", "pftAddr" +# ] + +# +# for field in fields: +# ref_value = getattr(ref_entry, field) +# dut_value = getattr(dut_entry, field) +# if ref_value != dut_value: +# errors.append(f"[FTQ_META][{idx}] {field} mismatch: ref={ref_value}, dut={dut_value}") + +# +# br_slot_fields = ["offset", "sharing", "valid", "lower", "tarStat"] +# for field in br_slot_fields: +# ref_value = getattr(ref_entry.ftb["brslot"], field) +# dut_value = dut_entry.ftb["brSlot"][field] +# if ref_value != dut_value: +# errors.append(f"[FTQ_META][{idx}] brSlot_{field} mismatch: ref={ref_value}, dut={dut_value}") + +# +# tail_slot_fields = ["offset", "sharing", "valid"] +# for field in tail_slot_fields: +# ref_value = getattr(ref_entry.ftb["tailslot"], field) +# dut_value = dut_entry.ftb["tailSlot"][field] +# if ref_value != dut_value: +# errors.append(f"[FTQ_META][{idx}] tailSlot_{field} mismatch: ref={ref_value}, dut={dut_value}") + +# return errors + + +# def check_ftq_meta_mem(ref_mem, dut_ftq_meta_mem): +# all_errors = [] + +# +# for i in range(64): +# ref_entry = ref_mem.ftq_meta_mem.read(i) +# dut_entry = dut_ftq_meta_mem[i] # DUT 中的 entry + +# errs = check_ftq_meta_entry(ref_entry, dut_entry, i) +# all_errors.extend(errs) + +# return all_errors + +def check_ftq_meta_entry(ref_entry, dut_entry, idx): + """ + Compare a single FTQ Meta Entry + ref_entry: Ftq_1R_SRAMEntry (Dataclass) + dut_entry: FtqMetaEntry + """ + errors = [] + + # 1. compare meta + if ref_entry.meta != dut_entry.meta: + errors.append( + f"[FTQ_META][{idx}] meta mismatch: " + f"ref={hex(ref_entry.meta)}, dut={hex(dut_entry.meta)}" + ) + + # 2. compare attribute in entry + ftb_fields = [ + "valid", "isCall", "isRet", "isJalr", + "last_may_be_rvi_call", "carry", "pftAddr" + ] + + for field in ftb_fields: + ref_val = getattr(ref_entry.ftb_entry, field) + dut_val = dut_entry.ftb[field] + if ref_val != dut_val: + errors.append( + f"[FTQ_META][{idx}] ftb.{field} mismatch: " + f"ref={ref_val}, dut={dut_val}" + ) + + # 3. compare slots + slots_to_check = [ + ("brslot", "brSlot"), + ("tailslot", "tailSlot") + ] + + slot_fields = ["offset", "sharing", "valid", "lower", "tarStat"] + + for ref_slot_name, dut_slot_name in slots_to_check: + ref_slot_obj = getattr(ref_entry.ftb_entry, ref_slot_name) + dut_slot_dict = dut_entry.ftb[dut_slot_name] + + for field in slot_fields: + if field in dut_slot_dict: + ref_s_val = getattr(ref_slot_obj, field) + dut_s_val = dut_slot_dict[field] + + if ref_s_val != dut_s_val: + errors.append( + f"[FTQ_META][{idx}] {dut_slot_name}.{field} mismatch: " + f"ref={ref_s_val}, dut={dut_s_val}" + ) + + return errors + +def check_ftq_meta_mem(ref_mem, dut_ftq_meta_mem_list): + all_errors = [] + ref_mem_obj = ref_mem.ftq_meta_mem + for i in range(64): + ref_entry = ref_mem_obj.read(i) + dut_entry = dut_ftq_meta_mem_list[i] + + errs = check_ftq_meta_entry(ref_entry, dut_entry, i) + all_errors.extend(errs) + + if not all_errors: + print("[FTQ_META] All 64 entries match perfectly.") + else: + print(f"[FTQ_META] Found {len(all_errors)} mismatches.") + + return all_errors + +def check_update_targets(ref, dut_update_targets): + errors = [] + for i in range(64): + ref_value = ref.update_targets[i] + dut_value = dut_update_targets[i].value + if ref_value != dut_value: + errors.append( + f"[UPDATE_TARGETS][{i}] mismatch: " + f"ref={ref_value}, dut={dut_value}" + ) + return errors + +def check_cfi_index_vec(ref, dut_cfi_index_vec): + errors = [] + for i in range(64): + ref_entry = ref.cfiIndex_vec[i] + dut_entry = dut_cfi_index_vec[i] + if ref_entry["valid"] != dut_entry["valid"].value: + errors.append( + f"[CFI_INDEX_VEC][{i}] valid mismatch: " + f"ref={ref_entry['valid']}, dut={dut_entry['valid'].value}" + ) + if ref_entry["bits"] != dut_entry["bits"].value: + errors.append( + f"[CFI_INDEX_VEC][{i}] bits mismatch: " + f"ref={ref_entry['bits']}, dut={dut_entry['bits'].value}" + ) + return errors + +def check_mispredict_vec(ref, dut_mispredict_vecs): + errors = [] + for i in range(64): + for j in range(16): + ref_value = ref.mispredict_vecs[i][j] + dut_value = dut_mispredict_vecs[i][j].value + if ref_value != dut_value: + errors.append( + f"[MISPREDICT_VEC][{i}][{j}] mismatch: " + f"ref={ref_value}, dut={dut_value}" + ) + return errors + +def check_pred_stage_queue(ref, dut_pred_stages): + errors = [] + for i in range(64): + ref_value = ref.pred_stages[i] + dut_value = dut_pred_stages[i].value + if ref_value != dut_value: + errors.append( + f"[PRED_STAGE_QUEUE][{i}] mismatch: " + f"ref={ref_value}, dut={dut_value}" + ) + return errors + +def check_entry_fetch_status(ref, dut_entry_fetch_status): + errors = [] + for i in range(64): + ref_value = ref.entry_fetch_status[i] + dut_value = dut_entry_fetch_status[i].value + if ref_value != dut_value: + errors.append( + f"[ENTRY_FETCH_STATUS][{i}] mismatch: " + f"ref={ref_value}, dut={dut_value}" + ) + return errors + +def check_entry_hit_status(ref, dut_entry_hit_status): + errors = [] + for i in range(64): + ref_value = ref.entry_hit_status[i] + dut_value = dut_entry_hit_status[i].value + if ref_value != dut_value: + errors.append( + f"[ENTRY_HIT_STATUS][{i}] mismatch: " + f"ref={ref_value}, dut={dut_value}" + ) + return errors + +def check_with_ref_after_write(dut, ref): + ftq_pc_mem_errors = check_ftq_pc_mem(ref, dut.ftq_pc_mem) + ftq_redirect_mem_errors = check_ftq_redirect_mem(ref, dut.ftq_redirect_mem) + ftb_entry_mem_errors = check_ftb_entry_mem(ref, dut.ftb_entry_mem) + # ftq_meta_mem_errors = check_ftq_meta_mem(ref, dut.ftq_meta_mem) + update_target_errors = check_update_targets(ref, dut.update_targets) + cfi_index_vec_errors = check_cfi_index_vec(ref, dut.cfiIndex_vec) + mispredict_vec_errors = check_mispredict_vec(ref, dut.mispredict_vecs) + pred_stage_errors = check_pred_stage_queue(ref, dut.pred_stages) + entry_fetch_status_errors = check_entry_fetch_status(ref, dut.entry_fetch_status) + return ftq_pc_mem_errors, ftq_redirect_mem_errors, ftb_entry_mem_errors, \ + update_target_errors, cfi_index_vec_errors, mispredict_vec_errors, pred_stage_errors, entry_fetch_status_errors + +def write_into_ftq_pc_mem(bpu_in_fire, selected_stage, selected_resp, ref: FTQ): + if selected_stage == 0: + ftq_idx_value = ref.bpu_ptr.value + else: + ftq_idx_value = selected_resp.ftq_idx_value.value + print("write into ftq pc mem at idx:", ftq_idx_value) + ref.ftq_pc_mem.write(bpu_in_fire, ftq_idx_value, Ftq_RF_Components.from_branch_prediction(selected_resp)) + +def write_into_ftq_redirect_mem(last_stage_valid, last_stage_idx, last_stage_spec_info, ref: FTQ): + # print("write into redirect mem at idx:", ftq_idx_value) + # print("last_stage_idx:", last_stage_idx) + # print("last_stage_valid:", last_stage_valid) + # print("last_stage_info:", Ftq_Redirect_SRAMEntry.from_spec_info(last_stage_spec_info)) + ref.ftq_redirect_mem.write(last_stage_valid, last_stage_idx, Ftq_Redirect_SRAMEntry.from_spec_info(last_stage_spec_info)) + +def write_into_ftb_entry_mem(last_stage_valid, last_stage_idx, last_stage_ftb_entry, ref: FTQ): + ref.ftb_entry_mem.write(last_stage_valid, last_stage_idx, FTBEntry.from_last_stage_ftb_entry(last_stage_ftb_entry)) + +def write_into_ftq_meta_mem(last_stage_valid, last_stage_idx, last_stage_meta, last_stage_meta_ftb_entry, ref: FTQ): + ref.ftq_meta_mem.write(last_stage_valid, last_stage_idx, Ftq_1R_SRAMEntry.from_meta_and_ftb(last_stage_meta, last_stage_meta_ftb_entry)) + + +def write_into_update_targets(bpu_in_fire, idx, selected_resp: BranchPredictionBundle, ref: FTQ): + if not bpu_in_fire: + return + else: + ref.update_targets[idx] = getTaget_ref(selected_resp) + +def write_into_cfi_index_vec(bpu_in_fire, idx, selected_resp: BranchPredictionBundle, ref: FTQ): + if not bpu_in_fire: + return + else: + ref.cfiIndex_vec[idx] = getCfi_ref(selected_resp) + +def write_into_mispredict_vec(bpu_in_fire, idx, ref: FTQ): + if not bpu_in_fire: + return + else: + ref.mispredict_vecs[idx] = [0] * 16 + +def write_into_pred_stage_queue(bpu_in_fire, idx, selected_stage, ref: FTQ): + if not bpu_in_fire: + return + else: + ref.pred_stages[idx] = selected_stage + +def write_into_entry_fetch_status(bpu_in_fire, idx, ref: FTQ): + if not bpu_in_fire: + return + else: + ref.entry_fetch_status[idx] = 0 + +def write_into_entry_hit_status(idx, dut, ref: FTQ): + if dut.io_fromBpu_resp_bits_s2_valid_3.value: + ref.entry_hit_status[idx] = dut.io_fromBpu_resp_bits_s2_entry_hit.value + +def update_ftq_ref_state(bpu_in_fire, selected_stage, selected_resp: BranchPredictionBundle, last_stage_spec_info, last_stage_ftb_entry, ref: FTQ, dut): + if selected_stage == 0: + ftq_idx_value = ref.bpu_ptr.value + else: + ftq_idx_value = selected_resp.ftq_idx_value.value + write_into_ftq_pc_mem(bpu_in_fire, selected_stage, selected_resp, ref) + write_into_ftq_redirect_mem(dut.io_fromBpu_resp_bits_s3_valid_3.value, dut.io_fromBpu_resp_bits_s3_ftq_idx_value.value, last_stage_spec_info, ref) + write_into_ftb_entry_mem(dut.io_fromBpu_resp_bits_s3_valid_3.value, dut.io_fromBpu_resp_bits_s3_ftq_idx_value.value, last_stage_ftb_entry, ref) + write_into_ftq_meta_mem(dut.io_fromBpu_resp_bits_s3_valid_3.value, dut.io_fromBpu_resp_bits_s3_ftq_idx_value.value, dut.io_fromBpu_resp_bits_last_stage_meta.value, Full_FTBEntry.from_last_stage_ftb_entry(last_stage_ftb_entry), ref) + update_ftq_ref_bpu_ptr(bpu_in_fire, selected_stage, selected_resp, ref) + update_ifu_ptr_when_redirect_ref(selected_stage, selected_resp, ref) + update_prefetch_ptr_when_redirect_ref(selected_stage, selected_resp, ref) + + write_into_update_targets(bpu_in_fire, ftq_idx_value, selected_resp, ref) + write_into_cfi_index_vec(bpu_in_fire, ftq_idx_value, selected_resp, ref) + write_into_mispredict_vec(bpu_in_fire, ftq_idx_value, ref) + write_into_pred_stage_queue(bpu_in_fire, ftq_idx_value, selected_stage, ref) + write_into_entry_fetch_status(bpu_in_fire, ftq_idx_value, ref) + +def update_ftq_ref_bpu_ptr(bpu_in_fire, selected_stage, selected_resp: BranchPredictionBundle, ref: FTQ): + if not bpu_in_fire: + return + if selected_stage == 0: + ref.bpu_ptr += 1 + else: + print("redirect bpu ptr:", CircularQueuePtr(FTQSIZE, selected_resp.ftq_idx_flag.value, selected_resp.ftq_idx_value.value) ) + ref.bpu_ptr = CircularQueuePtr(FTQSIZE, selected_resp.ftq_idx_flag.value, selected_resp.ftq_idx_value.value) + 1 + +def update_ifu_ptr_when_redirect_ref(selected_stage, selected_resp: BranchPredictionBundle, ref: FTQ): + if selected_stage == 0: + return + updated_ptr = CircularQueuePtr(FTQSIZE, selected_resp.ftq_idx_flag.value, selected_resp.ftq_idx_value.value) + if(not(ref.ifu_ptr < (updated_ptr))): + ref.ifu_ptr = updated_ptr + ref.pf_ptr = updated_ptr + +def update_prefetch_ptr_when_redirect_ref(selected_stage, selected_resp: BranchPredictionBundle, ref: FTQ): + if selected_stage == 0: + return + updated_ptr = CircularQueuePtr(FTQSIZE, selected_resp.ftq_idx_flag.value, selected_resp.ftq_idx_value.value) + if(not(ref.pf_ptr < (updated_ptr))): + ref.pf_ptr = updated_ptr + +def check_resp_ready(dut): + assert dut.io_fromBpu_resp_ready.value == bpu_resp_ready_ref(dut) + +def check_allow_bpu_in(dut): + allow_bpu_in = allow_bpu_in_ref(dut) + assert dut.allowBpuIn.value == allow_bpu_in + return allow_bpu_in + +def check_bpu_in_fire(dut): + bpu_in_fire = bpu_in_fire_ref(dut) + assert dut.bpu_in_fire.value == bpu_in_fire + return bpu_in_fire + +def check_bpu_in_stage(dut): + selected_stage = selected_stage_ref(dut) + assert dut.bpu_in_stage.value == selected_stage + return selected_stage + + diff --git a/ut_frontend/ftq/ftq_top/test/test_ftq_top3.py b/ut_frontend/ftq/ftq_top/test/test_ftq_top3.py index b89029da..f13bce8f 100644 --- a/ut_frontend/ftq/ftq_top/test/test_ftq_top3.py +++ b/ut_frontend/ftq/ftq_top/test/test_ftq_top3.py @@ -56,6 +56,8 @@ async def test_example_integration(ftq_env): fallThruError=s3_packet.fallThruError ) await ftq_env.ftq_agent.bundle.step(1) + assert dut.io_fromBpu_resp_bits_s1_pc_3.value == s1_packet.pc + print(f"{s1_packet.pc} == {dut.io_fromBpu_resp_bits_s1_pc_3.value}?") s3_redirect_fire = s3_valid and s3_hasRedirect s2_redirect_fire = s2_valid and s2_hasRedirect s1_enqueue_fire = s1_valid and await ftq_env.ftq_agent.get_fromBpu_resp_ready() diff --git a/ut_frontend/ftq/ftq_top/test/top_test_fixture.py b/ut_frontend/ftq/ftq_top/test/top_test_fixture.py index 8fe174cf..243dad63 100644 --- a/ut_frontend/ftq/ftq_top/test/top_test_fixture.py +++ b/ut_frontend/ftq/ftq_top/test/top_test_fixture.py @@ -1,19 +1,23 @@ -import random import toffee_test +from ut_frontend.ftq.ftq_top.env.ftq_bundle import BranchPredictionResp, FromBpuBundle, FromIfuBundle, ToICacheBundle, ToIfuBundle, ToPrefetchBundle, toBpuBundle +from ut_frontend.ftq.ftq_top.test.ftq_cover_points2 import * from ..env import FtqBundle from ..env import FtqEnv import toffee +from toffee import * from dut.FtqTop import DUTFtqTop -import toffee.funcov as fc -from toffee.funcov import CovGroup from .ftq_cover_points import ftq_cover_points +from .utils import * + + class NewDUTFtqTop(DUTFtqTop): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.newest_entry_target = self.GetInternalSignal("FtqTop_top.Ftq.newest_entry_target") self.newest_entry_ptr_value = self.GetInternalSignal("FtqTop_top.Ftq.newest_entry_ptr_value") + self.newest_entry_ptr_flag = self.GetInternalSignal("FtqTop_top.Ftq.newest_entry_ptr_flag") self.newest_entry_target_modified = self.GetInternalSignal("FtqTop_top.Ftq.newest_entry_target_modified") self.has_false_hit = self.GetInternalSignal("FtqTop_top.Ftq.has_false_hit") self.ifu_redirect_valid = self.GetInternalSignal("FtqTop_top.Ftq.fromIfuRedirect_valid_probe") @@ -23,7 +27,7 @@ def __init__(self, *args, **kwargs): self.ifu_redirect_target = self.GetInternalSignal("FtqTop_top.Ftq.ifuRedirectReg_next_bits_r_cfiUpdate_target") self.ifu_redirect_taken = self.GetInternalSignal("FtqTop_top.Ftq.ifuRedirectReg_next_bits_r_cfiUpdate_taken") self.ifu_flush = self.GetInternalSignal("FtqTop_top.Ftq.ifuFlush") - self.ifu_redirect_ftq_idx = self.GetInternalSignal("FtqTop_top.Ftq.ifuRedirectReg_next_bits_r_ftqIdx_value") + self.ifu_redirect_ftq_idx = self.GetInternalSignal("FtqTop_top.Ftq.__Vtogcov__ifuRedirectReg_next_bits_r_ftqIdx_value") self.ifu_redirect_ftq_offset = self.GetInternalSignal("FtqTop_top.Ftq.ifuRedirectReg_next_bits_r_ftqOffset") self.tobackend_newest_entry_en = self.GetInternalSignal("FtqTop_top.io_toBackend_newest_entry_en") self.tobackend_newest_entry_ptr = self.GetInternalSignal("FtqTop_top.io_toBackend_newest_entry_ptr_value") @@ -37,6 +41,11 @@ def __init__(self, *args, **kwargs): self.toBpu_redirect_bits_cfiUpdate_shift = self.GetInternalSignal("FtqTop_top.io_toBpu_redirect_bits_cfiUpdate_shift") self.toBpu_redirect_bits_cfiUpdate_addIntoHist = self.GetInternalSignal("FtqTop_top.io_toBpu_redirect_bits_cfiUpdate_addIntoHist") self.bpu_ptr = self.GetInternalSignal("FtqTop_top.Ftq.bpuPtr_value") + self.bpu_ptr_flag = self.GetInternalSignal("FtqTop_top.Ftq.bpuPtr_flag") + self.ifu_ptr = self.GetInternalSignal("FtqTop_top.Ftq.ifuPtr_value") + self.ifu_ptr_flag = self.GetInternalSignal("FtqTop_top.Ftq.ifuPtr_flag") + self.pf_ptr = self.GetInternalSignal("FtqTop_top.Ftq.pfPtr_value") + self.pf_ptr_flag = self.GetInternalSignal("FtqTop_top.Ftq.pfPtr_flag") self.ifu_ptr_write = self.GetInternalSignal("FtqTop_top.Ftq.ifuPtr_write_value") self.ifu_wb_ptr_write = self.GetInternalSignal("FtqTop_top.Ftq.ifuWbPtr_value") self.ifu_ptr_plus1_write = self.GetInternalSignal("FtqTop_top.Ftq.ifuPtrPlus1_value") @@ -50,6 +59,28 @@ def __init__(self, *args, **kwargs): self.toifu_redirect_ftqIdx_value = self.GetInternalSignal("FtqTop_top.io_toIfu_redirect_bits_ftqIdx_value") self.toifu_redirect_ftqOffset = self.GetInternalSignal("FtqTop_top.io_toIfu_redirect_bits_ftqOffset") self.toifu_redirect_level = self.GetInternalSignal("FtqTop_top.io_toIfu_redirect_bits_level") + + + self.canCommit = self.GetInternalSignal("FtqTop_top.Ftq.__Vtogcov__canCommit") + self.comm_ptr = self.GetInternalSignal("FtqTop_top.Ftq.commPtr_value") + self.comm_ptr_flag = self.GetInternalSignal("FtqTop_top.Ftq.commPtr_flag") + self.backendRedirect = self.GetInternalSignal("FtqTop_top.io_fromBackend_redirect_valid") + self.backendRedirectReg = self.GetInternalSignal("FtqTop_top.Ftq.backendRedirectReg_valid_REG") + self.allowBpuIn = self.GetInternalSignal("FtqTop_top.Ftq.allowBpuIn") + self.bpu_in_fire = self.GetInternalSignal("FtqTop_top.Ftq.bpu_in_fire") + self.bpu_in_stage = self.GetInternalSignal("FtqTop_top.Ftq.bpu_in_stage") + self.ifuRedirectToBpu_valid = self.GetInternalSignal("FtqTop_top.Ftq.ifuRedirectReg_next_valid_last_REG") + self.ifu_wb_ptr = self.GetInternalSignal("FtqTop_top.Ftq.ifuWbPtr_value") + self.ifu_wb_ptr_flag = self.GetInternalSignal("FtqTop_top.Ftq.ifuWbPtr_flag") + self.robCommPtr_flag = self.GetInternalSignal("FtqTop_top.Ftq.robCommPtr_flag") + self.robCommPtr_value = self.GetInternalSignal("FtqTop_top.Ftq.robCommPtr_value") + self.mmioFtqPtr_flag = self.GetInternalSignal("FtqTop_top.io_mmioCommitRead_mmioFtqPtr_flag") + self.mmioFtqPtr_value = self.GetInternalSignal("FtqTop_top.io_mmioCommitRead_mmioFtqPtr_value") + self.mmioLastCommit = self.GetInternalSignal("FtqTop_top.io_mmioCommitRead_mmioLastCommit") + self.bpu_ftb_update_stall = self.GetInternalSignal("FtqTop_top.Ftq.bpu_ftb_update_stall") + self.validInstructions = [None] * 16 + for i in range(15): + self.validInstructions[i + 1] = self.GetInternalSignal(f"FtqTop_top.Ftq.validInstructions_{i + 1}") def get_update_target(idx): return self.GetInternalSignal(f"FtqTop_top.Ftq.update_target_{idx}") @@ -64,21 +95,335 @@ def get_mispredict_vec(idx, offset): def get_commit_state_queue_reg(ftq_idx, offset): return self.GetInternalSignal(f"FtqTop_top.Ftq.commitStateQueueReg_{ftq_idx}_{offset}") - + self.get_update_target = get_update_target self.get_cfi_index_bits = get_cfi_index_bits self.get_cfi_index_valid = get_cfi_index_valid self.get_mispredict_vec = get_mispredict_vec self.get_commit_state_queue_reg = get_commit_state_queue_reg + self.ftq_pc_mem_io_commPtrPlus1_rdata_startAddr = self.GetInternalSignal("FtqTop_top.Ftq._ftq_pc_mem_io_commPtrPlus1_rdata_startAddr") + self.ftq_pc_mem_io_commPtr_rdata_startAddr = self.GetInternalSignal("FtqTop_top.Ftq._ftq_pc_mem_io_commPtr_rdata_startAddr") + + + + + #================================================= + # Connected to FTQ SUB QUEUES + #================================================= + + # ftq_pc_mem + self.ftq_pc_mem = [ + {} for _ in range(64) + ] + fields = ["fallThruError", "nextLineAddr", "startAddr"] + for waddr in range(64): + bank = waddr // 16 + entry = waddr % 16 + + for field in fields: + sig = ( + f"FtqTop_top.Ftq.ftq_pc_mem.mem." + f"dataBanks_{bank}.data_{entry}_{field}" + ) + self.ftq_pc_mem[waddr][field] = self.GetInternalSignal(sig) + + # ftq_redirect_mem + self.ftq_redirect_mem = [ + {} for _ in range(64) + ] + fields = [ + "NOS_flag", + "NOS_value", + "TOSR_flag", + "TOSR_value", + "TOSW_flag", + "TOSW_value", + "histPtr_flag", + "histPtr_value", + "sc_disagree_0", + "sc_disagree_1", + "sctr", + "ssp", + "topAddr", + ] + for waddr in range(64): + bank = waddr // 16 + entry = waddr % 16 + + for field in fields: + sig = ( + f"FtqTop_top.Ftq.ftq_redirect_mem." + f"dataBanks_{bank}.data_{entry}_{field}" + ) + self.ftq_redirect_mem[waddr][field] = self.GetInternalSignal(sig) + + # ftb_entry_mem + self.ftb_entry_mem = [ + {} for _ in range(64) + ] + fields = [ + "isCall", + "isRet", + "isJalr", + "brSlots_0_offset", # 4 bits + "brSlots_0_valid", + "tailSlot_offset", # 4 bits + "tailSlot_sharing", + "tailSlot_valid", + ] + + + for waddr in range(64): + bank = waddr // 16 + entry = waddr % 16 + + for field in fields: + sig = ( + f"FtqTop_top.Ftq.ftb_entry_mem." + f"dataBanks_{bank}.data_{entry}_{field}" + ) + self.ftb_entry_mem[waddr][field] = self.GetInternalSignal(sig) + + # # ---------- brSlots ---------- + # self.ftb_entry_mem[waddr]["brSlots_0"]["offset"] = \ + # self.GetInternalSignal(f"{base}_brSlots_0_offset") + + # self.ftb_entry_mem[waddr]["brSlots_0"]["valid"] = \ + # self.GetInternalSignal(f"{base}_brSlots_0_valid") + + # # ---------- entry flags ---------- + # self.ftb_entry_mem[waddr]["isCall"] = \ + # self.GetInternalSignal(f"{base}_isCall") + + # self.ftb_entry_mem[waddr]["isJalr"] = \ + # self.GetInternalSignal(f"{base}_isJalr") + + # self.ftb_entry_mem[waddr]["isRet"] = \ + # self.GetInternalSignal(f"{base}_isRet") + + # # ---------- tailSlot ---------- + # self.ftb_entry_mem[waddr]["tailSlot"] = {} + + # self.ftb_entry_mem[waddr]["tailSlot"]["offset"] = \ + # self.GetInternalSignal(f"{base}_tailSlot_offset") + # self.ftb_entry_mem[waddr]["tailSlot"]["sharing"] = \ + # self.GetInternalSignal(f"{base}_tailSlot_sharing") + # self.ftb_entry_mem[waddr]["tailSlot"]["valid"] = \ + # self.GetInternalSignal(f"{base}_tailSlot_valid") + + #================================================= + # Connected to FTQ STATUS QUEUES + #================================================= + self.update_targets = [None] * 64 + for i in range(64): + self.update_targets[i] = self.GetInternalSignal(f"FtqTop_top.Ftq.update_target_{i}") + + self.cfiIndex_vec = [{} for _ in range(64)] + fields = [ + "bits", + "valid", + ] + for i in range(64): + for field in fields: + sig = f"FtqTop_top.Ftq.cfiIndex_vec_{i}_{field}" + self.cfiIndex_vec[i][field] = self.GetInternalSignal(sig) + + self.mispredict_vecs = [[None for _ in range(16)] for _ in range(64)] + for i in range(64): + for j in range(16): + sig = f"FtqTop_top.Ftq.mispredict_vec_{i}_{j}" + self.mispredict_vecs[i][j] = self.GetInternalSignal(sig) + + self.pred_stages = [ None for _ in range(64)] + for i in range(64): + sig = f"FtqTop_top.Ftq.pred_stage_{i}" + self.pred_stages[i] = self.GetInternalSignal(sig) + + self.commitStateQueue = [[None for _ in range(16)] for _ in range(64)] + for i in range(64): + for j in range(16): + sig = f"FtqTop_top.Ftq.commitStateQueueReg_{i}_{j}" + self.commitStateQueue[i][j] = self.GetInternalSignal(sig) + + self.entry_fetch_status = [ None for _ in range(64)] + for i in range(64): + sig = f"FtqTop_top.Ftq.entry_fetch_status_{i}" + self.entry_fetch_status[i] = self.GetInternalSignal(sig) + + self.entry_hit_status = [ None for _ in range(64)] + for i in range(64): + sig = f"FtqTop_top.Ftq.entry_hit_status_{i}" + self.entry_hit_status[i] = self.GetInternalSignal(sig) + + + fields = [ + "isCall", + "isRet", + "isJalr", + "valid", + "brSlots_0_offset", + "brSlots_0_sharing", + "brSlots_0_valid", + "brSlots_0_lower", + "brSlots_0_tarStat", + "tailSlot_offset", + "tailSlot_sharing", + "tailSlot_valid", + "tailSlot_lower", + "tailSlot_tarStat", + "pftAddr", + "carry", + "last_may_be_rvi_call", + "strong_bias_1", + "strong_bias_0", + ] + self.ftq_meta_1r_sram_io_rdata_0_ftb_entry = { + field: self.GetInternalSignal(f"FtqTop_top.Ftq.ftq_meta_1r_sram.__Vtogcov__io_rdata_0_ftb_entry_{field}") + for field in fields + } + + + self.ftq_pd_mem_io_rdata_1 = {} + self.ftq_pd_mem_io_rdata_1["brMask"] = [self.GetInternalSignal(f"FtqTop_top.Ftq.ftq_pd_mem.__Vtogcov__io_rdata_1_brMask_{i}") for i in range(16)] + self.ftq_pd_mem_io_rdata_1["rvcMask"] =[self.GetInternalSignal(f"FtqTop_top.Ftq.ftq_pd_mem.__Vtogcov__io_rdata_1_rvcMask_{i}") for i in range(16)] + self.ftq_pd_mem_io_rdata_1["jmpInfo_bits"] =[self.GetInternalSignal(f"FtqTop_top.Ftq.ftq_pd_mem.__Vtogcov__io_rdata_1_jmpInfo_bits_{i}") for i in range(3)] + self.ftq_pd_mem_io_rdata_1["jmpInfo_valid"] = self.GetInternalSignal(f"FtqTop_top.Ftq.ftq_pd_mem.__Vtogcov__io_rdata_1_jmpInfo_valid") + self.ftq_pd_mem_io_rdata_1["jmpOffset"] = self.GetInternalSignal(f"FtqTop_top.Ftq.ftq_pd_mem.__Vtogcov__io_rdata_1_jmpOffset") + self.ftq_pd_mem_io_rdata_1["jalTarget"] = self.GetInternalSignal(f"FtqTop_top.Ftq.ftq_pd_mem.__Vtogcov__io_rdata_1_jalTarget") + self._ftq_pd_mem_io_rdata_1_jalTarget = self.GetInternalSignal(f"FtqTop_top.Ftq._ftq_pd_mem_io_rdata_1_jalTarget") + self.fromIfuRedirect_valid_probe = self.GetInternalSignal("FtqTop_top.Ftq.fromIfuRedirect_valid_probe") + self.realAhdValid = self.GetInternalSignal("FtqTop_top.Ftq.realAhdValid") + self.ftb_entry_gen_io_is_br_full = self.GetInternalSignal("FtqTop_top.Ftq._FTBEntryGen_io_is_br_full") + self.ftb_entry_gen_io_is_jalr_target_modified = self.GetInternalSignal("FtqTop_top.Ftq._FTBEntryGen_io_is_jalr_target_modified") + self.ftb_entry_gen_io_is_new_br = self.GetInternalSignal("FtqTop_top.Ftq._FTBEntryGen_io_is_new_br") + self.ftb_entry_gen_io_is_strong_bias_modified = self.GetInternalSignal("FtqTop_top.Ftq._FTBEntryGen_io_is_strong_bias_modified") + + #================================================= + # Generate different kinds of FTQ Pointers + #================================================= + + def gen_bpu_ptr(self) -> CircularQueuePtr: + return CircularQueuePtr(FTQSIZE, self.bpu_ptr_flag.value, self.bpu_ptr.value) + + def gen_ifu_ptr(self) -> CircularQueuePtr: + return CircularQueuePtr(FTQSIZE, self.ifu_ptr_flag.value, self.ifu_ptr.value) + + def gen_pf_ptr(self) -> CircularQueuePtr: + return CircularQueuePtr(FTQSIZE, self.pf_ptr_flag.value, self.pf_ptr.value) + + def gen_comm_ptr(self) -> CircularQueuePtr: + return CircularQueuePtr(FTQSIZE, self.comm_ptr_flag.value, self.comm_ptr.value) + + def gen_ifu_wb_ptr(self) -> CircularQueuePtr: + return CircularQueuePtr(FTQSIZE, self.ifu_wb_ptr_flag.value, self.ifu_wb_ptr.value) + + def gen_rob_comm_ptr(self) -> CircularQueuePtr: + return CircularQueuePtr(FTQSIZE, self.robCommPtr_flag.value, self.robCommPtr_value.value) + + def gen_mmio_ftq_ptr(self) -> CircularQueuePtr: + return CircularQueuePtr(FTQSIZE, self.mmioFtqPtr_flag.value, self.mmioFtqPtr_value.value) + + def gen_newest_entry_ptr(self) -> CircularQueuePtr: + return CircularQueuePtr(FTQSIZE, self.newest_entry_ptr_flag.value, self.newest_entry_ptr_value.value) + + def valid_entries(self) -> int: + return distance_between(self.gen_bpu_ptr(), self.gen_comm_ptr()) + + # These are helper functions when we want value list easily + def gen_ftq_meta_1r_sram_io_rdata_0_ftb_entry_value(self): + ftq_meta_1r_sram_io_rdata_0_ftb_entry_value = { + k: v.value for k, v in self.ftq_meta_1r_sram_io_rdata_0_ftb_entry.items() + } + return ftq_meta_1r_sram_io_rdata_0_ftb_entry_value + + def gen_ftq_pd_mem_io_rdata_1_value(self): + ftq_pd_mem_io_rdata_1_value = { + k: [x.value for x in v] if isinstance(v, list) else v.value + for k, v in self.ftq_pd_mem_io_rdata_1.items() + } + return ftq_pd_mem_io_rdata_1_value + + + +# @toffee_test.fixture +# async def ftq_env(toffee_request: toffee_test.ToffeeRequest): +# toffee.setup_logging(toffee.WARNING) +# dut = toffee_request.create_dut(NewDUTFtqTop,"clock") +# toffee.start_clock(dut) +# ftq_bundle = FtqBundle.from_prefix('io_') +# ftq_bundle.bind(dut) +# testcase_name = toffee_request.request.node.name +# print(f"current test case: {testcase_name}") +# toffee_request.add_cov_groups(ftq_cover_points(dut, ftq_bundle)) + +# # For test_ftq_top10 +# toffee_request.add_cov_groups([ +# to_bpu_redirect(dut), +# update_stall(dut), +# can_commit(dut), +# can_move_commit_ptr(dut), +# update_rob_commit_ptr(ftq_bundle.fromBackend), +# mmio_last_commit(dut), +# ftb_entry_gen_modify_old(dut), +# update_ftb_entry(dut), +# ]) +# # For test_ftq_top1 +# toffee_request.add_cov_groups([ +# ftq_get_bpu_resp_ready(dut), +# bpu_resp_valid(dut), +# bpu_in_fire(dut), +# not_allow_BPU_in(dut), +# allow_BPU_in_when_resp_redirect(dut), +# transfer_flush_to_IFU(dut), +# transfer_flush_to_Prefetch(dut), +# ]) +# yield FtqEnv(ftq_bundle, dut=dut) + @toffee_test.fixture async def ftq_env(toffee_request: toffee_test.ToffeeRequest): toffee.setup_logging(toffee.WARNING) dut = toffee_request.create_dut(NewDUTFtqTop,"clock") toffee.start_clock(dut) - ftq_bundle = FtqBundle.from_prefix('io_') - ftq_bundle.bind(dut) + testcase_name = toffee_request.request.node.name + print(f"current test case: {testcase_name}") + class FtqBundleDynamic(Bundle): + fromBackend = FromBackendBundle.from_prefix("fromBackend_") + fromIfu = FromIfuBundle.from_prefix("fromIfu_") + toIfu = ToIfuBundle.from_prefix("toIfu_") + toICache = ToICacheBundle.from_prefix("toICache_") + toPrefetch = ToPrefetchBundle.from_prefix("toPrefetch_") + toBpu = toBpuBundle.from_prefix("toBpu_") + + if testcase_name in ["test_bpu_enqueue", "test_commit_to_bpu"]: + fromBpuNew = BranchPredictionResp.from_prefix("fromBpu_resp_") + else: + fromBpu = FromBpuBundle.from_prefix("fromBpu_") + + ftq_bundle = FtqBundleDynamic.from_prefix('io_') + ftq_bundle.bind(dut) toffee_request.add_cov_groups(ftq_cover_points(dut, ftq_bundle)) - yield FtqEnv(ftq_bundle, dut=dut) - \ No newline at end of file + + # For test_ftq_top10 + toffee_request.add_cov_groups([ + to_bpu_redirect(dut), + update_stall(dut), + can_commit(dut), + can_move_commit_ptr(dut), + update_rob_commit_ptr(ftq_bundle.fromBackend), + mmio_last_commit(dut), + ftb_entry_gen_modify_old(dut), + update_ftb_entry(dut), + ]) + # For test_ftq_top1 + toffee_request.add_cov_groups([ + ftq_get_bpu_resp_ready(dut), + bpu_resp_valid(dut), + bpu_in_fire(dut), + not_allow_BPU_in(dut), + allow_BPU_in_when_resp_redirect(dut), + transfer_flush_to_IFU(dut), + transfer_flush_to_Prefetch(dut), + ]) + yield FtqEnv(ftq_bundle, dut=dut) \ No newline at end of file diff --git a/ut_frontend/ftq/ftq_top/test/utils.py b/ut_frontend/ftq/ftq_top/test/utils.py new file mode 100644 index 00000000..008be8ab --- /dev/null +++ b/ut_frontend/ftq/ftq_top/test/utils.py @@ -0,0 +1,956 @@ +import random + +from ut_frontend.ftq.ftq_top.env.ftq_bundle import BranchPredictionBundle, FromBackendBundle +from ut_frontend.ftq.ftq_top.ref.FtqRef import FTQ + +from ..ref.FtqPtr import CircularQueuePtr, distance_between +FTQSIZE = 64 + +#===================================== +# Generate input to assign the port +#===================================== +def gen_bpu_branch_resp_dict() -> dict: + return { + # 3 stages + "valid": random.randint(0, 1), + "pc_3": random.randint(0, (1 << 50) - 1), # 50 + "full_pred_3_br_taken_mask_0": random.randint(0, 1), + "full_pred_3_br_taken_mask_1": random.randint(0, 1), + "full_pred_3_slot_valids_0": random.randint(0, 1), + "full_pred_3_slot_valids_1": random.randint(0, 1), + "full_pred_3_targets_0": random.randint(0, (1 << 50) - 1), # 50 + "full_pred_3_targets_1": random.randint(0, (1 << 50) - 1), + "full_pred_3_offsets_0": random.randint(0, (1 << 4) - 1), + "full_pred_3_offsets_1": random.randint(0, (1 << 4) - 1), + "full_pred_3_fallThroughAddr": random.randint(0, (1 << 50) - 1), + "full_pred_3_fallThroughErr": random.randint(0, 1), + "full_pred_3_is_br_sharing": random.randint(0, 1), + "full_pred_3_hit": random.randint(0, 1), + # s2/s3 + "valid_3": random.randint(0, 1), + "hasRedirect_3": random.randint(0, 1), + "ftq_idx_flag": random.randint(0, 1), + "ftq_idx_value": random.randint(0, (1 << 6) - 1), # 6 + } + +def gen_last_stage_ftb_entry_dict() -> dict: + return { + "isCall": random.randint(0, 1), + "isRet": random.randint(0, 1), + "isJalr": random.randint(0, 1), + "valid": random.randint(0, 1), + "last_may_be_rvi_call": random.randint(0, 1), + "carry": random.randint(0, 1), + "pftAddr": random.randint(0, 15), # 4 bits + "brSlots_0_offset": random.randint(0, 15), # 4 bits + "brSlots_0_sharing": random.randint(0, 1), + "brSlots_0_valid": random.randint(0, 1), + "tailSlot_offset": random.randint(0, 15), # 4 bits + "tailSlot_sharing": random.randint(0, 1), + "tailSlot_valid": random.randint(0, 1), + } + +def gen_last_stage_spec_info_dict() -> dict: + return { + "histPtr_flag": random.randint(0, 1), + "histPtr_value": random.randint(0, (1 << 8) - 1), # 8 bits + "ssp": random.randint(0, (1 << 4) - 1), # 4 bits + "sctr": random.randint(0, (1 << 3) - 1), # 3 bits + "TOSW_flag": random.randint(0, 1), + "TOSW_value": random.randint(0, (1 << 5) - 1), # 5 bits + "TOSR_flag": random.randint(0, 1), + "TOSR_value": random.randint(0, (1 << 5) - 1), # 5 bits + "NOS_flag": random.randint(0, 1), + "NOS_value": random.randint(0, (1 << 5) - 1), # 5 bits + "topAddr": random.randint(0, (1 << 50) - 1), # 50 bits + "sc_disagree_0": random.randint(0, 1), + "sc_disagree_1": random.randint(0, 1) + } + + +def gen_bpu_resp(bpu_ptr: CircularQueuePtr = None): + port_dict_s1 = gen_bpu_branch_resp_dict() + if(bpu_ptr is not None): + bpuPtr = bpu_ptr + port_dict_s1["ftq_idx_flag"] = bpuPtr.flag + port_dict_s1["ftq_idx_value"] = bpuPtr.value + else: + bpuPtr = CircularQueuePtr(FTQSIZE, flag=port_dict_s1["ftq_idx_flag"], value=port_dict_s1["ftq_idx_value"]) + bpuPtr_sub1 = bpuPtr - 1 + bpuPtr_sub2 = bpuPtr_sub1 - 1 + port_dict_s2 = gen_bpu_branch_resp_dict() + port_dict_s3 = gen_bpu_branch_resp_dict() + + # Set constraint signals + port_dict_s2["ftq_idx_flag"] = bpuPtr_sub1.flag + port_dict_s2["ftq_idx_value"] = bpuPtr_sub1.value + port_dict_s3["ftq_idx_flag"] = bpuPtr_sub2.flag + port_dict_s3["ftq_idx_value"] = bpuPtr_sub2.value + + # Set redirect info not totally random + if random.random() < 0.5: + port_dict_s2["valid_3"] = 1 + port_dict_s2["hasRedirect_3"] = 1 + else: + port_dict_s2["valid_3"] = 0 + port_dict_s2["hasRedirect_3"] = 0 + + if random.random() < 0.5: + port_dict_s3["valid_3"] = 1 + port_dict_s3["hasRedirect_3"] = 1 + else: + port_dict_s3["valid_3"] = 0 + port_dict_s3["hasRedirect_3"] = 0 + + return port_dict_s1, port_dict_s2, port_dict_s3 + +def gen_backend_inputs_dict(): + """ backend -> ftq """ + return { + "io_fromBackend_redirect_valid": random.randint(0, 1), + "io_fromBackend_redirect_bits_ftqIdx_flag": random.randint(0, 1), + "io_fromBackend_redirect_bits_ftqIdx_value": random.randint(0, (1 << 6) - 1), + "io_fromBackend_redirect_bits_ftqOffset": random.randint(0, (1 << 4) - 1), + "io_fromBackend_redirect_bits_level": random.randint(0, 1), + "io_fromBackend_redirect_bits_cfiUpdate_pc": random.randint(0, (1 << 50) - 1), + "io_fromBackend_redirect_bits_cfiUpdate_target": random.randint(0, (1 << 50) - 1), + "io_fromBackend_redirect_bits_cfiUpdate_taken": random.randint(0, 1), + "io_fromBackend_redirect_bits_cfiUpdate_isMisPred": random.randint(0, 1), + "io_fromBackend_redirect_bits_cfiUpdate_backendIGPF": random.randint(0, 1), + "io_fromBackend_redirect_bits_cfiUpdate_backendIPF": random.randint(0, 1), + "io_fromBackend_redirect_bits_cfiUpdate_backendIAF": random.randint(0, 1), + "io_fromBackend_redirect_bits_debugIsCtrl": random.randint(0, 1), + "io_fromBackend_redirect_bits_debugIsMemVio": random.randint(0, 1), + "io_fromBackend_ftqIdxAhead_0_valid": random.randint(0, 1), + "io_fromBackend_ftqIdxAhead_0_bits_value": random.randint(0, (1 << 6) - 1), + "io_fromBackend_ftqIdxSelOH_bits": random.randint(0, (1 << 3) - 1), + } + +def gen_rob_commits_dict(): + """rob_commit""" + return { + "valid": random.randint(0, 1), + "commitType": random.randint(0, (1 << 3) - 1), + "ftqIdx_flag": random.randint(0, 1), + "ftqIdx_value": random.randint(0, (1 << 6) - 1), + "ftqOffset": random.randint(0, (1 << 4) - 1), + } + +def gen_ifu_inputs_dict(): + """ ifu -> ftq """ + d = {} + d["io_fromIfu_pdWb_valid"] = random.randint(0, 1) + # PCs + for i in range(16): + d[f"io_fromIfu_pdWb_bits_pc_{i}"] = random.randint(0, (1 << 50) - 1) + # pd fields for 16 lanes + for i in range(16): + d[f"io_fromIfu_pdWb_bits_pd_{i}_valid"] = random.randint(0, 1) + d[f"io_fromIfu_pdWb_bits_pd_{i}_isRVC"] = random.randint(0, 1) + d[f"io_fromIfu_pdWb_bits_pd_{i}_brType"] = random.randint(0, (1 << 2) - 1) + d[f"io_fromIfu_pdWb_bits_pd_{i}_isCall"] = random.randint(0, 1) + d[f"io_fromIfu_pdWb_bits_pd_{i}_isRet"] = random.randint(0, 1) + # ftq idx / misc + d["io_fromIfu_pdWb_bits_ftqIdx_flag"] = random.randint(0, 1) + d["io_fromIfu_pdWb_bits_ftqIdx_value"] = random.randint(0, (1 << 6) - 1) + d["io_fromIfu_pdWb_bits_misOffset_valid"] = random.randint(0, 1) + d["io_fromIfu_pdWb_bits_misOffset_bits"] = random.randint(0, (1 << 4) - 1) + d["io_fromIfu_pdWb_bits_cfiOffset_valid"] = random.randint(0, 1) + d["io_fromIfu_pdWb_bits_target"] = random.randint(0, (1 << 50) - 1) + d["io_fromIfu_pdWb_bits_jalTarget"] = random.randint(0, (1 << 50) - 1) + # instrRange bits + for i in range(16): + d[f"io_fromIfu_pdWb_bits_instrRange_{i}"] = random.randint(0, 1) + return d + +def gen_ifu_inputs_dict_full() -> dict: + """ ifu -> ftq """ + d = gen_ifu_inputs_dict() + return d | gen_rob_commits_dict_full() + +def gen_rob_commits_dict_full() -> dict: + """ full rob_commit info """ + d = {} + for i in range(8): + rc = gen_rob_commits_dict() + prefix = f"io_fromBackend_rob_commits_{i}_" + for k, v in rc.items(): + if k != "valid": + d[prefix + "bits_" + k] = v + else: + d[prefix + k] = v + return d + + + + + + + + +#===================================== +# Simulate the behavior of FTQ +#===================================== +def bpu_resp_ready_ref(dut): + if dut.canCommit.value == 1 or dut.valid_entries() < FTQSIZE: + assert dut.io_fromBpu_resp_ready.value == 1 + return 1 + else: + assert dut.io_fromBpu_resp_ready.value == 0 + return 0 + +# resp_ready & resp_valid will make resp_fire, but it need call dut.RefreshComb() to refresh the circuit +# and then resp_fire will actually be true +def bpu_resp_fire_ref(dut): + resp_ready = bpu_resp_ready_ref(dut) + resp_valid = dut.io_fromBpu_resp_valid.value + resp_fire = resp_ready & resp_valid + return resp_fire + + +# from BPU S1 result +def enq_fire_ref(dut): + resp_fire = bpu_resp_fire_ref(dut) + allow_bpu_in = allow_bpu_in_ref(dut) + enq_fire = resp_fire & allow_bpu_in + return enq_fire + +# S2 redirect +def S2_redirect_ref(dut): + S2_redirect_valid = dut.io_fromBpu_resp_bits_s2_valid_3.value + S2_redirect_hasRedirect = dut.io_fromBpu_resp_bits_s2_hasRedirect_3.value + return S2_redirect_valid and S2_redirect_hasRedirect + +# S3 redirect +def S3_redirect_ref(dut): + S3_redirect_valid = dut.io_fromBpu_resp_bits_s3_valid_3.value + S3_redirect_hasRedirect = dut.io_fromBpu_resp_bits_s3_hasRedirect_3.value + return S3_redirect_valid and S3_redirect_hasRedirect + +# from BPU result +def bpu_in_fire_ref(dut): + resp_fire = bpu_resp_fire_ref(dut) + allow_bpu_in = allow_bpu_in_ref(dut) + S2_redirect = S2_redirect_ref(dut) + S3_redirect = S3_redirect_ref(dut) + return allow_bpu_in and (S2_redirect or S3_redirect or resp_fire) + +def allow_bpu_in_ref(dut): + ifuFlush = dut.ifu_flush.value + backendRedirect = dut.backendRedirect.value + backendRedirectReg = dut.backendRedirectReg.value + return not ifuFlush and not backendRedirect and not backendRedirectReg + +def selected_stage_ref(dut): + S2_redirect = S2_redirect_ref(dut) + S3_redirect = S3_redirect_ref(dut) + if S3_redirect: + return 2 + elif S2_redirect: + return 1 + else: + return 0 + +def get_idx_of_selected_stage_ref(selectedResp: BranchPredictionBundle, selected_stage: int, ref: FTQ): + if selected_stage == 0: + ftq_idx_value = ref.bpu_ptr.value + else: + ftq_idx_value = selectedResp.ftq_idx_value.value + return ftq_idx_value + +# jiexi selectedResp +def getTaget_ref(selectedResp: BranchPredictionBundle): + hit = selectedResp.full_pred_3_hit.value + fallThroughErr = selectedResp.full_pred_3_fallThroughErr.value + if hit and not fallThroughErr: + # taken + # get first taken target + if selectedResp.full_pred_3_br_taken_mask_0.value == 1 and selectedResp.full_pred_3_slot_valids_0.value == 1: + return selectedResp.full_pred_3_targets_0.value + elif selectedResp.full_pred_3_is_br_sharing.value: + if selectedResp.full_pred_3_br_taken_mask_1.value == 1 and selectedResp.full_pred_3_slot_valids_1.value == 1: + return selectedResp.full_pred_3_targets_1.value + return selectedResp.full_pred_3_fallThroughAddr.value + elif selectedResp.full_pred_3_slot_valids_1.value == 1: + return selectedResp.full_pred_3_targets_1.value + return selectedResp.full_pred_3_fallThroughAddr.value + else: + # not taken + return selectedResp.pc_3.value + 32 + +def getCfi_ref(selectedResp: BranchPredictionBundle): + hit = selectedResp.full_pred_3_hit.value + if hit: + if selectedResp.full_pred_3_slot_valids_0.value == 1 and selectedResp.full_pred_3_br_taken_mask_0.value == 1: + valid = 1 + offset = selectedResp.full_pred_3_offsets_0.value + elif selectedResp.full_pred_3_is_br_sharing.value: + if selectedResp.full_pred_3_slot_valids_1.value == 1 and selectedResp.full_pred_3_br_taken_mask_1.value == 1: + valid = 1 + offset = selectedResp.full_pred_3_offsets_1.value + else: + valid = 0 + offset = 15 # when no takens, set cfiIndex to PredictWidth-1 + elif selectedResp.full_pred_3_slot_valids_1.value == 1: + valid = 1 + offset = selectedResp.full_pred_3_offsets_1.value + else: + valid = 0 + offset = 15 + else: + valid = 0 + offset = 15 + return {"valid":valid, "bits": offset} + +h_not_hit, h_false_hit, h_hit = range(3) + +c_empty = 0 +c_toCommit = 1 +c_committed = 2 +c_flushed = 3 + +def validInstructions_ref(dut): + + # canCommit = dut.gen_rob_comm_ptr() != dut.gen_ifu_wb_ptr() and not dut.bpu_ftb_update_stall.value == 0 + commPtr = dut.gen_comm_ptr() + row = [i.value for i in dut.commitStateQueue[commPtr.value]] + validInstructions = [ + (s == c_toCommit or s == c_committed) + for s in row + ] + return validInstructions + +def lastInstructionStatus_ref(dut, validInstructions): + commPtr = dut.gen_comm_ptr() + row = [i.value for i in dut.commitStateQueue[commPtr.value]] + indices = [i for i, v in enumerate(validInstructions) if v] + idx = indices[-1] if indices else -1 + if idx >= 0: + return row[idx] + else: + return None + +def firstInstructionFlushed_ref(row): + firstInstructionFlushed = ( + row[0] == c_flushed or (row[0] == c_empty and row[1] == c_flushed)) + return firstInstructionFlushed + +def canCommit_ref(dut, validInstructions): + has_valid = any(validInstructions) + commPtr = dut.gen_comm_ptr() + ifuWbPtr = dut.gen_ifu_wb_ptr() + robCommPtr = dut.gen_rob_comm_ptr() + lastInstructionStatus = lastInstructionStatus_ref(dut, validInstructions) + # print("lastInstructionStatus: ", lastInstructionStatus) + # print("commPtr: ", commPtr.value) + may_have_stall_from_bpu = dut.bpu_ftb_update_stall.value != 0 + canCommit = ( + commPtr != ifuWbPtr + and not may_have_stall_from_bpu + and ( + (robCommPtr > commPtr) + or ( + has_valid + and lastInstructionStatus == c_committed + ) + ) + ) + return canCommit + +def canMoveCommPtr_ref(dut, validInstructions): + has_valid = any(validInstructions) + lastInstructionStatus = lastInstructionStatus_ref(dut, validInstructions) + commPtr = dut.gen_comm_ptr() + ifuWbPtr = dut.gen_ifu_wb_ptr() + robCommPtr = dut.gen_rob_comm_ptr() + commit_state = [i.value for i in dut.commitStateQueue[commPtr.value]] + firstInstructionFlushed = firstInstructionFlushed_ref(commit_state) + may_have_stall_from_bpu = dut.bpu_ftb_update_stall.value != 0 + canMoveCommPtr = ( + commPtr != ifuWbPtr + and not may_have_stall_from_bpu + and ( + (robCommPtr > commPtr) + or ( + has_valid + and lastInstructionStatus == c_committed + ) + or firstInstructionFlushed + ) + ) + return canMoveCommPtr + +def last_valid_rob_commit_ref(fromBackend: FromBackendBundle): + rob_commits = [] + for i in range(8): + rob_commit = getattr(fromBackend, f"rob_commits_{i}") + rob_commits.append(rob_commit) + + valid_commits = [commit for commit in rob_commits if commit.valid.value == 1] + + return valid_commits[-1] if valid_commits else None + + +def mmioLastCommit_ref(dut): + commPtr = dut.gen_comm_ptr() + # mmioReadValid = dut.mmioReadValid.value + mmioReadValid = 1 + mmioReadPtr = dut.gen_mmio_ftq_ptr() + lastInstructionStatus = lastInstructionStatus_ref(dut, validInstructions_ref(dut)) + has_valid = any(validInstructions_ref(dut)) + mmioLastCommit = ( + mmioReadValid + and ( + (commPtr > mmioReadPtr) + or ( + commPtr == mmioReadPtr + and has_valid + and lastInstructionStatus == c_committed + ) + ) + ) + return mmioLastCommit + +TAR_OVF = 1 +TAR_UDF = 2 +TAR_FIT = 0 +BR_OFFSET_LEN = 12 +JMP_OFFSET_LEN = 20 + +class FtbSlot: + def __init__(self, offsetLen, subOffsetLen=None): + if subOffsetLen is not None: + assert subOffsetLen <= offsetLen + + self.offsetLen = offsetLen + self.subOffsetLen = subOffsetLen + + self.valid = False + self.offset = 0 + + self.lower = 0 + self.tarStat = TAR_FIT + self.sharing = False + + + def set_lower_stat_by_target(self, pc, target, is_share): + offLen = self.subOffsetLen if is_share else self.offsetLen + VAddrBits = 50 + shift_amt = offLen + 1 + higher_mask = (1 << (VAddrBits - shift_amt)) - 1 + + pc_higher = (pc >> shift_amt) & higher_mask + target_higher = (target >> shift_amt) & higher_mask + + if target_higher > pc_higher: + stat = TAR_OVF + elif target_higher < pc_higher: + stat = TAR_UDF + else: + stat = TAR_FIT + + lower_mask = (1 << offLen) - 1 + raw_lower = (target >> 1) & lower_mask + + self.lower = raw_lower & ((1 << self.offsetLen) - 1) + + self.tarStat = stat + self.sharing = int(is_share) + + + + # -------------------------------------- + # getTarget + # -------------------------------------- + def get_target(self, pc): + offLen = self.subOffsetLen if self.sharing else self.offsetLen + assert offLen is not None and offLen > 0 + + pc_higher = pc >> (offLen + 1) + + if self.tarStat == TAR_OVF: + higher = pc_higher + 1 + elif self.tarStat == TAR_UDF: + higher = pc_higher - 1 + else: + higher = pc_higher + + target = ( + (higher << (offLen + 1)) + | (self.lower << 1) + ) + + return target + + # -------------------------------------- + # fromAnotherSlot + # -------------------------------------- + def from_another_slot(self, that): + if self.offsetLen > that.offsetLen: + assert self.subOffsetLen == that.offsetLen + self.sharing = True + else: + assert self.offsetLen == that.offsetLen + self.sharing = False + + self.offset = that.offset + self.tarStat = that.tarStat + self.valid = that.valid + + # ZeroExt + self.lower = that.lower + +from copy import deepcopy + + + +class TailSlot(FtbSlot): + def set_by_jmp_target(self, pc, target): + self.set_lower_stat_by_target(pc, target, False) + +class FTBEntry: + def __init__(self, numBrSlot, dict=None): + self.numBrSlot = numBrSlot + self.numBr = numBrSlot + 1 + self.valid = False + numBr = self.numBr + + self.brSlots = [ + FtbSlot(offsetLen=BR_OFFSET_LEN) + for _ in range(numBrSlot) + ] + + self.strong_bias = [False] * numBr + + self.tailSlot = TailSlot(offsetLen=JMP_OFFSET_LEN, subOffsetLen=BR_OFFSET_LEN) + self.allSlots = [*self.brSlots, self.tailSlot] + + self.pftAddr = 0 + self.carry = False + + self.isJalr = False + self.isCall = False + self.isRet = False + self.last_may_be_rvi_call = False + if dict is not None: + self.valid = dict.get("valid", False) + self.pftAddr = dict.get("pftAddr", 0) + self.carry = dict.get("carry", False) + self.isJalr = dict.get("isJalr", False) + self.isCall = dict.get("isCall", False) + self.isRet = dict.get("isRet", False) + self.last_may_be_rvi_call = dict.get("last_may_be_rvi_call", False) + + for i in range(numBrSlot): + slot_dict = { + "valid": dict.get(f"brSlots_{i}_valid", False), + "offset": dict.get(f"brSlots_{i}_offset", 0), + "lower": dict.get(f"brSlots_{i}_lower", 0), + "tarStat": dict.get(f"brSlots_{i}_tarStat", TAR_FIT), + "sharing": dict.get(f"brSlots_{i}_sharing", False), + } + self.brSlots[i].valid = slot_dict["valid"] + self.brSlots[i].offset = slot_dict["offset"] + self.brSlots[i].lower = slot_dict["lower"] + self.brSlots[i].tarStat = slot_dict["tarStat"] + self.brSlots[i].sharing = slot_dict["sharing"] + + for i in range(numBr): + self.strong_bias[i] = dict.get(f"strong_bias_{i}", False) + + tail_dict = { + "valid": dict.get("tailSlot_valid", False), + "offset": dict.get("tailSlot_offset", 0), + "lower": dict.get("tailSlot_lower", 0), + "tarStat": dict.get("tailSlot_tarStat", TAR_FIT), + "sharing": dict.get("tailSlot_sharing", False), + } + self.tailSlot.valid = tail_dict["valid"] + self.tailSlot.offset = tail_dict["offset"] + self.tailSlot.lower = tail_dict["lower"] + self.tailSlot.tarStat = tail_dict["tarStat"] + self.tailSlot.sharing = tail_dict["sharing"] + + # --------------------------- + # helper functions + # --------------------------- + + def get_br_recorded_vec(self, offset): + return [ + slot.valid and slot.offset == offset + for slot in self.brSlots + ] + [ + self.tailSlot.valid and self.tailSlot.offset == offset and self.tailSlot.sharing + ] + + @property + def brValids(self): + return [s.valid for s in self.brSlots] + [ + self.tailSlot.valid and self.tailSlot.sharing + ] + + @property + def brOffset(self): + return [s.offset for s in self.brSlots] + [ + self.tailSlot.offset + ] + + @property + def jmpValid(self): + return self.tailSlot.valid and not self.tailSlot.sharing + + @property + def noEmptySlotForNewBr(self): + return all(s.valid for s in self.allSlots) + + def as_dict(self): + d = { + "valid": self.valid, + "pftAddr": self.pftAddr, + "carry": self.carry, + "isJalr": self.isJalr, + "isCall": self.isCall, + "isRet": self.isRet, + "last_may_be_rvi_call": self.last_may_be_rvi_call, + } + for i in range(self.numBrSlot): + d.update({ + f"brSlots_{i}_valid": self.brSlots[i].valid, + f"brSlots_{i}_offset": self.brSlots[i].offset, + f"brSlots_{i}_lower": self.brSlots[i].lower, + f"brSlots_{i}_tarStat": self.brSlots[i].tarStat, + f"brSlots_{i}_sharing": self.brSlots[i].sharing, + }) + for i in range(self.numBr): + d.update({ + f"strong_bias_{i}": self.strong_bias[i], + }) + d.update({ + "tailSlot_valid": self.tailSlot.valid, + "tailSlot_offset": self.tailSlot.offset, + "tailSlot_lower": self.tailSlot.lower, + "tailSlot_tarStat": self.tailSlot.tarStat, + "tailSlot_sharing": self.tailSlot.sharing, + }) + return d + + # --------------------------- + # debug helper + # --------------------------- + def dump(self): + print("FTBEntry:") + print(" valid:", self.valid) + for i, s in enumerate(self.brSlots): + print( + f" BR[{i}] valid={s.valid} " + f"offset={s.offset} lower={s.lower} " + f"tarStat={s.tarStat} sharing={s.sharing} " + f"bias={self.strong_bias[i]}" + ) + print(" Tail:", + "valid=", self.tailSlot.valid, + "offset=", self.tailSlot.offset, + "lower=", self.tailSlot.lower, + "tarStat=", self.tailSlot.tarStat, + "sharing=", self.tailSlot.sharing) + print(" pftAddr:", self.pftAddr, + "carry:", self.carry) + +def ftb_entry_gen( + start_addr, + old_entry: FTBEntry, + pd, + cfiIndex_valid, + cfiIndex_bits, + target, + hit, + mispredict_vec, + numBrSlot=1, + PredictWidth=16, +): + numBr = numBrSlot + 1 + instOffsetBits = 1 + + carryPos = (PredictWidth - 1).bit_length() + instOffsetBits # log2Ceil + + def get_lower(pc): + # move instOffsetBits bit right ,then take (carryPos - instOffsetBits) bits + return (pc >> instOffsetBits) & ((1 << (carryPos - instOffsetBits)) - 1) + + # ------------------------------------------------ + # 1 init entry + # ------------------------------------------------ + + init_entry = FTBEntry(numBrSlot=1) + init_entry.valid = True + + cfi_is_br = pd["brMask"][cfiIndex_bits] and cfiIndex_valid + + entry_has_jmp = pd["jmpInfo_valid"] + + new_jmp_is_jal = entry_has_jmp and not pd["jmpInfo_bits"][0] and cfiIndex_valid + new_jmp_is_jalr = entry_has_jmp and pd["jmpInfo_bits"][0] and cfiIndex_valid + new_jmp_is_call = entry_has_jmp and pd["jmpInfo_bits"][1] and cfiIndex_valid + new_jmp_is_ret = entry_has_jmp and pd["jmpInfo_bits"][2] and cfiIndex_valid + + cfi_is_jal = cfiIndex_bits == pd["jmpOffset"] and new_jmp_is_jal + cfi_is_jalr = cfiIndex_bits == pd["jmpOffset"] and new_jmp_is_jalr + + # ---- case br ---- + if cfi_is_br: + print("cfi is br") + slot = init_entry.brSlots[0] + slot.valid = True + slot.offset = cfiIndex_bits + # slot.set_lower_stat_by_target(start_addr, target, numBr == 1) + slot.set_lower_stat_by_target(start_addr, target, numBr == 1) + + init_entry.strong_bias[0] = True + + # print("cfiindex valid:", cfiIndex_valid) + + # ---- case jmp ---- + if entry_has_jmp: + print("entry_has_jmp") + init_entry.tailSlot.offset = pd["jmpOffset"] + init_entry.tailSlot.valid = new_jmp_is_jal or new_jmp_is_jalr + # print("DEBUG: start_addr = ", start_addr) + # print("DEBUG: target = ", target) + # print("DEBUG: jalTarget = ", pd["jalTarget"]) + init_entry.tailSlot.set_lower_stat_by_target( + start_addr, + target if cfi_is_jalr else pd["jalTarget"], + False, + ) + # print("DEBUG: tailSlot_lower = ", init_entry.tailSlot.lower) + init_entry.strong_bias[-1] = new_jmp_is_jalr + + # last_jmp_rvi + last_jmp_rvi = entry_has_jmp and pd["jmpOffset"] == (PredictWidth - 1) and not pd["rvcMask"][-1] + + # jmpPft + jmp_inst_len = 1 if pd["rvcMask"][pd["jmpOffset"]] else 2 + jmp_pft = get_lower(start_addr) + pd["jmpOffset"] + jmp_inst_len + # print("jmp_pft: ", jmp_pft) + # print("lower start addr: ", get_lower(start_addr)) + # print("entry has jmp: ", entry_has_jmp) + # print("last jmp rvi:", last_jmp_rvi) + # print("jmpOffset: ", pd["jmpOffset"]) + # print("last rvc mask:",pd["rvcMask"][-1]) + + if entry_has_jmp and not last_jmp_rvi: + init_entry.pftAddr = jmp_pft & 0b1111 + init_entry.carry = (jmp_pft >> (carryPos - instOffsetBits)) & 1 + else: + init_entry.pftAddr = get_lower(start_addr) + init_entry.carry = True + + # last_may_be_rvi_call + init_entry.last_may_be_rvi_call = ( + pd["jmpOffset"] == PredictWidth - 1 and not pd["rvcMask"][pd["jmpOffset"]] + ) + + init_entry.isJalr = new_jmp_is_jalr + init_entry.isCall = new_jmp_is_call + init_entry.isRet = new_jmp_is_ret + + # ------------------------------------------------ + # 2 hit check:check if it is new br + # ------------------------------------------------ + + oe = old_entry + br_recorded_vec = oe.get_br_recorded_vec(cfiIndex_bits) + br_recorded = any(br_recorded_vec) + + is_new_br = cfi_is_br and not br_recorded + new_br_offset = cfiIndex_bits + + # insert position + new_br_insert_onehot = [] + for i in range(numBr): + if i == 0: + cond = (not oe.brSlots[0].valid) or new_br_offset < oe.brSlots[0].offset + else: + cond = ( + oe.allSlots[i - 1].valid + and new_br_offset > oe.allSlots[i - 1].offset + and ( + not oe.allSlots[i].valid + or new_br_offset < oe.allSlots[i].offset + ) + ) + new_br_insert_onehot.append(cond) + + old_entry_modified = deepcopy(oe) + + # insert logic + for i in range(numBr): + slot = old_entry_modified.allSlots[i] + + if new_br_insert_onehot[i]: + slot.valid = True + slot.offset = new_br_offset + slot.set_lower_stat_by_target(start_addr, target, i == numBr - 1) + # slot.set_lower_stat_by_target(start_addr, target, False) + old_entry_modified.strong_bias[i] = True + + elif new_br_offset > oe.allSlots[i].offset: + old_entry_modified.strong_bias[i] = False + + else: + if i != 0: + noNeedToMoveFromFormerSlot = (i == numBr - 1) and not oe.brSlots[numBrSlot-1].valid + if not noNeedToMoveFromFormerSlot: + slot.from_another_slot(oe.allSlots[i - 1]) + old_entry_modified.strong_bias[i] = oe.strong_bias[i] + + may_have_to_replace = oe.noEmptySlotForNewBr # 所有br槽都满了 + pft_need_to_change = is_new_br and may_have_to_replace + + + if pft_need_to_change: + + if not any(new_br_insert_onehot): + new_pft_offset = new_br_offset + else: + new_pft_offset = oe.allSlots[-1].offset + + + lower_width = carryPos - instOffsetBits + mask = (1 << lower_width) - 1 + + base_lower = get_lower(start_addr) & mask + + pft_off = new_pft_offset & mask + + full_sum = base_lower + pft_off + + new_pft_addr = full_sum & mask + + carry_bit = (full_sum >> lower_width) & 1 + + old_entry_modified.pftAddr = new_pft_addr + old_entry_modified.carry = bool(carry_bit) + + old_entry_modified.last_may_be_rvi_call = False + old_entry_modified.isCall = False + old_entry_modified.isRet = False + old_entry_modified.isJalr = False + + # print(f"[DEBUG] New PFT Addr: {hex(new_pft_addr)}, Carry: {old_entry_modified.carry}") + # ------------------------------------------------ + # 3 jalr target modify + # ------------------------------------------------ + + old_target = oe.tailSlot.get_target(start_addr) + old_tail_is_jmp = not oe.tailSlot.sharing + + jalr_target_modified = ( + new_jmp_is_jalr + and old_target != target + and old_tail_is_jmp + ) + + old_entry_jmp_target_modified = deepcopy(oe) + + if jalr_target_modified: + old_entry_jmp_target_modified.tailSlot.set_lower_stat_by_target( + start_addr, target, False + ) + old_entry_jmp_target_modified.strong_bias = [False] * numBr + + # ------------------------------------------------ + # 4 strong bias modify + # ------------------------------------------------ + + old_entry_strong_bias = deepcopy(oe) + strong_bias_modified = False + + if br_recorded_vec[0]: + old_entry_strong_bias.strong_bias[0] = ( + oe.strong_bias[0] and cfiIndex_valid and oe.brSlots[0].valid + and cfiIndex_bits == oe.brSlots[0].offset + ) + elif br_recorded_vec[numBr - 1]: + old_entry_strong_bias.strong_bias[0] = False + old_entry_strong_bias.strong_bias[numBr - 1] = ( + oe.strong_bias[numBr - 1] and cfiIndex_valid and oe.brValids[numBr - 1] + and cfiIndex_bits == oe.brOffset[numBr - 1] + ) + # strong_bias_modified + for i in range(numBr): + if oe.strong_bias[i] and oe.brValids[i] and not old_entry_strong_bias.strong_bias[i]: + strong_bias_modified = True + break + # ------------------------------------------------ + # 5 choose final entry + # ------------------------------------------------ + + if not hit: + new_entry = init_entry + else: + if is_new_br: + new_entry = old_entry_modified + print("use old_entry_modified result") + elif jalr_target_modified: + new_entry = old_entry_jmp_target_modified + print("use old_entry_jmp_target_modified result") + else: + new_entry = old_entry_strong_bias + print("use old_entry_strong_bias result") + + # ------------------------------------------------ + # 6 out put signal + # ------------------------------------------------ + + taken_mask = [ + cfiIndex_valid and new_entry.brValids[i] and + new_entry.brOffset[i] == cfiIndex_bits + for i in range(numBr) + ] + + jmp_taken = ( + new_entry.jmpValid + and new_entry.tailSlot.offset == cfiIndex_bits + ) + + mispred_mask = [ + new_entry.brValids[i] + and mispredict_vec[new_entry.brOffset[i]] + for i in range(numBr) + ] + + mispred_mask.append( + new_entry.jmpValid + and mispredict_vec[pd["jmpOffset"]] + ) + + if hit and not is_new_br and not jalr_target_modified and not strong_bias_modified: + print("ftb_entry_gen result is old entry") + else: + print("ftb_entry_gen result is new entry") + print("FTB entry hit" if hit else "FTB entry miss") + return { + "new_entry": new_entry, + "new_br_insert_pos": new_br_insert_onehot, + "taken_mask": taken_mask, + "jmp_taken": jmp_taken, + "mispred_mask": mispred_mask, + "is_init_entry": not hit, + "is_old_entry": hit and not is_new_br and not jalr_target_modified and not strong_bias_modified, + "is_new_br": hit and is_new_br, + "is_jalr_target_modified": hit and jalr_target_modified, + "is_strong_bias_modified": hit and strong_bias_modified, + "is_br_full": hit and is_new_br and oe.noEmptySlotForNewBr, + "cfi_is_br": cfi_is_br + } + +def commit_mispredict( + mis, + commit_state, +): + + return [ + m and (state == c_committed) + for m, state in zip(mis, commit_state) + ]