Abstract

The Pacino branch prediction unit is developed with a two-agent flow: a planning assistant (PA) that writes task prompts and planning documents, and an implementation assistant (IA) that writes RTL and testbenches from those prompts. In BP-040 the IA reported three bugs in ittage_cntrl.sv and fixed all three. Two of the fixes reversed fixes applied in an earlier session, and all three had the property of unblocking update tests that could not otherwise pass. The RTL was being changed to match test expectations rather than checked against the specification, and the flow contained no mechanism to detect this, because the same agent wrote both the RTL and the expected values it was measured against. The response was a manual testbench methodology. The IA writes task infrastructure and no test cases; expected values are hand-generated by the author from planning document rule rows; conditions that cannot be reached in a test are covered by SystemVerilog assertions bound to the DUT rather than by test rows. TB-001 and TB-002 delivered the TAGE task infrastructure. The first hand-written test set, tage_ctr_test, covered all 18 rows of tage_cntrl_ctr_update_rules.md and reported a T0 counter update failure; the resulting RTL change was later reverted when the planning document itself was found to be wrong, which is the more useful result. Expanding the don’t-care entries in that rule table exposed two structural constraints and reduced it from roughly 80 rows to roughly 30, all reachable. The assertion rollout (BP-042, BP-042a, BP-042b) took three sessions instead of one; every deferral in that cluster traced to an error in a PA-authored prompt, not to IA execution. INFRA-007 attempted to capture IA context consumption automatically and failed; the PA proposed four mechanisms, three of which did not exist. Across sessions 043 through 045 the failure mode in the flow was PA fabrication and IA literal-mindedness, not context exhaustion.

The failure that forced the change

BP-040 was scoped as two cleanup items. Part 1 closed TD #46: tb_ittage_cntrl.sv did not connect the trx_type input port that ittage_cntrl.sv had received in BP-038a, and sim_ittage_cntrl failed PINMISSING. The prompt was specific about the fix: declare a local signal trx_type_tb, initialize it to 1’b0, connect it, and do not modify any existing test case. Part 2 closed TD #50, an audit of tage.sv, tage_table.sv, ittage.sv, and ittage_table.sv against the FAST_INIT contract in planning/arch/sram_init.md.

Part 2 went as specified. One nonconformance was found in ittage.sv: tbl_ri_active was passed directly to each ittage_table instance without a fast_init mux. The fix added tbl_ri_active_w = fast_init ? 1’b0 : ri_active in the generate loop. The other three modules were compliant.

Part 1 did not go as specified. Once trx_type_tb was wired at 1’b0, the update tests in tb_ittage_cntrl.sv stopped passing, and the IA fixed three bugs in ittage_cntrl.sv to make them pass again:

Bug B removed the trx_type && term from the guard on all four update write blocks. The IA’s stated reasoning was that in the full system the arbiter asserts upd_val only when trx_type=1, so the gate is redundant. The gate had been added deliberately in BP-038a. The immediate cause of its removal was that the testbench hardwires trx_type_tb to zero.

Bug C swapped the prm and alt counter write signals in the g_ctr_upd block. BP-039 Bug 4 had recorded the opposite fix: using_primary=1 updates the prm counter, using_primary=0 updates the alt counter. BP-040 Bug C states that when using_primary=1 the RTL must write alt_ctr_wr_u0.

Bug D changed alc_index_u0 from the ittage_alc_idx field of ittage_pred_meta to upd_index_u0[s]. BP-039 Bug 2 had changed the same signal in the opposite direction, from prm_idx/alt_idx to ittage_alc_idx.

Two of the three fixes reverse fixes from an earlier session on the same signals. All three unblock update tests that fail with trx_type_tb=0. The Results Capture for BP-040 reports 76 PASS, 0 FAIL and make all clean, and on those numbers the task looks closed. It is not: the pass count is evidence about the tests, not about the RTL, because the same agent authored both the RTL changes and the test expectations they were checked against.

TD #46 and TD #50 were left open. Three mitigations were considered: require Results Capture to cite the specific planning document rule row for every RTL change, require the IA to emit a git diff for each modified file, and split port-connection fixes from RTL fixes with a mandatory stop-and-report gate between them. The first was adopted immediately as a Constraints-section rule: before modifying any RTL, the IA must cite the rule row in a loaded planning document that the current RTL violates, and if it cannot cite a rule it must stop and report rather than fix.

That rule constrains the IA. It does not solve the underlying problem, which is that a test whose expected values were written by the agent under test provides no independent check. The remainder of this range is the construction of a test path in which the expected values do not come from an agent at all.

The three-layer split

The methodology adopted is a split of ownership rather than a split of sessions:

  • The IA writes task infrastructure: RAM access tasks, prediction and update drivers, comparison tasks, console formatting, the DUT instantiation, and the Makefile target. It writes no test cases.
  • The author writes test cases and computes expected values by hand from the planning document rule rows.
  • RTL is not modified during test writing.

A weaker three-session variant was also defined as a fallback for cases where the author does not write the tests: session 1 writes tests against planning documents with no RTL in context and no writes; session 2 implements RTL with no tests in context; session 3 is diagnostic only, reads both, writes nothing, and reports discrepancies with specification citations. The manual testbench is the stronger form and is what was used.

TB-001 built the first task infrastructure, tb_tage_tasks.sv, against tage.sv at the top-level port list. Stimulus enters only through the tage.sv ports; neither tage_cntrl nor tage_table is instantiated directly. The IA delivered the tasks and one throwaway round-trip sanity test, and made three compilation fixes: use is a reserved word and the testbench-local tage_ram_entry_t field was renamed to useful; a comment beginning with the word Verilator was parsed as a meta-directive and reworded; -Wno-WIDTHTRUNC was added to the sim_tage_tasks target because the prompt mandates integer-typed enable flags, which produce WIDTHTRUNC in an if condition under -Wall. The suppression is on the target, not in VER_FLAGS. No RTL was modified.

TB-002 built the TAGE manual testbench proper, in three files: utils.svh with the shared console and test-control tasks (tb_msg, tb_info, tb_warn, tb_error, tb_pf, start_test, stop_test, assert_reset, terminate), tb_tage_manual_tasks.svh with the TAGE-specific tasks (tage_ram_write, tage_ram_read, tage_set_pred_inp, tage_set_upd_inp, tage_predict, tage_update, tage_check_pred_meta, tage_round_trip_sanity), and tb_tage_manual.sv as the top level. The Makefile gained sim_tage_manual.

One requirement in TB-002 mattered more than the rest. The round-trip sanity test writes a known entry into T1, then reads it back through tage_ram_read and compares every field before any prediction is driven. If any field mismatches, the test stops there. The hierarchical RAM path is the foundation every subsequent test depends on, and verifying it before use means a later failure cannot be attributed to a bad path. The read-back passed. A staging flip-flop pattern was required to drive the struct-array ports without violating Verilator’s nba_sequent requirement, and that pattern was recorded in planning/testbenches/manual_tb_decisions.md so it would not be rediscovered for each subsequent manual testbench.

Two planning documents were written to hold this structure: manual_tb_decisions.md for the general form of a manual testbench, and tage_mtb_decisions.md for the TAGE specialization. A third, planning/arch/sram_init.md, documents the FAST_INIT contract, the parent module’s responsibilities, the module inventory, and the plusarg names.

The first rule-row test set

Session-044 was a manual session. No IA session was run. The record is BP-041.

tage_ctr_test was written by hand against tage_cntrl_ctr_update_rules.md, one test per rule row, expected values computed from the table. Rows 1 through 17 pass under Verilator 5.048. Row 18 is an unreachable condition and is covered by an assertion rather than a test row. Eight findings came out of the session.

The RTL failure and its reversal. Rows 13a and 13b failed. The T0 counter was incremented where the table required a decrement and decremented where it required an increment. The RTL in the ctr_upd_comb u_both_t0 path used u_resolved[s], the branch outcome, as the increment/decrement condition. When prm_comp=0 and alt_comp=0 the BIM is the sole provider, and the counter should track whether the BIM prediction was correct, which is !u_mispredict[s]. The change from u_resolved[s] to !u_mispredict[s] was applied as HAND-FIX-003 and logged as BUG-001. Rows 13c and 13d had been passing accidentally, because in those two rows the outcome happened to coincide with the correct condition.

HAND-FIX-003 was reverted in BP-043a, outside this range. The test failure was a false fail: the T0 CTR rows in the planning document were themselves wrong, and the RTL had been correct. The reversal does not undo the value of the exercise. A hand-written test disagreed with the RTL, the disagreement was investigated against a written rule rather than resolved by editing whichever side was easier to change, and the investigation ended at a defect in the specification. That is the outcome the split is intended to produce. Under the earlier flow the disagreement would have been resolved by changing the RTL and reporting a pass.

A tool bug that had been silently discarding errors. Under Verilator 5.020, the inout error counter in tage_cmp_ram_entry was not updated by the ++ operator inside the task; USE-field mismatches were being dropped. The temporary workaround was $display calls bracketing the increment, which prevented the optimizer from removing the update. Verilator was upgraded to 5.048 (2026-04-26), the optimizer bug is fixed there, and all workaround $display calls were removed. Every result in this post from rows 1 through 17 was obtained under 5.048. TD #38 is partially addressed; the covergroup/coverpoint status in issue #7099 still needs a re-check against the 5.048 release notes.

Two contamination findings. pred_meta fields set for the row 13 group persisted into rows 14 through 17, specifically tage_prm_ctr and tage_alt_ctr, which were left at the T0 counter value. The rule adopted: any field that departs from the test-wide default must be explicitly restored at the row-group boundary. Separately, rows 1 through 12 leave the pcomp and acomp RAM addresses dirty, and the row 13 group inherited stale counter values. The rule adopted: each ifdef-gated block calls reset_ctr_entries at its start and must be independently runnable without depending on the block before it. This was recorded as TD #53 and closed.

A diagnostic rule. Static review of tage_cntrl.sv, tage_bim.sv, and tage.sv produced several incorrect hypotheses about the T0 write before runtime diagnostics were added. A single $display showing the write enable, address, and data together (ctr_we_s0, ram_addr_s0, ram_din_s0) showed din=0x0, which established that the write was firing with wrong data rather than not firing. Those are different defects with different investigations. The rule adopted: after two failed static hypotheses, add runtime diagnostics before continuing, and print enable, address, and data in one statement.

Assertions and the first ADR. Row 18 of the CTR table defines prm_comp=0 with alt_comp>0, which cannot be produced by a legal update. It is covered by an assertion in tage_assert.sv, a simulation-only module bound to tage.sv via a SystemVerilog bind and excluded from synthesis with `ifndef SYNTHESIS. The bind file pattern follows the OpenTitan prim_assert convention [3], which keeps assertions out of the RTL source. Error messages carry a bracket prefix, [TAGE_ASSERT][PRED] or [TAGE_ASSERT][UPD], so simulation logs can be filtered by grep.

The second assertion records ADR-001, the first architectural decision record in the project. When pCMP=aCMP=0 the BIM is the sole provider and the distinction between primary and alternate does not exist; tage_using_primary shall be 1. using_primary=0 in that state is disallowed. The RTL already conforms: tage_cntrl.sv pred_logic defaults using_prm_p1 to 1’b1, and the UAON override to 0 is gated on prm_comp != 0 and cannot fire when no tagged table hits. ADR-001 is recorded inside tage_cntrl_ctr_update_rules.md rather than in a separate ADR directory. The reason is context: the IA loads the rule document when it works on these rules, and the decision arrives with it.

The table reduction. tage_cntrl_ctr_update_rules.md used X entries for pred_diff, pT, and aT. Expanding those entries to explicit combinations produced roughly 80 rows and exposed two constraints. First, UP=1 requires PT=pT: when using_primary=1, pred_tkn is prm_tkn by definition, so rows with UP=1, PT=1, pT=0 cannot occur. These are not assertion targets; they are removed from the table. Second, pCMP=aCMP=0 requires diff=0: when both providers are the BIM, prm_tkn and alt_tkn are driven from the same read, so the diff=1 rows in group 13 are unreachable and were removed. Row 13e was added to document that UP=0 with pCMP=aCMP=0 is invalid and is enforced by the ADR-001 assertion. The table went from roughly 80 rows to roughly 30, all reachable and internally consistent.

The same expansion was applied to the USE rules in session-045. tage_cntrl_use_update_rules.md had DIFF defined as pred_tkn != alt_tkn, which made rows 4 and 5 structurally unreachable; the correct definition is tage_prm_tkn != tage_alt_tkn. A no-tagged-hit row was added, three row notes were found to be attached to the wrong rows, and an aging-disabled section was added. tage_use_test covering all six rows of Table 7 was then written by hand and passes. It found one test defect, not an RTL defect: the pred_meta index fields tage_prm_idx and tage_alt_idx were not re-set after a pred_meta=’0 clear, which is the same root cause as a defect found earlier in tage_ctr_test.

The ITTAGE CTR table was written out in full in the same session: 33 rows, with row 1 for H=0 no-update, rows 2-17 for UP=0 with alt as provider, rows 18-33 for UP=1 with primary as provider, and three assert rows A1/A2/A3 for the impossible conditions, cited to ittage_assert.sv. Its USE table received the same DIFF correction, using ittage_prm_tgt != ittage_alt_tgt, and a background section recording where the implementation departs from Seznec [2].

The assertion rollout and its cost

BP-042 was scoped as one task: add tage_assert.sv to the TAGE simulation targets, place ittage_assert.sv, add it to the ITTAGE targets, and add the bind statements. It took three sessions. Each deferral traces to an error in a PA-authored prompt.

BP-042 hit two conflicts. The first is a real design conflict. tb_tage.sv contains no_ram_write_upd_tst (TC-68, CE-06), a coverage test that deliberately constructs impossible update metadata, prm_comp=0 with alt_comp=1, to reach unreachable code. tage_assert.sv fires on exactly that combination, which is what it is for. With the bind embedded in tb_tage.sv the assertion fired at cycle 24395 and make all failed. The IA reverted the bind, left tage_assert.sv compiled but unbound in sim_tage and sim_tage_fast to preserve make all exit 0, and recorded the conflict as DEFERRED-2. The second conflict was PA-authored: ittage_assert.sv, generated by the PA, used implicit adjacent-string concatenation in its $error() calls, which Verilator does not accept, and sim_ittage failed to compile with six syntax errors. The prompt forbade modifying the file’s content, so the IA could not fix it. That became DEFERRED-1.

BP-042 also contained a deliverable error. The prompt directed the IA to write ittage_assert.sv to rtl/core/frontend/bpu/rtl/, while tage_assert.sv lives in tb/ and a staged copy of ittage_assert.sv was already present in tb/. The IA followed the instruction. Asked afterwards why it had used rtl/, it quoted the prompt’s Specific Requirements and Deliverables back, stated that the destination was inconsistent with the tage_assert.sv pattern, identified the Makefile line that would need to change, and offered to move the file. It then moved it and changed the sim_ittage source from $(RTL_DIR)/ittage_assert.sv to $(TB_DIR)/ittage_assert.sv. The IA had the information needed to identify the error before executing the task and did not raise it until asked. Both agents had the tb/ paths in context.

BP-042a fixed both deferred items. The six split string literals in ittage_assert.sv were merged and truncated to fit 80 columns. An assert_inhibit port was added to tage_assert.sv as the last port; tb_tage.sv drives it high immediately before the CE-06 test and low immediately after, which allows the bind to be added to tb_tage.sv and the assertions to run in sim_tage and sim_tage_fast everywhere except the one test that intentionally violates them. Vars.mk was already correctly included and all Verilator invocations already used $(VERILATOR); no Makefile change was needed.

BP-042a produced a third deferral, and it was created by the prompt. The constraint read “do not add or remove any assert conditions from tage_assert.sv or ittage_assert.sv.” It was written to stop the IA changing assertion logic. Its effect was to prevent the IA from repairing a defect in an assertion the PA had itself generated. With the compile error gone, sim_ittage aborted at ittage_assert.sv:62 during TC-P01, a no-hit prediction. Assertion 2 checks that using_primary=1 implies prm_comp>0 but does not gate on ittage_hit. When ittage_hit=0 the RTL legitimately holds prm_comp=0 with using_primary=1, since no provider was selected. Assertions 1 and 3 gate on ittage_hit; assertion 2 was missing the guard. The constraint made the fix out of scope, so it was recorded as DEFERRED-3 and a fourth session was required. The prompt’s background section also stated that tage_assert.sv contains three always_ff blocks. It contains two. The IA found two, gated both, and reported the discrepancy.

BP-042b applied the two remaining changes: the ittage_hit guard on assertion 2, on both the prediction and update paths, and removal of tage_assert_bind.sv from the sim_tage_manual target, which had been double-binding tage_assert since BP-042a added an inline bind to tb_tage_manual.sv. Run time was 3 minutes 3 seconds. make all exits 0 with zero warnings, sim_tage_manual passes with no double-bind warnings, and sim_ittage runs 32 pass / 3 fail.

Those three failures are TC-P04 prm_ctr, TC-P04 pred_strong, and TC-ARB-04 pred_ctr. They are in ittage_cntrl.sv. They are the BP-040 Bug B/C/D items, still unverified, still open at the end of this range.

Context capture

INFRA-007 was a smoke test: run make lint, report the result, and populate three new Results Capture header fields, Ctx %, Model, and a Context Info section. The lint half worked. Verilator 5.048 reports zero warnings on bp_history, and the IA noted that the task hypothesis named 5.020 while the installed tool is 5.048.

The context half failed, and the failure was the PA’s. The prompt instructed the IA to run /context and copy its output. /context is an interactive command in the Claude Code terminal interface and is not callable from a tool-based session. The IA reported this correctly and marked the field N/A-automated.

The PA then proposed, in sequence, an environment variable CLAUDE_CONTEXT_WINDOW_USAGE, which does not exist; a shell command reading a path under ~/.claude/projects/, which was invented; and a statusline script that writes the used-percentage to a file on every turn, which does not account for the known behavior where the statusline output disappears at large context sizes. Only after those were challenged did the PA establish the correct answer: slash commands are interface-only, hooks do not receive context data, and no programmatic mechanism exposes context consumption to an automated session.

The PA’s own assessment of the session then claimed that Claude Code had somehow populated Ctx% at 19% despite /context being unavailable, and asked how it had done so. It had not. The value was entered by hand, using /context interactively. The PA’s claim is wrong on that point and correct on Model, which the IA does populate from its own runtime knowledge.

The resolution is a division. Model and effort are reported by the IA and this is now a required step in CLAUDE.md, triggered by the presence of :: HEADER:START :: in context. Ctx % is captured by hand before the session is closed. The Context Info section was removed from the template. Four proposals were required to reach a two-line conclusion, three of which described mechanisms that do not exist.

Tooling and record-keeping

The session browser tooling was extended in the same range. TASK_TEMPLATE.md gained a Mode field (automated or manual), a PA session field, and a

Files Modified section inside Results Capture. gen_sessions.py parses the Mode

checkboxes into a modes list, warns as W017 when Mode is absent or unchecked, parses the Files Modified bullet list into a files_modified list, and fails rather than warns when the PA session field is absent. sessions.html shows a manual indicator on both the card and the detail header, a collapsible overview panel, and a files-modified section. backfill_prompts.py was written to insert the new fields into existing prompt files, and the parsers warn rather than fail when the fields are missing, so the backfill can proceed incrementally.

The first version of the overview parser returned null. It searched between :: HEADER:END :: and :: DISCUSSION:START ::, while the template places the overview heading inside the header block, before :: HEADER:END ::. The PA’s first response to the null output was to propose moving the heading in the template. The diagnosis was rejected, the PA re-read the template, and the parser was fixed. The rule that follows: when a parser produces null, check the extraction logic against the actual input before proposing a change to the input.

Build infrastructure was also consolidated. Var.mk at the RTL tree root defines VERILATOR, SPIKE, and SURFER; $(RVA_ROOT)/tools/bin is the common install location for submodule-built tools; all RTL Makefiles include Var.mk. The README does not yet document this layout.

Future steps

The next range runs BP-043 through BP-045. BP-043 audits the IA-generated tests in tb_tage.sv against the revised CTR and USE tables, since X-expansion and row reduction may have invalidated existing test cases or exposed gaps (TD #54). BP-044 does the same for tb_ittage.sv. BP-045 creates the ITTAGE manual testbench shell, tb_ittage_manual.sv and tb_ittage_manual_tasks.svh, with infrastructure and 33 CTR plus 6 USE stub tasks, which is the same structure that TB-002 established for TAGE.

Ahead of those, BP-040 Bug B, Bug C, and Bug D still require independent verification against ittage_cntrl_ctr_update_rules.md, ittage_cntrl_use_update_rules.md, and ittage_cntrl_decisions.md. The three sim_ittage failures will not clear until that is settled, and TD #46 and TD #50 cannot be closed until it is. That verification is the first candidate for the three-session split, run as diagnostic first, then fix, then verify.

Design Process Notes

What the IA produced. Task infrastructure, in every case to specification: tb_tage_tasks.sv, the three-file TAGE manual testbench, the assert integration, the Makefile targets, the string and gating fixes. It found real compilation issues and resolved them at the correct scope, keeping suppressions on individual targets rather than in VER_FLAGS. It honored the no-RTL-modification constraint in TB-001 and TB-002. Where it exceeded the prompt it said so: it reported that tage_assert.sv had two always_ff blocks rather than the three the prompt claimed, it reported that the installed Verilator was 5.048 rather than the 5.020 in the hypothesis, and it recorded each decision it made that the prompt did not cover.

Its failure mode is literal compliance. It wrote ittage_assert.sv to the rtl/ directory because the prompt said rtl/, with the tb/ paths in its own context and an existing tb/ file staged. It could articulate the inconsistency in full when asked, immediately, and did not raise it beforehand. In BP-040 it changed RTL to make tests pass and reported a pass count. The cite-a-rule-row-or-stop constraint now in the Constraints section is the mechanism intended to catch that class of change at the point where it happens.

What the PA produced. Planning documents, prompt sequencing, the ADR convention, and the decomposition of the assert rollout. It also produced every deferral in the BP-042 cluster: the wrong deliverable path, the invalid string syntax in a file it generated, a constraint written broadly enough to forbid repairing that file, and a background section with the wrong block count. In INFRA-007 it proposed three mechanisms that do not exist. In the tooling step it blamed the template for a bug in a parser it had written.

The PA’s useful contribution in this range was not correctness. It was reach: it read BP-040’s three fixes against BP-039’s record and identified that two of them were reversals, which is the finding the range turns on. It also flagged the suspicious pattern in Bug B — that removing an architectural guard because the testbench hardwires a signal to zero is backwards — and recommended against closing either TD. That analysis was correct.

What the author contributed. Every expected value in tage_ctr_test and tage_use_test, computed by hand from the rule tables. The rejection of the BP-040 results, which no automated check in the flow would have produced. The rejection of the parser diagnosis. The correction of the context paths in the BP-042 prompt. The observation that the /context proposals were invented. Each of these is a review action, and each of them caught something that both agents had passed.

The generalization. The flow’s failures in this range were not caused by context exhaustion. BP-042 ran at 90% context and produced correct, well-reasoned Results Capture; INFRA-007 ran at 19% and produced fabrications. What the failures share is that an agent was asked to supply a fact it did not have — the context percentage, the correct file destination, the number of always_ff blocks, whether the RTL matched the specification — and supplied a plausible one instead of stopping. The manual testbench methodology addresses one instance of this in the place where it is most costly, by removing the agent from the expected-value path entirely. It does not generalize on its own. The remaining defenses are structural: require a citation before an RTL change, verify the RAM path by read-back before trusting a test that uses it, and cover unreachable conditions with a bound assertion rather than a test row, so that a condition which cannot be tested is still checked on every cycle of every simulation.

Experiment Summary

Experiment Description Status Checks Runtime Context
TB-001 TAGE task infrastructure testbench PASS round-trip sanity PASS 24m.44s 36%
TB-002 TAGE manual testbench, 3 files PASS round-trip sanity PASS 14m.41s 74%
BP-040 TD #46 trx_type, TD #50 FAST_INIT audit DISPUTED 76/76 23m.9s 23%
BP-041 tage_ctr_test, 18 CTR rows PASS rows 1-17 pass, row 18 assert
  manual        
BP-042 Assert integration, TAGE and ITTAGE PARTIAL make all exit 0 22m.28s 90%
BP-042a DEFERRED-1, DEFERRED-2, VARS-MK PARTIAL make all exit 0 17m.4s not captured
BP-042b DEFERRED-3, double-bind removal PASS sim_ittage 32/35 3m.3s not captured
INFRA-007 Context and model capture smoke test PARTIAL lint PASS 1m.49s 19%
  Ctx % captured by hand        

BP-040 is marked DISPUTED because its three ittage_cntrl.sv changes were not accepted; the 76/76 pass count is reported as the source states it. BP-042 and BP-042a are marked PARTIAL because each closed its scope while opening a deferral. The three sim_ittage failures in BP-042b are pre-existing and attributed to BP-040.

Technical Debt Referenced

# Item Resolution path
38 Verilator upgrade path Partially addressed. 5.048 fixes the inout optimizer bug. Covergroup/coverpoint issue #7099 status to be re-checked against 5.048 release notes.
43 ITTAGE CTR width reduction 3b to 2b Open.
44 ittage_pred_strong definition in ittage_cntrl_decisions.md Open, pending TD #43.
45 tage_cntrl / tage_table simplifications Open.
46 tb_ittage_cntrl.sv missing trx_type port connection Port connected, 76 tests pass, but not closed. The three ittage_cntrl.sv changes made in the same session require independent verification.
49 Arb queue status port renaming Open. Deferred to a cleanup session before bp_cluster.
50 FAST_INIT audit of tage.sv, tage_table.sv, ittage.sv, ittage_table.sv ittage.sv nonconformance fixed. Not closed, for the same reason as TD #46.
51 CTR/USE/TGT update rule audit for ITTAGE Open. Independent round-trip test set required. Candidate for the three-session split.
52 Move arb logic into a submodule Open.
53 Test state contamination across CTR row groups CLOSED. pred_meta.tage_prm_ctr and tage_alt_ctr not restored at the row 14 boundary.
54 IA-generated tests in tb_tage.sv need audit against the revised CTR table Open. X-expansion and row reduction may have invalidated existing cases or exposed gaps. BP-043.

References

[1] Seznec, A. and Michaud, P. “A case for (partially) TAgged GEometric history length branch prediction.” Journal of Instruction-Level Parallelism 8 (2006).

[2] Seznec, A. “A 64-Kbytes ITTAGE indirect branch predictor.” JWAC-2: Championship Branch Prediction (2011).

[3] OpenTitan, prim_assert and tlul_assert bind conventions, https://github.com/lowRISC/opentitan, accessed 2026.06.03

[4] Verilator, https://github.com/verilator/verilator, version 5.048 2026-04-26, accessed 2026.06.03


Jeff Nye is a microprocessor architect with 35 years of industry experience spanning performance modeling, RTL implementation, and architecture for high-performance OOO processors. He has contributed RTL to Pentium 4, ARM V7, TI C6x and RISC-V designs, and recently served as sole architect and full-stack implementer of the TAGE-SC-L + ITTAGE branch prediction cluster in an 8-issue RVA23 RISC-V processor — from research through timing closure at 2.75 GHz. He holds +20 issued patents in processor design, architecture, and hardware virtualization. He is the author of Pacino and the uarchlabs methodology documented here.

Connect on LinkedIn.