From 192c19c853c5ef48c78aafa9145ec07c2dfee936 Mon Sep 17 00:00:00 2001 From: DingL2 <1163413566@qq.com> Date: Sat, 4 Oct 2025 18:44:40 +0800 Subject: [PATCH] env --- .gitignore | 1 + .../ifu/ifu_top/env/ifu_icache_receiver.py | 28 + ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py | 171 +++++ .../ifu/ifu_top/env/ifu_req_receiver_ref.py | 650 ++++++++++++++++++ .../ifu/ifu_top/env/ifu_top_ctrl_ref.py | 358 ++++++++++ .../ifu/ifu_top/env/pred_checker_ref.py | 222 ++++++ ut_frontend/ifu/ifu_top/env/predecode_ref.py | 77 +++ .../ifu/ifu_top/env/rvc_expander_ref.py | 328 +++++++++ ut_frontend/ifu/ifu_top/env/tools.py | 115 ++++ 9 files changed, 1950 insertions(+) create mode 100644 ut_frontend/ifu/ifu_top/env/ifu_icache_receiver.py create mode 100644 ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/ifu_req_receiver_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/ifu_top_ctrl_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/pred_checker_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/predecode_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/rvc_expander_ref.py create mode 100644 ut_frontend/ifu/ifu_top/env/tools.py diff --git a/.gitignore b/.gitignore index ccfe1aa..ce32fc2 100644 --- a/.gitignore +++ b/.gitignore @@ -143,6 +143,7 @@ celerybeat.pid .venv venv/ ENV/ +!ut_frontend/ifu/ifu_top/env/ env.bak/ venv.bak/ diff --git a/ut_frontend/ifu/ifu_top/env/ifu_icache_receiver.py b/ut_frontend/ifu/ifu_top/env/ifu_icache_receiver.py new file mode 100644 index 0000000..c5a3b41 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/ifu_icache_receiver.py @@ -0,0 +1,28 @@ +from ..commons import PREDICT_WIDTH, is_next_line, is_last_in_line + +class IFUICacheReceiverRef(): + def __init__(self): + pass + + def gen_exceptions(self,none_mmio_exceptions, mmio_exceptions): + f2_exception = [] + for i in range(2): + f2_exception.append(none_mmio_exceptions[i] if none_mmio_exceptions[i] != 0 else mmio_exceptions[i]) + return f2_exception + + def gen_exceptions_each_instr(self, non_mmio_exceptions, mmio_exceptions, pcs, rvcs, start_addr, double_line): + cross_page_vec = [] + exception_each_instr = [] + f2_exception = self.gen_exceptions(non_mmio_exceptions, mmio_exceptions) + # 生成每条指令的异常情况 + for i in range(PREDICT_WIDTH): + if not is_next_line(pcs[i], start_addr): + exception_each_instr.append(f2_exception[0]) + else: + exception_each_instr.append(f2_exception[1] if double_line else 0) + + enable_next = is_last_in_line(pcs[i]) and not rvcs[i] and double_line and non_mmio_exceptions[0] == 0 + cross_page_vec.append(non_mmio_exceptions[1] if enable_next else 0) + return exception_each_instr, cross_page_vec + + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py b/ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py new file mode 100644 index 0000000..0aa510f --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py @@ -0,0 +1,171 @@ +from .ifu_top_ctrl_ref import IFUTopCtrl +from ..datadef import MMIOReq, ExceptionType, FTQIdx, ITLBReq, MMIOToIbufferFTQ, MMIOCycleInfo, MMIOState, PbmtAssist +from ..instr_utils import is_rvc, if_call, if_ret, get_cfi_type +from .rvc_expander_ref import rvc_expand_ref + +class IFUMMIOCtrler(): + def __init__(self): + super().__init__() + self.pbmt_assist = PbmtAssist() + self.cur_state = MMIOState.STATE_IDLE + self.next_mmio_state = MMIOState.STATE_IDLE + self.first_instr = True + self.low_instr = 0 + self.high_instr = 0 + self.mmio_rvc = False + self.need_resend = False + self.mmio_exception = 0 + self.resend_paddr = 0 + self.resend_gpaddr = 0 + self.exceptions = [False, False] + self.commit_res = MMIOToIbufferFTQ() + + + def calc_f2_mmio_exception(self, double_line, pmps, pbmts): + f2_mmio_exception = [0, 0] + if not double_line: + return f2_mmio_exception + + if pmps[0] != pmps[1] or pbmts[0] != pbmts[1]: + f2_mmio_exception[1] = 3 + return f2_mmio_exception + + def setup_before_mmio_states(self, cycle_req: MMIOCycleInfo, final_exceptions): + self.cycle_req = cycle_req + self.exceptions = final_exceptions + + def reset_all_state(self): + self.reset_state() + self.cur_state = MMIOState.STATE_IDLE + + def reset_state(self): + self.next_mmio_state = MMIOState.STATE_IDLE + self.need_resend = False + self.mmio_rvc = False + self.resend_paddr = 0 + self.resend_gpaddr = 0 + self.mmio_exception = ExceptionType.NONE + + def push_state(self, req: MMIOReq): + last_state = self.cur_state + self.cur_state = self.next_mmio_state + if self.next_mmio_state == MMIOState.STATE_IDLE: + is_mmio_space = (self.cycle_req.icache_pmp_mmios[0] or self.pbmt_assist.is_uncache(self.cycle_req.icache_itlb_pbmts[0])) and (self.exceptions[0] + self.exceptions[1] == 0) + if is_mmio_space: + if self.cycle_req.icache_itlb_pbmts[0] == PbmtAssist.NC: + self.next_mmio_state = MMIOState.STATE_SEND_REQ + # return self.next_mmio_state, self.cycle_req.icache_paddr + else: + self.next_mmio_state = MMIOState.STATE_WAIT_LAST_CMT + # self.from_icache_itlb_pbmt = req.itlb_pbmt + # self.from_icache_pmp_mmio = req.pmp_mmio + elif self.next_mmio_state == MMIOState.STATE_WAIT_LAST_CMT: + if self.first_instr or req.last_commited: + self.next_mmio_state = MMIOState.STATE_SEND_REQ + # return self.next_mmio_state, self.cycle_req.icache_paddr + # else: + # if req.last_commit: + # self.mmio_state = MMIOState.STATE_SEND_REQ + + elif self.next_mmio_state == MMIOState.STATE_SEND_REQ: + if req.to_uncache_ready: + self.next_mmio_state = MMIOState.STATE_WAIT_RESP + elif self.next_mmio_state == MMIOState.STATE_WAIT_RESP: + self.high_instr = (req.from_uncache.data >> 16) & ((1 << 16) -1) + self.low_instr = req.from_uncache.data & ((1 << 16) -1) + self.commit_res = self.calc_instr_and_infos() + if req.from_uncache.valid: + self.mmio_rvc = (req.from_uncache.data & 3) != 3 + self.need_resend = (not self.mmio_rvc) and ((self.cycle_req.icache_paddr >> 1) & 3) == 3 + + if self.need_resend: + self.next_mmio_state = MMIOState.STATE_SEND_TLB + itlb_req = ITLBReq() + itlb_req.vaddr = self.cycle_req.ftq_start_addr + 2 + # return self.next_mmio_state, itlb_req + else: + self.next_mmio_state = MMIOState.STATE_WAIT_COMMIT + # return self.next_mmio_state, self.get_instr_and_infos() + + elif self.next_mmio_state == MMIOState.STATE_SEND_TLB: + if req.itlb_req_ready: + self.next_mmio_state = MMIOState.STATE_TLB_RESP + + elif self.next_mmio_state == MMIOState.STATE_TLB_RESP: + if req.itlb_resp.valid: + itlb_resp_excp_code = req.itlb_resp.get_excp_code() + self.resend_paddr = req.itlb_resp.paddr + self.resend_gpaddr = req.itlb_resp.gpaddr + if itlb_resp_excp_code != ExceptionType.NONE: + self.mmio_exception = itlb_resp_excp_code + else: + self.mmio_exception = ExceptionType.AF if req.itlb_resp.pbmt != self.cycle_req.icache_itlb_pbmts[0] else ExceptionType.NONE + + if self.mmio_exception != ExceptionType.NONE: + self.next_mmio_state = MMIOState.STATE_WAIT_COMMIT + # return self.next_mmio_state, self.mmio_exception + else: + self.next_mmio_state = MMIOState.STATE_SEND_PMP + # return self.next_mmio_state, self.resend_paddr + + + elif self.next_mmio_state == MMIOState.STATE_SEND_PMP: + self.mmio_exception = ExceptionType.AF if (req.pmp_resp.instr) or self.cycle_req.icache_pmp_mmios[0] != req.pmp_resp.mmio else ExceptionType.NONE + if self.mmio_exception != ExceptionType.NONE: + self.next_mmio_state = MMIOState.STATE_WAIT_COMMIT + # return self.next_mmio_state, self.mmio_exception + else: + self.next_mmio_state = MMIOState.STATE_RESEND_REQ + # return self.next_mmio_state, self.resend_paddr + + elif self.next_mmio_state == MMIOState.STATE_RESEND_REQ: + if req.to_uncache_ready: + self.next_mmio_state = MMIOState.STATE_WAIT_RESEND_RESP + elif self.next_mmio_state == MMIOState.STATE_WAIT_RESEND_RESP: + if req.from_uncache.valid: + self.next_mmio_state = MMIOState.STATE_WAIT_COMMIT + self.high_instr = req.from_uncache.data & ((1 << 16) -1) + self.commit_res = self.calc_instr_and_infos() + # return self.next_mmio_state, self.get_instr_and_infos() + elif self.next_mmio_state == MMIOState.STATE_WAIT_COMMIT: + mmio_commit = any(commit.valid and commit.ftqIdx == self.cycle_req.ftq_idx and commit.ftqOffset == 0 for commit in req.rob_commits) + if mmio_commit or self.cycle_req.icache_itlb_pbmts[0] == PbmtAssist.NC: + self.next_mmio_state = MMIOState.STATE_COMMITED + # return self.next_mmio_state, self.cycle_req.ftq_start_addr + 2 if self.is_rvc else self.cycle_req.ftq_start_addr + 4 + elif self.next_mmio_state == MMIOState.STATE_COMMITED: + self.first_instr = self.first_instr and (not req.to_ibuffer_ready) + self.reset_state() + + if last_state == MMIOState.STATE_WAIT_RESP and self.cur_state == MMIOState.STATE_SEND_TLB: + itlb_req = ITLBReq() + itlb_req.vaddr = self.cycle_req.ftq_start_addr + 2 + return [self.cur_state, itlb_req] + elif (last_state == MMIOState.STATE_TLB_RESP or last_state == MMIOState.STATE_SEND_PMP) \ + and self.cur_state == MMIOState.STATE_WAIT_COMMIT.value: + return [self.cur_state, self.mmio_exception, self.resend_gpaddr] + elif (last_state == MMIOState.STATE_TLB_RESP.value and self.cur_state == MMIOState.STATE_SEND_PMP) \ + or (last_state == MMIOState.STATE_SEND_PMP.value and self.cur_state == MMIOState.STATE_RESEND_REQ): + return [self.cur_state, self.resend_paddr] + elif ((last_state == MMIOState.STATE_IDLE or last_state == MMIOState.STATE_WAIT_LAST_CMT) \ + and self.cur_state == MMIOState.STATE_SEND_REQ): + return [self.cur_state, self.cycle_req.icache_paddr] + elif (last_state == MMIOState.STATE_WAIT_RESP.value or last_state == MMIOState.STATE_WAIT_RESEND_RESP.value) \ + and self.cur_state == MMIOState.STATE_WAIT_COMMIT.value: + return [self.cur_state, self.commit_res] + elif (last_state == MMIOState.STATE_WAIT_COMMIT and self.cur_state == MMIOState.STATE_COMMITED): + return [MMIOState.STATE_COMMITED, self.cycle_req.ftq_start_addr + 2 if self.commit_res.is_rvc else self.cycle_req.ftq_start_addr + 4] + + return [self.cur_state] + + def calc_instr_and_infos(self): + res = MMIOToIbufferFTQ() + new_instr = (self.high_instr << 16) | self.low_instr + res.br_type = get_cfi_type(new_instr) + + res.is_rvc = is_rvc(new_instr) + res.is_call = if_call(new_instr, res.br_type) + res.is_ret = if_ret(new_instr, res.br_type) + res.expd_instr, res.ill = rvc_expand_ref(new_instr, self.cycle_req.csr_fs_is_off) + if res.ill: + res.expd_instr = new_instr + return res diff --git a/ut_frontend/ifu/ifu_top/env/ifu_req_receiver_ref.py b/ut_frontend/ifu/ifu_top/env/ifu_req_receiver_ref.py new file mode 100644 index 0000000..3850d25 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/ifu_req_receiver_ref.py @@ -0,0 +1,650 @@ + +from toffee import Model, driver_hook +from ..datadef import ExistsIdx, ICacheResp, ICacheStatusResp, FTQQuery, FTQFlushInfo, \ + FTQResp, ToIbufferAllRes,FTQRedirect, FTQFlushFromBPU, NonMMIOReq, NonMMIOResp, \ + MMIOCycleInfo, MMIOReq, ExceptionType +from .ifu_mmio_ref import IFUMMIOCtrler +from .ifu_top_ctrl_ref import IFUTopCtrl, DataManagement +from .predecode_ref import PredecodeRef, F3PredecoderRef +from .pred_checker_ref import PredCheckerRef, TYPE_JAL +from .rvc_expander_ref import rvc_expand_ref +from .ifu_icache_receiver import IFUICacheReceiverRef + +from ..commons import PREDICT_WIDTH, LAST_HALF_ERR, BLOCK_OFF_BITS, is_next_line, is_last_in_line, \ + calc_double_line, calc_blk_length, calc_cut_ptr + +def generate_prefix_one_hots(prefix_length, length): + prefix_length = min(max(prefix_length, 0), length) + bits = [1] * prefix_length + [0] * (length - prefix_length) + return bits + +def bool_list_to_int(bits): + result = 0 + for bit in reversed(bits): + result = (result << 1) | bit + return result + + +class IFUReqReceiverRef(): + def __init__(self): + # self.ftq_double_line = False + pass + + def cal_f1_pcs(self, start_addr): + # self.f1_pcs = [start_addr + i * 2 for i in range(PREDICT_WIDTH)] + + return [start_addr + i * 2 for i in range(PREDICT_WIDTH)], calc_double_line(start_addr) + + def get_f1_pcs(self): + return self.f1_pcs + + def set_paddr(self, paddr): + self.f1_paddr = paddr + + def get_f1_paddr(self): + return self.f1_paddr + + def set_gpaddr(self, gpaddr): + self.f1_gpaddr = gpaddr + + def get_f1_gpaddr(self): + return self.f1_gpaddr + + def set_f2_exception(self, exception: list[int], bked_excp): + self.f2_none_mmio_exception = exception + self.backend_exception = bked_excp + + # 无跳转时的指令距离 + def calc_ftr_ranges(self, valid, cur_start_addr, next_start_addr): + self.start_addr = cur_start_addr + self.next_start_addr = next_start_addr + if valid: + length = PREDICT_WIDTH + else: + # block_length = (next_start_addr - cur_start_addr) // 2 + length = calc_blk_length(cur_start_addr, next_start_addr) + # length = ((next_start_addr - cur_start_addr) & 0x1F ) // 2 + + # self.ftr_ranges = generate_prefix_one_hots(length, PREDICT_WIDTH) + return generate_prefix_one_hots(length, PREDICT_WIDTH) + + def calc_jump_ranges(self, valid, ftq_offset): + length = ftq_offset + 1 if valid else PREDICT_WIDTH + # self.jump_ranges = generate_prefix_one_hots(length, PREDICT_WIDTH) + return generate_prefix_one_hots(length, PREDICT_WIDTH) + + # def get_ranges(self): + # return [ftr & jmp for ftr, jmp in zip(self.ftr_ranges, self.jump_ranges)] + + def calc_cut_ptr(self, startAddr): + idx_pos = (startAddr >> 1) & 0x1F + # self.cut_ptr = [idx_pos + i for i in range(PREDICT_WIDTH + 1)] + return [idx_pos + i for i in range(PREDICT_WIDTH + 1)] + + def cut_from_cacheline(self, cacheline, cut_ptr): + instrs = [] + for idx in cut_ptr: + assert 0 <= idx < 64 + pos = idx * 16 + instrs.append((cacheline >> pos) & 0xFFFF) + return instrs + +AGENT_NAME="top_agent" + + +class IFUReceiverModel(Model): + def __init__(self): + super().__init__() + self.common_data = DataManagement() + self.step_funcs = [] + + self.ifu_req_receiver = IFUReqReceiverRef() + self.mmio_ctrl = IFUMMIOCtrler() + self.top_ctrl = IFUTopCtrl(self.common_data) + self.predecode_ref = PredecodeRef() + self.f3predecoder_ref = F3PredecoderRef() + self.pred_checker_ref = PredCheckerRef() + self.icache_receiver_ref = IFUICacheReceiverRef() + self.last_half_valid = False + + # self.f2_exception = [0, 0] + # exception_each_instr = [0 for _ in range(17)] + self.wb_tgt = 0 + + # 根据start addr和下一个块的start addr等计算信息; this function will be called at the f0 prepare stage + def save_ftq_offset_start_addr(self): + # this should be removed later + # self.next_tgt = next_start_addr + # nothing happens at f0 prepare-f0 except set ftq valid to be true + yield False + + # self.all_together_range = [ a & b for a, b in zip(self.ifu_req_receiver.ftr_ranges, self.ifu_req_receiver.jump_ranges)] + + # this will be calculated after the f0 prepare stage, where + start_addr_f1 = self.common_data.ftq_req.get(1).startAddr + + + f1_pcs, double_line = self.ifu_req_receiver.cal_f1_pcs(start_addr_f1) + self.common_data.pcs.set(f1_pcs) + self.common_data.double_line.set(double_line) + # TODO: add cut ptr var of stages + cut_ptr = calc_cut_ptr(start_addr_f1) + self.common_data.cut_ptr.set(cut_ptr) + # print(f"calc cut_ptr: {cut_ptr}") + yield False + + # works at f2 stage + + next_start_addr_f2 = self.common_data.ftq_req.get(2).nextStartAddr + offset: ExistsIdx = self.common_data.ftq_req.get(2).ftqOffset + + ftr_ranges = self.ifu_req_receiver.calc_ftr_ranges(offset.exists, start_addr_f1, next_start_addr_f2) + jump_ranges = self.ifu_req_receiver.calc_jump_ranges(offset.exists, offset.offsetIdx) + instr_ranges = [ftr & jmp for ftr, jmp in zip(ftr_ranges, jump_ranges)] + + self.common_data.instr_range.set(instr_ranges) + + yield True + + + def icache_resp_receive(self, icache_resp: ICacheResp): + self.common_data.from_icache_paddr.set(icache_resp.paddr) + self.common_data.from_icache_gpaddr.set(icache_resp.gpaddr) + self.common_data.backend_exception.set(icache_resp.backend_exception) + + # self.ifu_req_receiver.set_paddr(icache_resp.paddr) + # self.ifu_req_receiver.set_gpaddr(icache_resp.gpaddr) + # self.ifu_req_receiver.set_f2_exception(icache_resp.exceptions, icache_resp.backend_exception) + + + self.common_data.itlb_pbmt.set(icache_resp.itlb_pbmts[0]) + self.common_data.pmp_mmio.set(icache_resp.pmp_mmios[0]) + + + # mmio_exception = + cut_instrs = self.ifu_req_receiver.cut_from_cacheline(icache_resp.data, self.common_data.cut_ptr.get(2)) + # print(self.common_data.cut_ptr.get(2)) + # print(icache_resp.data) + # print(cut_instrs) + self.common_data.cut_data = cut_instrs + + none_mmio_exception = icache_resp.exceptions + + f2_mmio_exception = self.mmio_ctrl.calc_f2_mmio_exception(icache_resp.double_line, icache_resp.pmp_mmios, icache_resp.itlb_pbmts) + + f2_exception = [] + for i in range(2): + f2_exception.append(none_mmio_exception[i] if none_mmio_exception[i] != 0 else f2_mmio_exception[i]) + + self.common_data.exceptions.set(f2_exception) + + + # self.top_ctrl.f2_exception = self.f2_exception + predecode_res = self.predecode_ref.predecode(self.common_data.cut_data) + self.common_data.predecode_res.set(predecode_res) + + cross_page_vec = [] + + exception_each_instr = [] + # 生成每条指令的异常情况 + for i in range(PREDICT_WIDTH): + if not is_next_line(self.common_data.pcs.get(2)[i], self.common_data.ftq_req.get(2).startAddr): + exception_each_instr.append(f2_exception[0]) + else: + exception_each_instr.append(f2_exception[1] if self.common_data.double_line.get(2) else 0) + + enable_next = is_last_in_line(self.common_data.pcs.get(2)[i]) and not predecode_res.rvcs[i] and self.common_data.double_line.get(2) and none_mmio_exception[0] == 0 + cross_page_vec.append(none_mmio_exception[1] if enable_next else 0) + self.common_data.exception_each_instr.set(exception_each_instr) + self.common_data.crosspage_exception_vec.set(cross_page_vec) + + + yield False + + f3predecode_res = self.f3predecoder_ref.f3_predecode(self.common_data.predecode_res.get(3).new_instrs) + self.common_data.f3predecode_res.set(f3predecode_res) + + integrated_valids = self.common_data.predecode_res.get(3).half_valid_starts if self.top_ctrl.last_half_valid else self.common_data.predecode_res.get(3).valid_starts + + self.common_data.instr_valids.set(integrated_valids) + + pred_check_stg1_res = self.pred_checker_ref.pred_check_stg1(f3predecode_res, self.common_data.predecode_res.get(3), integrated_valids, self.common_data.instr_range.get(3), \ + self.common_data.ftq_req.get(3).ftqOffset , self.common_data.pcs.get(3), self.common_data.ftq_req.get(3).nextStartAddr) + + self.common_data.pred_check_stg1_res.set(pred_check_stg1_res) + # calculate cross page exceptions + + # self.cross_page_vec = [] + + self.common_data.lastvalid_idx.set(pred_check_stg1_res.fixed_length-1) + + # here, will later be changed to internal values from internal wires + ranges_and_valids = [a & b for (a,b ) in zip(pred_check_stg1_res.ranges, integrated_valids)] + self.common_data.instr_ranges_and_valids.set(ranges_and_valids) + + # prepare to ibuffer datas + + self.common_data.to_ibuffer.toIbuffer.valid = True + + self.common_data.to_ibuffer.toIbuffer.instr_valids = bool_list_to_int(integrated_valids) + + self.common_data.to_ibuffer.toIbuffer.enqEnable = bool_list_to_int(ranges_and_valids) + + self.common_data.to_ibuffer.toIbuffer.instrs = self.common_data.normal_expd_instrs.get(3) + + self.common_data.to_ibuffer.toIbuffer.pds.brTypes = f3predecode_res.brTypes + self.common_data.to_ibuffer.toIbuffer.pds.isCalls = f3predecode_res.isCalls + self.common_data.to_ibuffer.toIbuffer.pds.isRets = f3predecode_res.isRets + self.common_data.to_ibuffer.toIbuffer.pds.isRVCs = self.common_data.predecode_res.get(3).rvcs + + self.common_data.to_ibuffer.toIbuffer.ftqPtr = self.common_data.ftq_req.get(3).ftqIdx + + self.common_data.to_ibuffer.toIbuffer.foldpcs = [xorfold((pc>>1) & ((1 << 49) -1), 10, 49) for pc in self.common_data.pcs.get(3)] + + self.common_data.to_ibuffer.toIbuffer.exceptionTypes = [] + + for i in range(PREDICT_WIDTH): + cur_exception = self.common_data.exception_each_instr.get(3)[i] + self.common_data.to_ibuffer.toIbuffer.exceptionTypes.append(cur_exception if cur_exception != 0 else self.common_data.crosspage_exception_vec.get(3)[i]) + + self.common_data.to_ibuffer.toIbuffer.backendException = self.common_data.backend_exception.get(3) + + print("f3 finished") + yield False + + # wb + + pred_check_stg2_res = self.pred_checker_ref.pred_check_stg2(True) + self.common_data.pred_check_stg2_res = pred_check_stg2_res + + fired_pred_check_stg1_res = self.common_data.pred_check_stg1_res.get(4) + miss_pred_idx = -1 + for i in range(len(pred_check_stg2_res.miss_pred)): + if pred_check_stg2_res.miss_pred[i] != 0: + miss_pred_idx = i + break + + wb_lastvalid_idx = self.common_data.lastvalid_idx.get(4) + wb_instr_valids = self.common_data.instr_valids.get(4) + + wb_stg_predecode_res = self.common_data.predecode_res.get(4) + wb_stg_f3_predecode_res = self.common_data.f3predecode_res.get(4) + + miss_offset, mis_type = self.top_ctrl.redirect_flush(miss_pred_idx, wb_instr_valids, wb_lastvalid_idx, \ + wb_stg_predecode_res.rvcs, fired_pred_check_stg1_res, False) + + if miss_pred_idx <0: + miss_pred_idx = len(pred_check_stg2_res.miss_pred) - 1 + + # pc + 4 + wb_tgt = self.common_data.pcs.get(4)[wb_lastvalid_idx] + 4 if mis_type == 2 else pred_check_stg2_res.fixed_tgts[miss_pred_idx] + # print(pred_check_stg2_res.fixed_tgts[miss_pred_idx]) + # print(mis_type) + first_jmp_idx = len(pred_check_stg2_res.jmp_tgts) -1 + + # print(pred_check_stg2_res.jmp_tgts) + + for i in range(len(pred_check_stg2_res.jmp_tgts)): + if wb_instr_valids[i] and wb_stg_f3_predecode_res.brTypes[i] == TYPE_JAL: + first_jmp_idx = i + break + + jal_tgt = pred_check_stg2_res.jmp_tgts[first_jmp_idx] + + + # prepare write back datas + self.common_data.wb_ftq.valid = True + + self.common_data.wb_ftq.ftqIdx.flag = self.common_data.ftq_req.get(4).ftqIdx.flag + self.common_data.wb_ftq.ftqIdx.value = self.common_data.ftq_req.get(4).ftqIdx.value + + self.common_data.wb_ftq.pcs = self.common_data.pcs.get(4) + + self.common_data.wb_ftq.misOffset = miss_offset + + self.common_data.wb_ftq.target = wb_tgt + self.common_data.wb_ftq.instrRanges = self.common_data.instr_ranges_and_valids.get(4) + + self.common_data.wb_ftq.pds.brTypes = wb_stg_f3_predecode_res.brTypes + self.common_data.wb_ftq.pds.isCalls = wb_stg_f3_predecode_res.isCalls + self.common_data.wb_ftq.pds.isRets = wb_stg_f3_predecode_res.isRets + self.common_data.wb_ftq.pds.isRVCs = wb_stg_predecode_res.rvcs + self.common_data.wb_ftq.pds.pdValids = self.common_data.instr_valids.get(4) # if the instr is valid in the block + + self.common_data.wb_ftq.jalTarget = jal_tgt + self.common_data.wb_ftq.cfiOffset_valid = fired_pred_check_stg1_res.taken_occurs + yield True + + # MMIO在这个周期里做不了,不过可以先存下来 + + + def fake_resp(self, icache_status_resp: ICacheStatusResp): + icache_work = self.icache_resp_receive(icache_status_resp.resp) + self.step_funcs.append(icache_work) + # self.extd_instrs = [] + # for instr in self.predecode_res.new_instrs: + # ext_instr, ill = rvc_expand_ref(instr, fsIsOff=fs_is_off) + # self.extd_instrs.append(instr if ill else ext_instr) + + self.top_ctrl.check_icache_resp_all_valid(icache_status_resp.resp.icache_valid, icache_status_resp.resp.vaddrs) + + + def set_fs_is_off(self, fs_is_off): + expds = self.expd_instrs_yield(fs_is_off) + self.step_funcs.append(expds) + + def expd_instrs_yield(self, fs_is_off): + self.common_data.normal_expd_instrs.set(self.expd_instrs(fs_is_off, self.common_data.predecode_res.get(3).new_instrs)) + yield True + + def expd_instrs(self, fs_is_off, new_instrs): + extd_instrs = [] + ills = [] + for instr in new_instrs: + ext_instr, ill = rvc_expand_ref(instr, fsIsOff=fs_is_off) + extd_instrs.append(instr if ill==1 else ext_instr) + ills.append(ill) + return extd_instrs, ills + + + def set_icache_ready(self, icache_ready): + self.top_ctrl.set_icache_ready(icache_ready) + + + def query_from_ftq(self, query: FTQQuery): + # 先把ftq query保存下来 + # 这个函数已经弃用,改为保存整个ftq + self.common_data.ftq_req.set(query) + self.step_funcs.append(self.save_ftq_offset_start_addr()) + + def bpu_flush_redirect(self, flush_from_bpu: FTQFlushFromBPU): + self.top_ctrl.flush_from_bpu(flush_from_bpu) + yield True + # later will add redirect, but not now + + + def from_ftq_flush(self, ftqFlushInfo:FTQFlushFromBPU): + # done + # self.common_data.ftq_redirect.set(ftqFlushInfo.redirect if ftqFlushInfo.redirect.valid else FTQRedirect()) + + self.step_funcs.append(self.bpu_flush_redirect(ftqFlushInfo)) + + + def step(self): + print("st...") + # 进行所有功能性的计算 + next_step_funcs = [] + while self.step_funcs: + step_func = self.step_funcs.pop() + # yield 函数未结束 + res = next(step_func) + if not res: + next_step_funcs.append(step_func) + self.step_funcs = next_step_funcs + + # 根据控制结果传递值 + self.common_data.step_pass_value(self.top_ctrl.get_fires()) + + # 这个用于控制信号的检验 + + def get_ftq_ready(self): + return self.top_ctrl.get_ftq_ready() + + + def get_bpu_flush(self): + return self.top_ctrl.get_bpu_flush() + + # 以下方法协助校验接取指令的正确性 + + def get_exception_vecs(self): + return self.common_data.exceptions.get(3), self.common_data.exception_each_instr.get(3) + + + def get_f3_pcs(self): + return self.common_data.pcs.get(3) + + + def get_cut_ptrs(self): + return self.common_data.cut_ptr.get(2) + + + def get_addrs(self): + return self.common_data.from_icache_paddr.get(3), self.common_data.from_icache_gpaddr.get(3) + + + def get_cut_instrs(self): + return self.common_data.cut_data + + + def get_ranges(self): + return bool_list_to_int(self.common_data.instr_range.get(3)) + + + def get_predecode_res(self): + return self.common_data.predecode_res.get(2) + + + def get_f3predecoder_res(self): + return self.common_data.f3predecode_res.get(3), self.common_data.instr_valids.get(3) + + + def get_pred_checker_stg1_res(self): + return self.common_data.pred_check_stg1_res.get(3) + + + def get_pred_checker_stg2_res(self): + return self.common_data.pred_check_stg2_res + + + def get_extended_instrs(self): + return self.common_data.normal_expd_instrs.get(3) + + + # + # def get_wb_flush(self): + # return self.top_ctrl.miss_off + + + def get_cur_last_half_valid(self): + return self.top_ctrl.last_half_valid + + + def collect_res_backto_ftq(self): + return self.common_data.wb_ftq + + + def get_toibuffer_info(self)-> ToIbufferAllRes: + + return self.common_data.to_ibuffer + + @driver_hook(agent_name=AGENT_NAME) + def deal_with_non_mmio(self, req: NonMMIOReq): + res = NonMMIOResp() + + # f0 + res.bpu_flush_res = self.top_ctrl.flush_from_bpu(req.bpu_flush_info, req.ftq_req.ftqIdx) + res.ftq_ready = req.icache_resp.ready + if res.bpu_flush_res or (not res.ftq_ready): + return res + + # entering f1 stage + + # this will be calculated after the f0 prepare stage, where + start_addr_f1 = req.ftq_req.startAddr + + f1_pcs, double_line = self.ifu_req_receiver.cal_f1_pcs(start_addr_f1) + + cut_ptrs = calc_cut_ptr(start_addr_f1) + + + # works at f2 stage + + next_start_addr_f2 = req.ftq_req.nextStartAddr + offset: ExistsIdx = req.ftq_req.ftqOffset + + ftr_ranges = self.ifu_req_receiver.calc_ftr_ranges(offset.exists, start_addr_f1, next_start_addr_f2) + jump_ranges = self.ifu_req_receiver.calc_jump_ranges(offset.exists, offset.offsetIdx) + instr_ranges = [ftr & jmp for ftr, jmp in zip(ftr_ranges, jump_ranges)] + + # at f2 stage, icache resp comes + res.cut_ptrs = cut_ptrs + cacheline = req.icache_resp.resp.data | (req.icache_resp.resp.data << 512) + res.cut_instrs = self.ifu_req_receiver.cut_from_cacheline(cacheline, res.cut_ptrs) + res.predecode_res = self.predecode_ref.predecode(res.cut_instrs) + + res.icache_all_valid = self.top_ctrl.check_icache_resp_all_valid(req.icache_resp.resp.icache_valid, req.icache_resp.resp.vaddrs, req.icache_resp.resp.double_line, \ + double_line, req.ftq_req.startAddr, req.ftq_req.nextlineStart) + if not res.icache_all_valid: + return res + + # goto f3 stage: + + f2_mmio_exception = self.mmio_ctrl.calc_f2_mmio_exception(req.icache_resp.resp.double_line, req.icache_resp.resp.pmp_mmios, req.icache_resp.resp.itlb_pbmts) + + f2_exception = self.icache_receiver_ref.gen_exceptions(req.icache_resp.resp.exceptions, f2_mmio_exception) + + exception_each_instr, cross_page_vec = \ + self.icache_receiver_ref.gen_exceptions_each_instr(req.icache_resp.resp.exceptions, \ + f2_mmio_exception, f1_pcs, res.predecode_res.rvcs, req.ftq_req.startAddr, double_line) + + res.exception_vecs = (f2_exception, exception_each_instr) + res.pcs = f1_pcs + res.addrs = (req.icache_resp.resp.paddr, req.icache_resp.resp.gpaddr) + + res.ranges = bool_list_to_int(instr_ranges) + res.f3_predecode_res = self.f3predecoder_ref.f3_predecode(res.predecode_res.new_instrs) + + expd_instrs, ills = self.expd_instrs(req.fs_is_off, res.predecode_res.new_instrs) + integrated_valids = res.predecode_res.half_valid_starts if self.last_half_valid else res.predecode_res.valid_starts + + res.pred_checker_stg1_res, pred_checker_stg2_res = self.pred_checker_ref.pred_check_stgs(res.f3_predecode_res, res.predecode_res, integrated_valids, instr_ranges, \ + offset, f1_pcs, req.ftq_req.nextStartAddr) + + last_valid_idx = res.pred_checker_stg1_res.fixed_length-1 + last_half_valid = self.top_ctrl.check_last_req_half_valid(res.pred_checker_stg1_res.ranges, integrated_valids, \ + res.predecode_res.rvcs, res.pred_checker_stg1_res.takens) + self.last_half_valid = last_half_valid + + res.to_ibuffer = self.get_toibuffer_info() + res.to_ibuffer.toIbuffer.valid = True + + res.to_ibuffer.toIbuffer.instr_valids = bool_list_to_int(integrated_valids) + + ranges_and_valids = [a & b for (a,b ) in zip(res.pred_checker_stg1_res.ranges, integrated_valids)] + + res.to_ibuffer.toIbuffer.enqEnable = bool_list_to_int(ranges_and_valids) + + res.to_ibuffer.toIbuffer.instrs = expd_instrs + res.to_ibuffer.toIbuffer.illegalInstrs = ills + + res.to_ibuffer.toIbuffer.pds.brTypes = res.f3_predecode_res.brTypes + res.to_ibuffer.toIbuffer.pds.isCalls = res.f3_predecode_res.isCalls + res.to_ibuffer.toIbuffer.pds.isRets = res.f3_predecode_res.isRets + res.to_ibuffer.toIbuffer.pds.isRVCs = res.predecode_res.rvcs + + res.to_ibuffer.toIbuffer.ftqPtr = req.ftq_req.ftqIdx + + res.to_ibuffer.toIbuffer.foldpcs = [xorfold((pc>>1) & ((1 << 49) -1), 10, 49) for pc in f1_pcs] + + res.to_ibuffer.toIbuffer.exceptionTypes = [] + + for i in range(PREDICT_WIDTH): + cur_exception = exception_each_instr[i] + res.to_ibuffer.toIbuffer.exceptionTypes.append(cur_exception if cur_exception != 0 else cross_page_vec[i]) + + res.to_ibuffer.toIbuffer.backendException = req.icache_resp.resp.backend_exception + res.to_ibuffer.toBackendGpaddrMem.wen = ExceptionType.GPF in f2_exception + if res.to_ibuffer.toBackendGpaddrMem.wen: + res.to_ibuffer.toBackendGpaddrMem.waddr = req.ftq_req.ftqIdx.value + res.to_ibuffer.toBackendGpaddrMem.gpaddr = req.icache_resp.resp.gpaddr + # entering wb stage, collect res + + res.pred_checker_stg2_res = pred_checker_stg2_res + + res.wb_res = self.collect_res_backto_ftq() + + res.wb_res.valid = True + + res.wb_res.ftqIdx.flag = req.ftq_req.ftqIdx.flag + res.wb_res.ftqIdx.value = req.ftq_req.ftqIdx.value + + res.wb_res.pcs = f1_pcs + + miss_pred_idx = -1 + for i in range(len(res.pred_checker_stg2_res.miss_pred)): + if res.pred_checker_stg2_res.miss_pred[i] != 0: + miss_pred_idx = i + break + + miss_offset, mis_type = self.top_ctrl.redirect_flush(miss_pred_idx, integrated_valids, last_valid_idx, \ + res.predecode_res.rvcs, res.pred_checker_stg1_res, False) + if miss_pred_idx <0: + miss_pred_idx = len(res.pred_checker_stg2_res.miss_pred) - 1 + res.wb_res.misOffset = miss_offset + wb_tgt = f1_pcs[last_valid_idx] + 4 if mis_type == LAST_HALF_ERR else res.pred_checker_stg2_res.fixed_tgts[miss_pred_idx] + res.wb_res.target = wb_tgt + res.wb_res.instrRanges = ranges_and_valids + + res.wb_res.pds.brTypes = res.f3_predecode_res.brTypes + res.wb_res.pds.isCalls = res.f3_predecode_res.isCalls + res.wb_res.pds.isRets = res.f3_predecode_res.isRets + res.wb_res.pds.isRVCs = res.predecode_res.rvcs + res.wb_res.pds.pdValids = integrated_valids # if the instr is valid in the block + + first_jmp_idx = len(res.pred_checker_stg2_res.jmp_tgts) -1 + + for i in range(len(res.pred_checker_stg2_res.jmp_tgts)): + if integrated_valids[i] and res.f3_predecode_res.brTypes[i] == TYPE_JAL: + first_jmp_idx = i + break + + res.wb_res.jalTarget = res.pred_checker_stg2_res.jmp_tgts[first_jmp_idx] + res.wb_res.cfiOffset_valid = res.pred_checker_stg1_res.taken_occurs + + res.last_half_valid = last_half_valid + return res + + @driver_hook(agent_name=AGENT_NAME) + def set_up_before_mmio_states(self, mmio_cycle_info: MMIOCycleInfo): + f1_pcs, double_line = self.ifu_req_receiver.cal_f1_pcs(mmio_cycle_info.ftq_start_addr) + + mmio_exceptions = self.mmio_ctrl.calc_f2_mmio_exception(double_line, mmio_cycle_info.icache_pmp_mmios, \ + mmio_cycle_info.icache_itlb_pbmts) + + f2_exception = self.icache_receiver_ref.gen_exceptions(mmio_cycle_info.exceptions, mmio_exceptions) + + self.mmio_ctrl.setup_before_mmio_states(mmio_cycle_info, f2_exception) + self.mmio_ctrl.push_state(MMIOReq()) + + + @driver_hook(agent_name=AGENT_NAME) + def deal_with_single_mmio_req(self, mmio_req:MMIOReq): + return self.mmio_ctrl.push_state(mmio_req) + + @driver_hook + def reset_mmio_state(self): + self.mmio_ctrl.reset_all_state() + + +def xorfold(x: int, res_width: int, width: int | None = None) -> int: + """ + 把整数 x 按 res_width 分块并按位 XOR 折叠,返回 res_width 位结果。 + - x: 非负整数(对应 UInt) + - res_width: 每块位宽,>0 + - width: x 的逻辑位宽(可选)。如果不写,就用 x.bit_length();如需和硬件一致(保留前导0),请显式传入。 + """ + assert res_width > 0 + if width is None: + width = max(1, x.bit_length()) # Python 对 0 的 bit_length 是 0,这里至少给 1 + + k = (width + res_width - 1) // res_width # 上取整块数 + acc = 0 + mask_block = (1 << res_width) - 1 + + # 逐块提取并 XOR:第 i 块是 [i*res_width, (i+1)*res_width) + for i in range(k): + chunk = (x >> (i * res_width)) & mask_block + acc ^= chunk + + # 结果自然落在 res_width 位 + return acc \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/ifu_top_ctrl_ref.py b/ut_frontend/ifu/ifu_top/env/ifu_top_ctrl_ref.py new file mode 100644 index 0000000..0316ea6 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/ifu_top_ctrl_ref.py @@ -0,0 +1,358 @@ +from toffee import Model, driver_hook, monitor_hook +from ..datadef import FTQFlushFromBPU, FTQFlushFromBPUStg, FTQIdx, ExistsIdx, FTQRedirect, FTQQuery, PreDecodeDataDef, \ + F3PreDecodeData, ToIbufferAllRes, PredCheckerStage1RetData, PredCheckerStage2RetData, FTQResp, ICacheResp +from enum import Enum + +from .tools import FakeReg, StagesWire, StagesWireManager +from ..commons import PREDICT_WIDTH, PRED_ERR, LAST_HALF_ERR +AGENT_NAME="top_agent" +FULL_LAST_IDX = PREDICT_WIDTH - 1 + + +class MMIOState(Enum): + STATE_IDLE = 0 + STATE_WAIT_LAST_CMT = 1 + STATE_SEND_REQ = 2 + STATE_WAIT_RESP = 3 + STATE_SEND_TLB = 4 + STATE_TLB_RESP = 5 + STATE_SEND_PMP = 6 + STATE_RESEND_REQ = 7 + STATE_WAIT_RESEND_RESP = 8 + STATE_WAIT_COMMIT = 9 + STATE_COMMITED = 10 + +class PbmtAssist(): + NC = 1 + IO = 2 + + def is_uncache(self, num): + return num == self.NC or num == self.IO + +class DataManagement(): + def __init__(self): + self.stage_wire_manager = StagesWireManager() + self.ftq_req: StagesWire[FTQQuery] = self.stage_wire_manager.create(init_val=FTQQuery()) + self.pcs: StagesWire[list[int]] = self.stage_wire_manager.create(init_val=[0] * PREDICT_WIDTH, start=1) + self.double_line: StagesWire[int] = self.stage_wire_manager.create(start=1, end=3, init_val=False) + self.cut_ptr: StagesWire[list[int]] = self.stage_wire_manager.create(start=1, end=2, init_val=[0] * (PREDICT_WIDTH+1)) + self.instr_range: StagesWire[list[int]] = self.stage_wire_manager.create(start=2, end=3, init_val=[0] * PREDICT_WIDTH) + self.from_icache_paddr: StagesWire[int] = self.stage_wire_manager.create(start=2, end=3, init_val=0) + self.from_icache_gpaddr: StagesWire[int] = self.stage_wire_manager.create(start=2, end=3, init_val=0) + self.exceptions: StagesWire[list[int]] = self.stage_wire_manager.create(start=2, end=3, init_val=[0, 0]) + self.backend_exception: StagesWire[bool] = self.stage_wire_manager.create(start=2, end=3, init_val=False) + self.pmp_mmio: StagesWire[bool] = self.stage_wire_manager.create(start=2, end=3, init_val=False) + self.itlb_pbmt: StagesWire[bool] = self.stage_wire_manager.create(start=2, end=3, init_val=False) + self.exception_each_instr: StagesWire[list[int]] = self.stage_wire_manager.create(start=2, end=3, init_val=[0] * 16) + self.predecode_res: StagesWire[PreDecodeDataDef] = self.stage_wire_manager.create(start=2, end=4, init_val=PreDecodeDataDef()) + self.f3predecode_res: StagesWire[F3PreDecodeData] = self.stage_wire_manager.create(start=3, end=4, init_val=F3PreDecodeData()) + self.crosspage_exception_vec: StagesWire[list[int]] = self.stage_wire_manager.create(start=2, end=3, init_val=[0] * 16) + self.lastvalid_idx: StagesWire[int] = self.stage_wire_manager.create(start=3, end=4, init_val=16) + self.instr_valids: StagesWire[list[bool]] = self.stage_wire_manager.create(start=3, end=4, init_val=[False] * 16) + self.pred_check_stg1_res: StagesWire[PredCheckerStage1RetData] = self.stage_wire_manager.create(start=3, end=4, init_val=PredCheckerStage1RetData()) + self.normal_expd_instrs: StagesWire[list[int]] = self.stage_wire_manager.create(start=3, end=4, init_val=[0] * 16) + self.instr_ranges_and_valids : StagesWire[list[int]] = self.stage_wire_manager.create(start=3, end=4, init_val=[0] * 16) + + self.ftq_redirect: FakeReg[FTQRedirect] = FakeReg(FTQRedirect()) + self.cut_data: list[int] = [0] * (PREDICT_WIDTH + 1) + + # self.ftq_bpu_flush_info = FTQFlushFromBPU() + + self.to_ibuffer = ToIbufferAllRes() + self.wb_ftq = FTQResp() + + self.pred_check_stg2_res: PredCheckerStage2RetData = PredCheckerStage2RetData() + + # self.from_icache: ICacheResp = ICacheResp() + + + def step_pass_value(self, fires): + self.stage_wire_manager.fresh_all(fires) + + + +class IFUTopCtrl(): + + def __init__(self, reuseable_data: DataManagement): + self.common_data = reuseable_data + + self.f2_exception = [0, 0] + + self.f1_ready = True + self.f2_ready = True + self.icache_ready = True + self.icache_resp_valid = True + self.f0_fire = True + self.f1_fire = True + self.f2_fire = True + self.wb_enable = True + self.to_ibuffer_ready = True + self.from_uncache_valid = True + + self.f0_flush = False + self.bpu_f0_flush = False + # self.cur_ftq_idx = None + self.last_half_valid = False + self.f3_mmio_use_seq_pc = False + + # self.miss_off = ExistsIdx() + # self.mis_type = 0 + + self.mmio_state = MMIOState.STATE_IDLE + + self.ftq_redirect : FTQRedirect = FTQRedirect() + + + # @driver_hook(agent_name=AGENT_NAME) + def get_ftq_ready(self): + # self.f1_ready = self.f1_fire + return self.icache_ready and self.f1_ready + + def get_fires(self): + return {1: self.f0_fire, 2:self.f1_fire, 3: self.f2_fire, 4: self.wb_enable} + + # @driver_hook(agent_name=AGENT_NAME) + def set_icache_ready(self, icache_ready): + self.icache_ready = icache_ready + + def set_f2_mmio_exception(self, double_line, pmps, pbmts): + self.f2_mmio_exception = [0, 0] + if not double_line: + return + + if pmps[0] != pmps[1] or pbmts[0] != pbmts[1]: + self.f2_mmio_exception[1] = 3 + + self.pmp_mmio = pmps[0] + self.itlb_pbmt = pbmts[0] + + def set_ftq_valid(self, ftq_valid): + self.ftq_valid = ftq_valid + + def step(self): + # 更新f0_flush, f2_flush, f3_flush, mmio_redirect, wb_redirect + last_f2_flush = self.f2_flush + last_f2_fire = self.f2_fire + last_f3_valid = self.f3_valid + last_wb_valid = self.wb_valid + # last_wb_redirect = self.wb_redirect + # last_mmio_redirect = self.mmio_redirect + last_f3_wb_not_flush = self.f3_wb_not_flush + last_f1_valid = self.f1_valid + last_f2_valid = self.f2_valid + mmio_f3_flush = self.f3_flush + last_f3_mmio_use_seq_pc = self.f3_mmio_use_seq_pc + + last_f2_icache_all_resp_reg = self.f2_icache_all_resp_reg + + + self.ftq_valid = self.common_data.ftq_req.get(0).valid + + + last_wb_enable = self.wb_enable + + self.wb_valid = last_wb_enable + + self.wb_redirect = self.common_data.wb_ftq.misOffset.exists and last_wb_valid + + + exception_exists = False + for exc in self.f3_exception: + if exc != 0: + exception_exists = True + break + + # done + # the res related to func will not change + self.f3_req_is_mmio = last_f3_valid and (self.pmp_mmio or self.itlb_pbmt == PbmtAssist.NC ) and not exception_exists + + # temporarily done + self.f3_wb_not_flush = self.common_data.ftq_req.get(4).ftqIdx == self.common_data.ftq_req.get(3).ftqIdx and last_f3_valid and last_wb_valid + + +# val redirect_mmio_req = +# fromFtqRedirectReg.valid && redirect_ftqIdx === f3_ftq_req.ftqIdx && redirect_ftqOffset === 0.U + self.redirect_mmio_req = self.ftq_redirect.valid and self.ftq_redirect.ftqIdx == self.common_data.ftq_req.get(3).ftqIdx + + if ((last_f2_fire and not last_f2_flush) and self.f3_req_is_mmio): + self.f3_mmio_use_seq_pc = True + elif self.redirect_mmio_req: + # this condition value will be handled while receiving redirect req? + self.f3_mmio_use_seq_pc = False + + # here is the 'last' mmio state + self.mmio_redirect = self.f3_req_is_mmio and self.mmio_state == MMIOState.STATE_WAIT_COMMIT and self.last_from_uncache_valid and last_f3_mmio_use_seq_pc + + backend_redirect = self.ftq_redirect.valid + self.f3_flush = backend_redirect or (self.wb_redirect and not last_f3_wb_not_flush) + + + # these are all wires, so they all use value of this term + self.f2_flush = backend_redirect or self.mmio_redirect or self.wb_redirect + + self.f0_flush = self.f2_flush or self.bpu_f0_flush + + + # fires & readys to control + + + # f3_ready := (io.toIbuffer.ready && (f3_mmio_req_commit || !f3_req_is_mmio)) || !f3_valid + + # ibuffer ready 是外部信号;也就是当前不是mmio状态或者mmio提交已经完成时可以为真 + + self.f3_ready = self.to_ibuffer_ready and (self.f3_mmio_req_commit or not self.f3_req_is_mmio) or not last_f3_valid + + # icache_resp_all_valid + + # val f2_icache_all_resp_wire = + # fromICache.valid && + # fromICache.bits.vaddr(0) === f2_ftq_req.startAddr && + # (fromICache.bits.doubleline && fromICache.bits.vaddr(1) === f2_ftq_req.nextlineStart || !f2_doubleLine) + + + + # when(f2_flush)(f2_icache_all_resp_reg := false.B) + # .elsewhen(f2_valid && f2_icache_all_resp_wire && !f3_ready)(f2_icache_all_resp_reg := true.B) + # .elsewhen(f2_fire && f2_icache_all_resp_reg)(f2_icache_all_resp_reg := false.B) + + + self.f2_fire = last_f2_valid and self.f3_ready and self.icache_resp_all_valid + + self.f2_ready = self.f2_fire or not last_f2_valid + + self.f1_fire = last_f1_valid and self.f2_ready + + + self.f3_fire = self.to_ibuffer_valid and self.to_ibuffer_ready + + + self.f1_ready = self.f1_fire or not last_f1_valid + + self.f0_fire = self.ftq_valid and self.icache_ready and self.f1_ready + + + + # valids to control + # f0 valid is ftq valid + if self.f2_flush: + self.f1_valid = False + elif self.f0_fire and not self.f0_flush: + self.f1_valid = True + elif self.f1_fire: + self.f1_valid = False + + # when(f2_flush)(f2_valid := false.B) + # .elsewhen(f1_fire && !f1_flush)(f2_valid := true.B) + # .elsewhen(f2_fire)(f2_valid := false.B) + + if self.f2_flush: + self.f2_valid = False + elif self.f1_fire and not self.f2_flush: + self.f2_valid = True + elif self.f2_fire: + self.f2_valid = False + + + if self.f3_flush and not self.f3_req_is_mmio: + self.f3_valid = False + elif mmio_f3_flush and self.f3_req_is_mmio and not self.f3_need_not_flush: + self.f3_valid = False + elif self.f2_fire and not self.f2_flush: + self.f3_valid = True + elif self.f3_fire and not self.f3_req_is_mmio: + self.f3_valid = False + elif self.f3_req_is_mmio and self.f3_mmio_req_commit: + self.f3_valid = False + + # done + self.wb_enable = (last_f2_fire and not last_f2_flush) and (self.f3_req_is_mmio) and (not self.f3_flush) + + self.f3_exception = self.f2_exception + + self.last_from_uncache_valid = self.from_uncache_valid + + # @driver_hook(agent_name=AGENT_NAME) + def check_icache_resp_all_valid(self, icache_resp_valid, vaddrs, icache_doubleline, req_doubleline, start, next_start): + if not req_doubleline: + return True + return icache_resp_valid and vaddrs[0] == start and (icache_doubleline and vaddrs[1] == next_start) + + # self.icache_resp_valid = icache_resp_valid + # if not icache_resp_valid: + # self.f2_ready = False + # self.f1_fire = self.f2_ready + # else: + # # here will be further changed and tested + # self.f2_ready = True + # self.f1_fire = self.f2_ready + + # val f2_icache_all_resp_wire = + # fromICache.valid && + # fromICache.bits.vaddr(0) === f2_ftq_req.startAddr && + # (fromICache.bits.doubleline && fromICache.bits.vaddr(1) === f2_ftq_req.nextlineStart || !f2_doubleLine) + + # @driver_hook(agent_name=AGENT_NAME) + def flush_from_bpu(self, flush_bpu: FTQFlushFromBPU, cur_ftq_idx: FTQIdx): + for key in flush_bpu.stgs.keys(): + flush_info: FTQFlushFromBPUStg = flush_bpu.stgs[key] + if not flush_info.stg_valid: + continue + if not (flush_info.ftqIdx > cur_ftq_idx): + return True + return False + + def ftq_redirect(self, redirect: FTQRedirect): + self.ftq_redirect.valid = redirect.valid + self.ftq_redirect.ftqIdx.flag = redirect.ftqIdx.flag + self.ftq_redirect.ftqIdx.value = redirect.ftqIdx.value + self.ftq_redirect.ftqOffset = redirect.ftqOffset + self.ftq_redirect.redirect_level = redirect.redirect_level + + self.backend_redirect = self.ftq_redirect.valid + + + # @driver_hook(agent_name=AGENT_NAME) + def get_bpu_flush(self): + return self.bpu_f0_flush + + + + # may TODO: add pred checker err check and add mmio + def redirect_flush(self, miss_pred_idx, valids, last_idx, rvcs, pred_check_stg1_res: PredCheckerStage1RetData, mmio=False): + # TODO + miss_off = ExistsIdx() + mis_type = 0 + ranges = pred_check_stg1_res.ranges + takens = pred_check_stg1_res.takens + + + exists_last_half_err = check_last_valid(ranges, valids, last_idx, rvcs, takens, mmio=mmio) and last_idx != FULL_LAST_IDX + # print(exists_last_half_err) + self.last_half_valid = check_last_valid(ranges, valids, PREDICT_WIDTH - 1, rvcs, takens, mmio) + if exists_last_half_err: + miss_off.exists = True + miss_off.offsetIdx = last_idx + mis_type = LAST_HALF_ERR + return miss_off, mis_type + + # here may to be fixed + + if 0 <= miss_pred_idx < 16: + miss_off.exists = True + miss_off.offsetIdx = miss_pred_idx + mis_type = PRED_ERR + + return miss_off, mis_type + + def check_last_req_half_valid(self, ranges, valids, rvcs, takens): + return check_last_valid(ranges, valids, PREDICT_WIDTH-1, rvcs, takens) + + +def check_last_valid(ranges, valids, last_idx, rvcs, takens, mmio=False): + return ranges[last_idx] and valids[last_idx] and (not rvcs[last_idx]) and (not takens[last_idx]) \ + and (not mmio) + + + + + diff --git a/ut_frontend/ifu/ifu_top/env/pred_checker_ref.py b/ut_frontend/ifu/ifu_top/env/pred_checker_ref.py new file mode 100644 index 0000000..66f0df2 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/pred_checker_ref.py @@ -0,0 +1,222 @@ +from ..datadef import F3PreDecodeData, PredCheckerRetData, ExistsIdx, PreDecodeDataDef, PredCheckerStage2RetData, PredCheckerStage1RetData +from typing import Generator +from ..commons import PREDICT_WIDTH + +TYPE_JAL=2 +TYPE_JALR=3 +TYPE_BR = 1 +TYPE_NONE = 0 + + +NO_FAULT=0 +JAL_FAULT=1 +RET_FAULT=2 +TGT_FAULT=3 +NON_CFI_FAULT=4 +INVALID_TAKEN_FAULT=5 +JALR_FAULT=6 + + +def get_first_true(bool_list: list[bool]): + if True not in bool_list: + return len(bool_list) + return bool_list.index(True) + + +class PredCheckerRef(): + def __init__(self): + self.generator_queue: list[Generator] = [] + + def pred_check_stg1(self, f3_pd: F3PreDecodeData, pd: PreDecodeDataDef, instr_valids, instr_ranges, jmp_idx_all: ExistsIdx, pcs, tgt) -> PredCheckerStage1RetData: + generator = self.pred_check_yield(f3_pd, pd, instr_valids, instr_ranges, jmp_idx_all, pcs, tgt) + res = next(generator) + self.generator_queue.append(generator) + return res + + def pred_check_stg2(self, fire) -> PredCheckerStage2RetData: + res = PredCheckerStage2RetData() + if self.generator_queue: + new_gen = self.generator_queue.pop() + res = new_gen.send(fire) + return res + + + def pred_check_yield(self, f3_pd: F3PreDecodeData, pd: PreDecodeDataDef, instr_valids, instr_ranges, jmp_idx_all: ExistsIdx, pcs, tgt): + jal_errs = [False] * 16 + jalr_errs = [False] * 16 + ret_errs = [False] * 16 + tgt_errs = [False] * 16 + non_cfi_errs = [False] * 16 + invalid_errs = [False] * 16 + + decode_jmp_offs = pd.jmp_offsets + rvcs = pd.rvcs + whether_jmp = jmp_idx_all.exists + jmp_idx = jmp_idx_all.offsetIdx + # res = PredCheckerRetData() + + res1 = PredCheckerStage1RetData() + jal_idxs = self.check_num_errs(f3_pd.brTypes, TYPE_JAL) + jal_errs = self.jmp_type_err_check(jal_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + + jalr_init_idxs = self.check_num_errs(f3_pd.brTypes, TYPE_JALR) + ret_idxs = self.check_num_errs(f3_pd.isRets, 1) + jalr_idxs = [jalr and not ret for jalr, ret in zip(jalr_init_idxs, ret_idxs)] + + jalr_errs = self.jmp_type_err_check(jalr_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + ret_errs = self.jmp_type_err_check(ret_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + + true_jmp_stg = min(get_first_true(jal_errs), get_first_true(jalr_errs), get_first_true(ret_errs)) + if true_jmp_stg >= jmp_idx: + res1.ranges = instr_ranges[:] + retake = jmp_idx + else: + res1.ranges = [True] * true_jmp_stg + [False] * (len(instr_ranges) - true_jmp_stg) + retake = true_jmp_stg + + res1.fixed_length = 16 if 0 not in res1.ranges else res1.ranges.index(0) + # above done the stage 1 work: jal/jalr/ret errs and fix range & taken idx + + res1.takens = [res1.ranges[i] and instr_valids[i] and \ + ( f3_pd.brTypes[i] == TYPE_JAL or f3_pd.brTypes[i] == TYPE_JALR or f3_pd.isRets[i] \ + or (whether_jmp and f3_pd.brTypes[i] != TYPE_NONE and i == retake)) \ + for i in range(len(instr_ranges))] + + res1.taken_occurs = (1 in res1.takens) + + + next_fire = yield res1 + + res2 = PredCheckerStage2RetData() + + if next_fire: + if whether_jmp: + jmp_off_range = res1.ranges[jmp_idx] + instr_valid_off = instr_valids[jmp_idx] + non_cfi_errs[jmp_idx] = jmp_off_range and instr_valid_off and f3_pd.brTypes[jmp_idx] == TYPE_NONE + invalid_errs[jmp_idx] = jmp_off_range and not instr_valid_off + jmp_tgt_predecode = decode_jmp_offs[jmp_idx] + pcs[jmp_idx] + tgt_errs[jmp_idx] = jmp_off_range and instr_valid_off and (f3_pd.brTypes[jmp_idx] == TYPE_JAL or f3_pd.brTypes[jmp_idx] == TYPE_BR) and jmp_tgt_predecode != tgt + + # self.fault_type = [] + # self.fixed_tgt = [] + + for i in range(len(jal_errs)): + res2.faults[i] = (JAL_FAULT if jal_errs[i] else \ + JALR_FAULT if jalr_errs[i] else \ + RET_FAULT if ret_errs[i] else \ + TGT_FAULT if tgt_errs[i] else \ + NON_CFI_FAULT if non_cfi_errs[i] else \ + INVALID_TAKEN_FAULT if invalid_errs[i] else \ + NO_FAULT + ) + res2.miss_pred[i] = (res2.faults[i] != NO_FAULT) + cur_jmp_tgt = (pcs[i] + decode_jmp_offs[i]) & (1 << 64) -1 + res2.jmp_tgts[i] = cur_jmp_tgt + if jal_errs[i] or tgt_errs[i]: + res2.fixed_tgts[i] = cur_jmp_tgt + else: + seq_tgt = pcs[i] + (2 if rvcs[i] or not instr_valids[i] else 4) + res2.fixed_tgts[i] = seq_tgt + res2.fixed_tgts[i] &= (1 << 64) -1 + + yield res2 + + + def pred_check_stgs(self, f3_pd: F3PreDecodeData, pd: PreDecodeDataDef, instr_valids, instr_ranges, jmp_idx_all: ExistsIdx, pcs, tgt): + jal_errs = [False] * 16 + jalr_errs = [False] * 16 + ret_errs = [False] * 16 + tgt_errs = [False] * 16 + non_cfi_errs = [False] * 16 + invalid_errs = [False] * 16 + + decode_jmp_offs = pd.jmp_offsets + rvcs = pd.rvcs + whether_jmp = jmp_idx_all.exists + jmp_idx = jmp_idx_all.offsetIdx if whether_jmp else PREDICT_WIDTH + # res = PredCheckerRetData() + + res1 = PredCheckerStage1RetData() + jal_idxs = self.check_num_errs(f3_pd.brTypes, TYPE_JAL) + jal_errs = self.jmp_type_err_check(jal_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + + jalr_init_idxs = self.check_num_errs(f3_pd.brTypes, TYPE_JALR) + ret_idxs = self.check_num_errs(f3_pd.isRets, 1) + jalr_idxs = [jalr and not ret for jalr, ret in zip(jalr_init_idxs, ret_idxs)] + + jalr_errs = self.jmp_type_err_check(jalr_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + # print(f"jalr_errs: {jalr_errs}") + ret_errs = self.jmp_type_err_check(ret_idxs, whether_jmp, jmp_idx, instr_ranges, instr_valids) + + true_jmp_stg = min(get_first_true(jal_errs), get_first_true(jalr_errs), get_first_true(ret_errs)) + # print(f"valid: {whether_jmp}; jmp_idx: {jmp_idx}, true_jmp_stg: {true_jmp_stg}") + + if true_jmp_stg >= jmp_idx: + res1.ranges = instr_ranges[:] + retake = jmp_idx + else: + true_jmp_stg += 1 + res1.ranges = [True] * true_jmp_stg + [False] * (len(instr_ranges) - true_jmp_stg) + retake = true_jmp_stg + # print(f"res1_ranges: {res1.ranges}") + res1.fixed_length = 16 if 0 not in res1.ranges else res1.ranges.index(0) + # above done the stage 1 work: jal/jalr/ret errs and fix range & taken idx + + res1.takens = [res1.ranges[i] and instr_valids[i] and \ + ( f3_pd.brTypes[i] == TYPE_JAL or f3_pd.brTypes[i] == TYPE_JALR or f3_pd.isRets[i] \ + or (whether_jmp and (f3_pd.brTypes[i] != TYPE_NONE) and (i == retake))) \ + for i in range(len(res1.ranges))] + + res1.taken_occurs = (1 in res1.takens) + + + + res2 = PredCheckerStage2RetData() + + if whether_jmp: + jmp_off_range = res1.ranges[jmp_idx] + instr_valid_off = instr_valids[jmp_idx] + non_cfi_errs[jmp_idx] = jmp_off_range and instr_valid_off and f3_pd.brTypes[jmp_idx] == TYPE_NONE + invalid_errs[jmp_idx] = jmp_off_range and not instr_valid_off + jmp_tgt_predecode = decode_jmp_offs[jmp_idx] + pcs[jmp_idx] + tgt_errs[jmp_idx] = jmp_off_range and instr_valid_off and (f3_pd.brTypes[jmp_idx] == TYPE_JAL or f3_pd.brTypes[jmp_idx] == TYPE_BR) and jmp_tgt_predecode != tgt + + # self.fault_type = [] + # self.fixed_tgt = [] + + for i in range(len(jal_errs)): + res2.faults[i] = (JAL_FAULT if jal_errs[i] else \ + JALR_FAULT if jalr_errs[i] else \ + RET_FAULT if ret_errs[i] else \ + TGT_FAULT if tgt_errs[i] else \ + NON_CFI_FAULT if non_cfi_errs[i] else \ + INVALID_TAKEN_FAULT if invalid_errs[i] else \ + NO_FAULT + ) + res2.miss_pred[i] = (res2.faults[i] != NO_FAULT) + cur_jmp_tgt = (pcs[i] + decode_jmp_offs[i]) & ((1 << 64) -1) + res2.jmp_tgts[i] = cur_jmp_tgt + if jal_errs[i] or tgt_errs[i]: + res2.fixed_tgts[i] = cur_jmp_tgt + else: + seq_tgt = pcs[i] + (2 if (rvcs[i] or not instr_valids[i]) else 4) + res2.fixed_tgts[i] = seq_tgt + res2.fixed_tgts[i] &= (1 << 64) -1 + + return res1, res2 + + + # async def agent_pred_check(self, ftqValid, ftqOffBits, instrRange, instrValid, jumpOffset, pc, pds, tgt, fire): + + # returning: whether fault exists and the true position; -1 means no + def check_num_errs(self, num_list:list[int], tgt_val): + return [tgt_val == x for x in num_list] + + def jmp_type_err_check(self, idxs, whether_jmp, jmp_instr_offset, ranges, valids): + return [ranges[i] and valids[i] and idxs[i] and ((not whether_jmp) or ((i < jmp_instr_offset) and whether_jmp)) for i in range(len(idxs))] + + + # def check_empty + + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/predecode_ref.py b/ut_frontend/ifu/ifu_top/env/predecode_ref.py new file mode 100644 index 0000000..3b69a66 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/predecode_ref.py @@ -0,0 +1,77 @@ +from ..instr_utils import fetch, concat,get_cfi_type, if_call, if_ret, is_rvc +from ..datadef import PreDecodeDataDef, F3PreDecodeData + + + +class F3PredecoderRef(): + + def f3_predecode(self, instrs: list[int]) -> F3PreDecodeData: + ret = F3PreDecodeData() + for i in range(16): + instr = instrs[i] + ret.brTypes.append(get_cfi_type(instr)) + ret.isCalls.append(1 if if_call(instr, ret.brTypes[i]) else 0) + ret.isRets.append(1 if if_ret(instr, ret.brTypes[i]) else 0) + return ret + + +class PredecodeRef(): + def predecode(self, instrs: list[int]) -> PreDecodeDataDef: + ret = PreDecodeDataDef() + + for i in range(16): + ret.new_instrs.append(instrs[i] | (instrs[i+1] << 16)) + ret.rvcs.append(is_rvc(ret.new_instrs[i])) + ret.jmp_offsets.append(self.calc_imm(ret.new_instrs[i])) + ret.valid_starts.append(False) + ret.half_valid_starts.append(False) + + if i == 0: + ret.valid_starts[i] = True + ret.half_valid_starts[i] = False + elif i == 1: + ret.half_valid_starts[i] = True + ret.valid_starts[i] = ret.rvcs[0] + else: + if ret.half_valid_starts[i-1] == True: + ret.half_valid_starts[i] = ret.rvcs[i-1] + else: + ret.half_valid_starts[i] = True + + if ret.valid_starts[i-1] == True: + ret.valid_starts[i] = ret.rvcs[i-1] + else: + ret.valid_starts[i] = True + return ret + + def extend_to_64(self, value, bits): + value &= (1 << bits) - 1 # 截断到 bits 位 + sign_bit = 1 << (bits - 1) + if value & sign_bit: + # 手动补全高位 1,但仍保持非负 Python int(模拟补码) + value |= ((-1) << bits) & ((1 << 64) - 1) + return value + + + def calc_imm(self, instr): + + op = fetch(instr, 0, 1) + funct = fetch(instr, 13, 15) + + if op < 3: # C.J or beq + + if op == 1 and (funct == 6 or funct == 7): # beq + imm = concat(instr, [[12], [5, 6], [2], [10, 11], [3, 4]]) << 1 + return self.extend_to_64(imm, 9) + # if funct == 5: # C.J + return self.extend_to_64(concat(instr, [[12], [8], [9, 10], [6],[7], [2], [11], [3, 5]]) << 1, 12) + + rvi_funct = fetch(instr, 0, 6) + + if rvi_funct == 99: # 1100011 b + return self.extend_to_64(concat(instr, [[31], [7], [25, 30], [8, 11]]) << 1, 13) + + # if rvi_funct == 111: # "1101111 JAL": + return self.extend_to_64(concat(instr, [[31], [12, 19], [20], [21, 30]]) << 1, 21) + + \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/rvc_expander_ref.py b/ut_frontend/ifu/ifu_top/env/rvc_expander_ref.py new file mode 100644 index 0000000..70ee3a7 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/rvc_expander_ref.py @@ -0,0 +1,328 @@ +def rvc_expand_ref(rvc_instr,fsIsOff): + ill = 0 + expanded = 0 + rvc_instr_16bit = rvc_instr & 0xFFFF + opcode = rvc_instr_16bit & 0b11 + if opcode == 0b00: + # C.ADDI4SPN指令 + if (rvc_instr_16bit & 0xE003) == 0x0000: + nzuimm = (((rvc_instr_16bit >> 5) & 0x3C) << 4) | (((rvc_instr_16bit >> 5) & 0x1) << 3) | (((rvc_instr_16bit >> 5) & 0x2) << 1) | (((rvc_instr_16bit >> 5) & 0xC0) >> 2) + rd = 8 + ((rvc_instr_16bit >> 2) & 0x7) + expanded = 0x00000013 | (nzuimm << 20) | (rd << 7) |(2<<15) # ADDI + if nzuimm == 0: + ill = 1 + else : + ill = 0 + return expanded,ill #"C.ADDI4SPN -> ADDI" + # C.FLD + elif (rvc_instr_16bit & 0xE003) == 0x2000: + rd = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x3) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2) + expanded = 0x00000007 | (nzuimm << 20) | (rd << 7) |(0b011 <<12) |(rs1 << 15) # ADDI + if(fsIsOff==True): + ill=1 + else: + ill=0 + return expanded,ill + #C.lw lw的格式形如: | imm[11:0] | rs1 | 010 | rd | 0000011 | + elif (rvc_instr_16bit & 0xE003) == 0x4000: + rd = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x1) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2)| (((rvc_instr_16bit >> 5) & 0x2) << 1) + expanded = 0x00000003 | (nzuimm << 20) | (rd << 7) |(0b010 <<12) |(rs1 << 15) # ADDI + return expanded,ill + #C.ld + elif (rvc_instr_16bit & 0xE003) == 0x6000: + rd2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x3) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2) + expanded = 0x00000003 | (nzuimm << 20) | (rd2 << 7) |(0b011 <<12) |(rs1 << 15) # ADDI + return expanded,ill + #c.lbu/lhu/lh/sb/sh + elif (rvc_instr_16bit & 0xE003) == 0x8000: + rs2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + fun6 = (rvc_instr_16bit>>10)&0x3F + if(fun6==0b100000):#lbu + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) | ((rvc_instr_16bit >> 6) & 0x1) + expanded = 0x00000003 | (rs2 << 7) | (0b100 <<12) | (rs1 << 15) |(nzuimm << 20) # + return expanded,ill + elif(fun6==0b100001)&(((rvc_instr_16bit >> 6) & 0x1)==0):#lhu + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) + expanded = 0x00000003 | (rs2 << 7) | (0b101 <<12) | (rs1 << 15) |(nzuimm << 20) # + return expanded,ill + elif(fun6==0b100001)&(((rvc_instr_16bit >> 6) & 0x1)==1):#lh + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) + expanded = 0x00000003 | (rs2 << 7) | (0b001 <<12) | (rs1 << 15) |(nzuimm << 20) # + return expanded,ill + elif(fun6==0b100010):#sb + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) | ((rvc_instr_16bit >> 6) & 0x1) + expanded = 0x00000023 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b000 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + return expanded,ill + elif((fun6==0b100011)&(((rvc_instr_16bit >> 6) & 0x1)==0)):#sh + nzuimm = ((rvc_instr_16bit >> 4) & 0x2) + expanded = 0x00000023 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b001 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + return expanded,ill + else: + ill=2 + return expanded,ill + #C.fsd + elif (rvc_instr_16bit & 0xE003) == 0xa000: + rs2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x3) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2) + expanded = 0x00000027 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b011 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + if(fsIsOff==True): + ill=1 + else: + ill=0 + return expanded,ill + + #C.sw RVI的SW格式形如:| imm[11:5]| rs2 | rs1 | 010 | imm[4:0] | 0100011 | + elif (rvc_instr_16bit & 0xE003) == 0xC000: + rs2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x1) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2)| (((rvc_instr_16bit >> 5) & 0x2) << 1) + expanded = 0x00000023 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b010 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + return expanded,ill + #C.sd + elif (rvc_instr_16bit & 0xE003) == 0xE000: + rs2 = 8 + ((rvc_instr_16bit >> 2) & 0x7) + rs1 = 8 + ((rvc_instr_16bit >> 7) & 0x7) + nzuimm = (((rvc_instr_16bit >> 5) & 0x3) << 6) | (((rvc_instr_16bit >> 5) & 0xE0) >>2) + expanded = 0x00000023 | ((nzuimm & 0x1F) << 7) | (rs2 << 20) |(0b011 <<12) |(rs1 << 15) |((nzuimm & 0xFE0) << 20)# + return expanded,ill + else: + return expanded,ill + + elif opcode == 0b01: + #C.addi + if (rvc_instr_16bit & 0xE003) == 0x0001: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm = 0xFE0 | ((rvc_instr_16bit >> 2) & 0x1F) + else: + nzuimm = 0x000 | ((rvc_instr_16bit >> 2) & 0x1F) + expanded = 0x00000013 | (rd << 7) | (0b000 << 12) |(rs1 << 15) | (nzuimm << 20) # + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x2001: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm = 0xFE0 | ((rvc_instr_16bit >> 2) & 0x1F) + else: + nzuimm = 0x000 | ((rvc_instr_16bit >> 2) & 0x1F) + expanded = 0x0000001B | (rd << 7) | (0b000 << 12) |(rs1 << 15) | (nzuimm << 20) # + if (rd == 0): + ill = 1 + + return expanded,ill + + elif (rvc_instr_16bit & 0xE003) == 0x4001: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm = 0xFE0 | ((rvc_instr_16bit >> 2) & 0x1F) + else: + nzuimm = 0x000 | ((rvc_instr_16bit >> 2) & 0x1F) + expanded = 0x00000013 | (rd << 7) | (0b000 << 12) |(0b00000 << 15) | (nzuimm << 20) # + + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x6001: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + if(rd == 2) or (rd == 0): + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm=0xE00|(((rvc_instr_16bit>>2)&0x1)<<5)|(((rvc_instr_16bit>>3)&0x3)<<7)|(((rvc_instr_16bit>>5)&0x1)<<6)|(((rvc_instr_16bit>>6)&0x1)<<4) + else: + nzuimm=0x000|(((rvc_instr_16bit>>2)&0x1)<<5)|(((rvc_instr_16bit>>3)&0x3)<<7)|(((rvc_instr_16bit>>5)&0x1)<<6)|(((rvc_instr_16bit>>6)&0x1)<<4) + if(nzuimm == 0): + ill = 1 + expanded = 0x00000013 | (rd << 7) | (0b000 << 12) |(rd << 15) | (nzuimm << 20) # + else:#lui lui指令的格式形如: | imm[31:12] | rd | 0110111 | + if ((rvc_instr_16bit >> 12) & 0x1): + nzuimm = 0xFFFE0000 | (((rvc_instr_16bit >> 2) & 0x1F) << 12) + else: + nzuimm = 0x00000000 | (((rvc_instr_16bit >> 2) & 0x1F) << 12) + expanded = 0x00000037 | (rd << 7) | (nzuimm) + if(nzuimm == 0): + # ill = 1 + ill = 2 # temperarily exclude this condition + expanded = 0x0000007F + + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x8001: + fun6 = (rvc_instr_16bit>>10) & 0x3F + fun2 = (rvc_instr_16bit>>5 ) & 0x3 + fun3 = ((rvc_instr_16bit >> 2) & 0x7) + rd = 8+((rvc_instr_16bit >> 7) & 0x7) + rs2 = 8+((rvc_instr_16bit >> 2) & 0x7) + nzuimm = (rvc_instr_16bit>>2)&0x1F + if(fun6 == 0x20):#SRLI + expanded = 0x00000013 | (rd << 7) |0b101<<12| (rd << 15)| (nzuimm<<20) + return expanded,ill + elif(fun6 == 0x24):#SRLI + expanded = 0x02000013 | (rd << 7) |0b101<<12| (rd << 15)| (nzuimm<<20) + return expanded,ill + elif(fun6 == 0x21):#SRAI + expanded = 0x00000013 | (rd << 7) |0b101<<12| (rd << 15)| (nzuimm<<20)|(0b0100000<<25) + return expanded,ill + elif(fun6 == 0x25):#SRAI + expanded = 0x00000013 | (rd << 7) |0b101<<12| (rd << 15)| (nzuimm<<20)|(0b0100001<<25) + return expanded,ill + elif(fun6 == 0x22):#andi + expanded = 0x00000013 | (rd << 7) |0b111<<12| (rd << 15)| (nzuimm<<20) + return expanded,ill + elif(fun6 == 0x26):#andi + expanded = 0x00000013 | (rd << 7) |0b111<<12| (rd << 15)| (nzuimm<<20)|(0b1111111<<25) + return expanded,ill + elif(fun6 == 0x23):#sub/xor/or/and + if(fun2 == 0b00):#sub + expanded = 0x00000033 | (rd << 7) |0b000<<12| (rd << 15)| (rs2<<20)|(0b0100000<<25) + return expanded,ill + elif(fun2 == 0b01):#xor + expanded = 0x00000033 | (rd << 7) |0b100<<12| (rd << 15)| (rs2<<20)|(0b0000000<<25) + return expanded,ill + elif(fun2 == 0b10):#or + expanded = 0x00000033 | (rd << 7) |0b110<<12| (rd << 15)| (rs2<<20)|(0b0000000<<25) + return expanded,ill + elif(fun2 == 0b11): + expanded = 0x00000033 | (rd << 7) |0b111<<12| (rd << 15)| (rs2<<20)|(0b0000000<<25) + return expanded,ill + elif(fun6 == 0x27):# C.subw/addw/mul/not/zext.b/sext.b/zext.h/sext.h/zext.w/ + if(fun2 == 0b00):#subw + expanded = 0x0000003B | (rd << 7) |0b000<<12| (rd << 15)| (rs2<<20)|(0b0100000<<25) + return expanded,ill + elif(fun2 == 0b01):#addw + expanded = 0x0000003B | (rd << 7) |0b000<<12| (rd << 15)| (rs2<<20)|(0b0000000<<25) + return expanded,ill + elif(fun2 == 0b10):#mul + expanded = 0x00000033 | (rd << 7) |0b000<<12| (rd << 15)| (rs2<<20)|(0b0000001<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b000):#zext.b + expanded = 0x0ff00013 | (rd << 7) |0b111<<12| (rd << 15) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b001):#sext.b + expanded = 0x00000013 | (rd << 7) |0b001<<12| (rd << 15)|(0b00100<<20)|(0b0110000<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b010):#zext.h + expanded = 0x0000003B | (rd << 7) |0b100<<12| (rd << 15)|(0b00000<<20)|(0b0000100<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b011):#sext.h + expanded = 0x00000013 | (rd << 7) |0b001<<12| (rd << 15)|(0b00101<<20)|(0b0110000<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b100):#zext.w + expanded = 0x0000003B | (rd << 7) |0b000<<12| (rd << 15)|(0b00000<<20)|(0b0000100<<25) + return expanded,ill + elif(fun2 == 0b11)&(fun3==0b101):#not + expanded = 0xfff00013 | (rd << 7) |0b100<<12| (rd << 15)|(0b00000<<20) + return expanded,ill + else: + ill =2 + return expanded,ill + else: + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xa001: + nzuimm = 0xFFFE0000|(((rvc_instr_16bit>>2)&0x1)<<5)|(((rvc_instr_16bit>>2)&0xe))|(((rvc_instr_16bit>>2)&0x10)<<3)|(((rvc_instr_16bit>>2)&0x20)<<1)|(((rvc_instr_16bit>>2)&0x40)<<4)|(((rvc_instr_16bit>>2)&0x180)<<1)|(((rvc_instr_16bit>>2)&0x200)>>5)|(((rvc_instr_16bit>>2)&0x400)<<1) + if(((rvc_instr_16bit>>2)&0x400)>>10): + expanded = 0x800FF06F|(0b00000 << 7)|((nzuimm &0x7fe) <<20)|((nzuimm &0x800) <<9)# + else: + expanded = 0x0000006F|(0b00000 << 7)|((nzuimm &0x7fe) <<20)|((nzuimm &0x800) <<9)# + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xc001: + rs1 = ((rvc_instr_16bit >> 7) & 0x7)+8 + nzuimm = 0xFF & ((((rvc_instr_16bit>>2)&0x1)<<4)|((((rvc_instr_16bit>>2)&0x6)>>1))|(((rvc_instr_16bit>>2)&0x18)<<2)|(((rvc_instr_16bit>>2)&0x300)>>6)|(((rvc_instr_16bit>>2)&0x400)>>3)) + if((((rvc_instr_16bit>>2)&0x400)>>10)): + expanded = 0xF00000E3|((nzuimm&0x0F)<<8)|rs1<<15|((nzuimm&0xF0)<<21) + else: + expanded = 0x00000063|((nzuimm&0x0F)<<8)|rs1<<15|((nzuimm&0xF0)<<21) + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xe001: + rs1 = ((rvc_instr_16bit >> 7) & 0x7)+8 + nzuimm = 0xFF & ((((rvc_instr_16bit>>2)&0x1)<<4)|((((rvc_instr_16bit>>2)&0x6)>>1))|(((rvc_instr_16bit>>2)&0x18)<<2)|(((rvc_instr_16bit>>2)&0x300)>>6)|(((rvc_instr_16bit>>2)&0x400)>>3)) + if((((rvc_instr_16bit>>2)&0x400)>>10)): + expanded = 0xF00000E3|((nzuimm&0x0F)<<8)|0x001<<12|rs1<<15|((nzuimm&0xF0)<<21) + else: + expanded = 0x00000063|((nzuimm&0x0F)<<8)|0x001<<12|rs1<<15|((nzuimm&0xF0)<<21) + return expanded,ill + elif opcode == 0b10: + if (rvc_instr_16bit & 0xE003) == 0x0002: + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + + nzuimm = 0x000 | ((rvc_instr_16bit >> 2) & 0x1F) | ((rvc_instr_16bit >> 7) & 0x20) + expanded = 0x00000013 | (rd << 7) | (0b001 << 12) |(rs1 << 15) | (nzuimm << 20) # + + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x2002:#fldsp + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + + nzuimm = (((rvc_instr_16bit >> 2) & 0x7) <<6) | ((rvc_instr_16bit >> 7) & 0x20) | ((rvc_instr_16bit >> 2) & 0x18) + expanded = 0x00000007 | (rd << 7) | (0b011 << 12) |(0b00010 << 15) | (nzuimm << 20) # + if(fsIsOff==True): + ill=1 + else: + ill=0 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x4002:#lwsp + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + + nzuimm = (((rvc_instr_16bit >> 2) & 0x3) <<6) | ((rvc_instr_16bit >> 7) & 0x20) | ((rvc_instr_16bit >> 2) & 0x1c) + expanded = 0x00000003 | (rd << 7) | (0b010 << 12) |(0b00010 << 15) | (nzuimm << 20) # + if (rd == 0): + ill = 1 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x6002:#ldsp + rd = ((rvc_instr_16bit >> 7) & 0x1F) + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + + nzuimm = (((rvc_instr_16bit >> 2) & 0x7) <<6) | ((rvc_instr_16bit >> 7) & 0x20) | ((rvc_instr_16bit >> 2) & 0x18) + expanded = 0x00000003 | (rd << 7) | (0b011 << 12) |(0b00010 << 15) | (nzuimm << 20) # + if (rd == 0): + ill = 1 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0x8002:#JR/MV/EBREAK/JALR/ADD + rs1 = ((rvc_instr_16bit >> 7) & 0x1F) + rs2 = ((rvc_instr_16bit >> 2) & 0x1F) + funct4 = (rvc_instr_16bit>>12)&0x1 + nzuimm = (((rvc_instr_16bit >> 2) & 0x7) <<6) | ((rvc_instr_16bit >> 7) & 0x20) | ((rvc_instr_16bit >> 2) & 0x18) + if((funct4==0)&(rs2==0)&(rs1!=0)): #JR + expanded = 0x00000067 | (0b00000 << 7) | (0b000 << 12) |(rs1 << 15) # + elif(funct4==0)&(rs2!=0):#&(rs1!=0)):#MV 0000000 rs2 rs1 000 rd 0110011 ADD + expanded = 0x00000013 | (rs1 << 7) | (0b000 << 12) |(rs2 << 15) # + elif((funct4==1)&(rs2==0)&(rs1==0)): #ebreak + expanded = 0b00000000000100000000000001110011 + elif((funct4==1)&(rs2==0)&(rs1!=0)): #jalr + expanded = 0x00000067 | (0b00001 << 7) | (0b000 << 12) |(rs1 << 15) # + elif(funct4==1)&(rs2!=0):#&(rs1!=0)): #add + expanded = 0x00000033 | (rs1 << 7) | (0b000 << 12) |(rs1 << 15) |(rs2 << 20) # + else: + ill = 1 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xa002:#fsdsp + rs2 = ((rvc_instr_16bit >> 2) & 0x1F) + nzuimm = ((rvc_instr_16bit >> 1) & 0x1C0) | ((rvc_instr_16bit >> 7) & 0x38) + expanded = 0x00000027 |((nzuimm & 0x1F)<<7)| (0b011 << 12 ) | (0b00010 << 15)| (rs2 << 20)| ((nzuimm&0xFE0)<< 20) # + if(fsIsOff==True): + ill=1 + else: + ill=0 + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xc002:#swsp + rs2 = ((rvc_instr_16bit >> 2) & 0x1F) + nzuimm = ((rvc_instr_16bit >> 1) & 0xC0) | ((rvc_instr_16bit >> 7) & 0x3C) + expanded = 0x00000023 |((nzuimm & 0x1F)<<7)| (0b010 << 12 ) | (0b00010 << 15)| (rs2 << 20)| ((nzuimm&0xFE0)<< 20) # + + return expanded,ill + elif (rvc_instr_16bit & 0xE003) == 0xE002:#sdsp + rs2 = ((rvc_instr_16bit >> 2) & 0x1F) + nzuimm = ((rvc_instr_16bit >> 1) & 0x1C0) | ((rvc_instr_16bit >> 7) & 0x38) + expanded = 0x00000023 |((nzuimm & 0x1F)<<7)| (0b011 << 12 ) | (0b00010 << 15)| (rs2 << 20)| ((nzuimm&0xFE0)<< 20) # + + return expanded,ill + else: + return expanded,ill + else: + return rvc_instr, False \ No newline at end of file diff --git a/ut_frontend/ifu/ifu_top/env/tools.py b/ut_frontend/ifu/ifu_top/env/tools.py new file mode 100644 index 0000000..4338626 --- /dev/null +++ b/ut_frontend/ifu/ifu_top/env/tools.py @@ -0,0 +1,115 @@ +from typing import TypeVar, Generic, Optional +T=TypeVar("T") + +class FakeReg(Generic[T]): + def __init__(self, init_val: T=0, default_ret_cur=True): + self.cur:T = init_val + self.next:T = init_val + self.default_ret_cur = default_ret_cur + + def fresh(self): + self.cur = self.next + + def get_cur(self) -> T: + return self.cur + + def get_next(self) -> T: + return self.next + + def set(self, val): + self.next = val + + def get(self) -> T: + if self.default_ret_cur: + return self.get_cur() + return self.get_next() + + +# class StagesReg(): +# def __init__(self, start=0, end=4, init_val=0): +# self.regs : dict[int, FakeReg] = {} +# self.start = start +# self.end = end +# for i in range(start, end+1): +# self.regs[i] = FakeReg(init_val=init_val) + +# def set(self, val): +# self.regs[self.start].set(val) + +# def fresh(self, fires: dict[int, bool]): +# for i in range(self.end+1, self.start-1, -1): +# if (i > self.start) and fires[i]: +# self.regs[i].set(self.regs[i-1].get_cur()) +# # self.regs[i].fresh() + +# def get(self, idx): +# if idx not in self.regs.keys(): +# print("key err! please check!") +# return None + +# return self.regs[idx].get() + + +class StagesWire(Generic[T]): + + + def __init__(self, start: int = 0, end: int = 4, init_val: T = None): + self.wires: dict[int, T] = {} + self.start: int = start + self.end: int = end + for i in range(start, end+1): + self.wires[i] = init_val + + + def set(self, val: T) -> None: + self.wires[self.start] = val + + def fresh(self, fires: dict[int, bool]) -> None: + # 从高到低搬运(i <- i-1),只在该级 fire 时搬 + for i in range(self.end, self.start, -1): + if fires.get(i, False): + self.wires[i] = self.wires[i - 1] + + def get(self, idx: int) -> T: + if idx not in self.wires: + raise KeyError(f"idx {idx} not in [{self.start}, {self.end}]") + return self.wires[idx] + + +class StagesWireManager(Generic[T]): + def __init__(self): + self.wires: list[StagesWire] = [] + + def create(self, start: int = 0, end: int = 4, init_val: Optional[T] = None) -> StagesWire[T]: + new_wire = StagesWire(start=start, end=end, init_val=init_val) + self.wires.append(new_wire) + return new_wire + + def fresh_all(self, fires: dict[int, bool]): + for wire in self.wires: + wire.fresh(fires) + +class StagesReg(Generic[T]): + def __init__(self, start: int = 0, end: int = 4, init_val: T = None): # type: ignore[assignment] + self.regs: dict[int, FakeReg[T]] = {} + self.start: int = start + self.end: int = end + for i in range(start, end + 1): + self.regs[i] = FakeReg[T](init_val) + + def set(self, val: T) -> None: + self.regs[self.start].set(val) + + def fresh(self, fires: dict[int, bool]) -> None: + # 从高到低搬运(i <- i-1),只在该级 fire 时搬 + for i in range(self.end, self.start, -1): + if fires.get(i, False): + self.regs[i].set(self.regs[i - 1].get_cur()) + # 统一提交 + # for r in self.regs.values(): + # r.fresh() + + def get(self, idx: int) -> T: + if idx not in self.regs: + raise KeyError(f"idx {idx} not in [{self.start}, {self.end}]") + return self.regs[idx].get() \ No newline at end of file