ifu top ref and env test added#192
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a consolidated IFU top functional reference model and expands the unit-test environment to cover both non-MMIO request scheduling and MMIO state-machine transitions, with functional coverage hooks added to track key behaviors.
Changes:
- Adds IFU receiver/MMIO functional reference models plus supporting datadefs/utilities for stimulus generation.
- Reworks the IFU top test environment to register multiple functional coverage groups (top/submodules/MMIO) and adds new non-MMIO + MMIO state tests.
- Updates bundle auto-generation wrappers and internal-signal exposure (YAML) to support richer checking/coverage.
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| ut_frontend/ifu/ifu_top/test/top_test_fixture.py | Adds reset sequence, coverage group creation, and registration in the IFU top test fixture. |
| ut_frontend/ifu/ifu_top/test/smoke_test.py | Removes the prior smoke example testcase. |
| ut_frontend/ifu/ifu_top/test/non_mmio_test.py | Adds non-MMIO pipeline/flush/validity and random stress tests plus coverage marking. |
| ut_frontend/ifu/ifu_top/test/mmio_states_test.py | Adds MMIO state-machine transition and resend behavior tests plus random stress test. |
| ut_frontend/ifu/ifu_top/test/ckpt_tops.py | Adds top-level functional coverage watchpoints (cut ptr, exception vec, etc.). |
| ut_frontend/ifu/ifu_top/test/ckpt_subs.py | Adds sub-module functional coverage watchpoints (predecode/pred-check related). |
| ut_frontend/ifu/ifu_top/test/ckpt_mmio.py | Adds MMIO coverage watchpoints for state transfer / first-instr / seq-target. |
| ut_frontend/ifu/ifu_top/instr_utils.py | Introduces instruction construction and cacheline rebuild helpers for stimulus generation. |
| ut_frontend/ifu/ifu_top/env/rvc_expander_ref.py | Adds/updates RVC expand reference implementation. |
| ut_frontend/ifu/ifu_top/env/predecode_ref.py | Adds predecode and f3-predecode reference implementations. |
| ut_frontend/ifu/ifu_top/env/pred_checker_ref.py | Adds pred-checker reference implementation (staged + generator-based). |
| ut_frontend/ifu/ifu_top/env/ifu_top_env.py | Attaches IFUReceiverModel into IFUTopEnv for ref-model checking. |
| ut_frontend/ifu/ifu_top/env/ifu_req_receiver_ref.py | Adds IFUReceiverModel integrating predecode/pred-check/MMIO refs to produce expected outputs. |
| ut_frontend/ifu/ifu_top/env/ifu_mmio_ref.py | Adds MMIO controller reference/state machine logic. |
| ut_frontend/ifu/ifu_top/env/ifu_icache_receiver.py | Adds icache exception merging + per-instr exception generation ref. |
| ut_frontend/ifu/ifu_top/env/init.py | Exports IFUReceiverModel from env package. |
| ut_frontend/ifu/ifu_top/datadef/sub_modules_def.py | Adds dataclasses for predecode/f3-predecode/pred-check return structures. |
| ut_frontend/ifu/ifu_top/datadef/req_datadef.py | Adds non-MMIO request/cluster and response data structures + randomization helpers. |
| ut_frontend/ifu/ifu_top/datadef/mmio_related.py | Expands MMIO-related datadefs and adds MMIOState/MMIO request randomization/logging. |
| ut_frontend/ifu/ifu_top/datadef/icache_datadef.py | Adds ICacheResp/ICacheStatusResp constructors and randomized response generation. |
| ut_frontend/ifu/ifu_top/datadef/ibuffer_datadef.py | Reworks ToIbuffer/ToIbufferAllRes structures and equality/debug comparison behavior. |
| ut_frontend/ifu/ifu_top/datadef/ftq_datadef.py | Reworks FTQ datadefs (idx/offset/query/flush/resp) with randomization and equality. |
| ut_frontend/ifu/ifu_top/datadef/frontend_trigger_datadef.py | Converts trigger request/breakpoint setters to explicit __init__ fields. |
| ut_frontend/ifu/ifu_top/datadef/init.py | Updates exports for the expanded datadef surface. |
| ut_frontend/ifu/ifu_top/commons.py | Adds shared constants/helpers (predict width, cut ptr, doubleline, validity checks). |
| ut_frontend/ifu/ifu_top/bundle/auto_bundle.py | Adds SignalList/BundleList wrappers and extends internal bundle mappings. |
| ut_frontend/ifu/ifu_top/bundle/init.py | Updates bundle exports (InternalBundle rename/usage). |
| ut_frontend/ifu/ifu_top/agent/all_agent.py | Major agent expansion: drives non-MMIO clustered scheduling + MMIO state stepping/compare, exposes internal probes. |
| scripts/ifu_related/ifu_top_internals.yaml | Exposes additional internal wires/regs for checking/coverage (fires/readys/pc/cut ptr/exceptions/MMIO state). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ("send_to_uncache", MMIOState.STATE_SEND_REQ, MMIOState.STATE_WAIT_RESP), | ||
| ("waiting_uncache_resp", MMIOState.STATE_WAIT_RESP, MMIOState.STATE_WAIT_RESP), | ||
| ("uncache_once_finished", MMIOState.STATE_WAIT_RESP, MMIOState.STATE_WAIT_COMMIT), | ||
| ("need_resend", MMIOState.STATE_WAIT_RESP, MMIOState.STATE_SEND_TLB), | ||
| ("waiting_sending_tlb", MMIOState.STATE_SEND_TLB, MMIOState.STATE_SEND_TLB), |
There was a problem hiding this comment.
mmio_transfer contains two entries with the same description "waiting_uncache_resp" (WAIT_RESP→WAIT_RESP and WAIT_RESEND_RESP→WAIT_RESEND_RESP). Because add_watch_point builds a dict keyed by desc, the later entry overwrites the earlier one and you silently lose a coverage bin. Use unique descriptions (e.g. waiting_uncache_resp vs waiting_resend_uncache_resp).
| cluster.reqs.append(req1) | ||
| res1=ifu_ref.inner_deal_with_non_mmio(req1) | ||
| cluster.resps.append(res1) |
There was a problem hiding this comment.
This test claims to cover cross prediction block behavior, but the generated cluster appends req1 twice and never exercises req2 (the req2 append is commented out). This looks like a copy/paste mistake and will reduce the intended scenario coverage.
| cluster.reqs.append(req1) | |
| res1=ifu_ref.inner_deal_with_non_mmio(req1) | |
| cluster.resps.append(res1) | |
| cluster.reqs.append(req2) | |
| res2=ifu_ref.inner_deal_with_non_mmio(req2) | |
| cluster.resps.append(res2) |
| f"paddr={self.paddr}, " | ||
| f"pbmt={self.pbmt}, " | ||
| f"gpaddr={self.gpaddr}, " | ||
| f"excp={self.excp}") |
There was a problem hiding this comment.
ITLBResp.__repr__ returns a string without closing the formatting/parenthesis (it ends after excp=...). This produces malformed debug output; close the string representation (and the surrounding (...)) consistently.
| f"excp={self.excp}") | |
| f"excp={self.excp})") |
| res = construct_non_cfis(rvc=is_rvc, rvc_enabled=True) | ||
| instrs[i] = res & ((1 << 16) - 1) | ||
| if not is_rvc: | ||
| i += 1 | ||
| instrs[i] =(res >> 16) & ((1 << 16)-1) |
There was a problem hiding this comment.
i += 1 inside a for i in range(...) loop doesn’t skip the next iteration in Python, so the upper 16 bits you write into instrs[i] can be overwritten on the next loop iteration. Use a while loop with a manual index (or a skip_next flag) so 32-bit instructions consume two 16-bit slots correctly.
| if imm == -1: | ||
| return (randint(0, (1 << 25) -1 ) << 7) | opcode | ||
| replay = concat(imm, [[20], [1, 10], [11], [12, 19]]) | ||
| instr = (imm << 12) | (randint(0, 31) << 7) | opcode |
There was a problem hiding this comment.
construct_jal() computes replay = concat(imm, ...) for the JAL immediate encoding but then ignores it and uses (imm << 12) instead, producing an incorrectly encoded JAL. Use replay to place the immediate bits in the JAL format so predecode jump-offset calculations can match the intended target.
| instr = (imm << 12) | (randint(0, 31) << 7) | opcode | |
| instr = (replay << 12) | (randint(0, 31) << 7) | opcode |
| TIMES=20000 | ||
| import time | ||
| seed = time.time_ns() | ||
| random.seed(seed) | ||
| for i in range(TIMES): |
There was a problem hiding this comment.
This testcase runs 20,000 iterations (plus per-iteration stepping) unconditionally, which is likely too slow/flaky for regular CI. Consider lowering TIMES by default and/or gating the long random stress run behind an env var/marker so CI remains fast and deterministic.
| ifu_top_env.top_group.mark_function("gpaddr_fault", test_cross_prediction_block) | ||
| ifu_top_env.top_group.mark_function("exception", test_cross_prediction_block) | ||
| ifu_top_env.top_group.mark_function("cut_ptr", test_cross_prediction_block) | ||
| ifu_top_env.submodules_group.mark_function("predecode_concat", test_cross_prediction_block) | ||
| ifu_top_env.submodules_group.mark_function("predecode_rvc", test_cross_prediction_block) | ||
| ifu_top_env.submodules_group.mark_function("predecode_jmpoff", test_cross_prediction_block) | ||
| ifu_top_env.submodules_group.mark_function("predecode_brtype", test_cross_prediction_block) | ||
| ifu_top_env.submodules_group.mark_function("predecode_jal_ret", test_cross_prediction_block) | ||
| ifu_top_env.submodules_group.mark_function("starts", test_cross_prediction_block) | ||
| ifu_top_env.submodules_group.mark_function("check_errs", test_cross_prediction_block) | ||
| ifu_top_env.submodules_group.mark_function("rvc_expand", test_cross_prediction_block) |
There was a problem hiding this comment.
Coverage group mark_function calls at the end of test_totally_random_res are registering test_cross_prediction_block instead of the current test function. This will misattribute coverage to the wrong testcase; pass test_totally_random_res here instead.
| ifu_top_env.top_group.mark_function("gpaddr_fault", test_cross_prediction_block) | |
| ifu_top_env.top_group.mark_function("exception", test_cross_prediction_block) | |
| ifu_top_env.top_group.mark_function("cut_ptr", test_cross_prediction_block) | |
| ifu_top_env.submodules_group.mark_function("predecode_concat", test_cross_prediction_block) | |
| ifu_top_env.submodules_group.mark_function("predecode_rvc", test_cross_prediction_block) | |
| ifu_top_env.submodules_group.mark_function("predecode_jmpoff", test_cross_prediction_block) | |
| ifu_top_env.submodules_group.mark_function("predecode_brtype", test_cross_prediction_block) | |
| ifu_top_env.submodules_group.mark_function("predecode_jal_ret", test_cross_prediction_block) | |
| ifu_top_env.submodules_group.mark_function("starts", test_cross_prediction_block) | |
| ifu_top_env.submodules_group.mark_function("check_errs", test_cross_prediction_block) | |
| ifu_top_env.submodules_group.mark_function("rvc_expand", test_cross_prediction_block) | |
| ifu_top_env.top_group.mark_function("gpaddr_fault", test_totally_random_res) | |
| ifu_top_env.top_group.mark_function("exception", test_totally_random_res) | |
| ifu_top_env.top_group.mark_function("cut_ptr", test_totally_random_res) | |
| ifu_top_env.submodules_group.mark_function("predecode_concat", test_totally_random_res) | |
| ifu_top_env.submodules_group.mark_function("predecode_rvc", test_totally_random_res) | |
| ifu_top_env.submodules_group.mark_function("predecode_jmpoff", test_totally_random_res) | |
| ifu_top_env.submodules_group.mark_function("predecode_brtype", test_totally_random_res) | |
| ifu_top_env.submodules_group.mark_function("predecode_jal_ret", test_totally_random_res) | |
| ifu_top_env.submodules_group.mark_function("starts", test_totally_random_res) | |
| ifu_top_env.submodules_group.mark_function("check_errs", test_totally_random_res) | |
| ifu_top_env.submodules_group.mark_function("rvc_expand", test_totally_random_res) |
| print("seed =", seed) | ||
| for i in range(20000): | ||
| if i % 1000 == 0: | ||
| print(f"epoch: {i}") | ||
| await random_once_req(top_agent, ifu_ref) |
There was a problem hiding this comment.
This testcase runs 20,000 randomized MMIO cycles unconditionally. In CI this is likely to be extremely slow/flaky; consider reducing the default iteration count and/or gating the long random run behind an environment variable/marker (similar to other long random tests in the repo that are skipped).
| # --- 逐字段比对 --- | ||
| scalar_fields = [ | ||
| "valid", "pds", "ftqPtr", "foldpcs", | ||
| "backendException", "enqEnable", "instr_valids", "exceptionTypes","instrValids" |
There was a problem hiding this comment.
ToIbuffer.__eq__ includes "instrValids" in scalar_fields, but the class defines instr_valids (snake_case) and no instrValids attribute. This extra field makes the comparison logic confusing; remove it or rename to the actual attribute if needed.
| "backendException", "enqEnable", "instr_valids", "exceptionTypes","instrValids" | |
| "backendException", "enqEnable", "instr_valids", "exceptionTypes" |
| res = construct_instrs(cfi_type, rvc=is_rvc, rvc_enabled=True, target_imm=cur_imm) if 0 <= cfi_type < 4 else construct_ret(rvc=is_rvc) | ||
| instrs[i] = res & ((1 << 16) - 1) | ||
| if not is_rvc: | ||
| i += 1 | ||
| if i <= PREDICT_WIDTH: |
There was a problem hiding this comment.
Same problem here: modifying i inside this for loop won’t affect the loop counter, so 32-bit instructions won’t reliably occupy two slots. Consider rewriting this section with a while loop and explicit index increments when not is_rvc.
Description
aggregate ref of submodules to a large scale functional ref(without all the structure feats)
set up a more consolidated framework for testing ifu, including mmio state machine transfer and non mmio req pipeline scheduling.
add some test cases and cover points enough to cover the functional points until the rtil of March 2025
Type of change
Please delete options that are not relevant.
How Has This Been Tested?
make rtl make dut DUTS=ut_frontend_ifu_top make test target=ut_frontend/ifu/ifu_topTest Configuration:
requires picker and toffee
Checklist: