One or Two Prediction Slots, the Last Front-End Debt and a CSR Definition
Abstract
NUM_PRED_SLOTS sets how many branches the predictor reports per fetch
block. It was meant to take 1 or 2 and only 2 worked. BP-128 made both
values work from one macro in bp_defines_pkg.sv and added a one-slot twin
of every lint and simulation target, 106 in all. At two slots nothing
changed. At one slot every program retired exactly, and several took up to
80% more cycles, because a block’s second branch has no slot.
Two tasks then sized the branch history path by the slot count. The first found that at one slot the history correction after a mispredict sometimes needs two bits, and that carrying one cost mispredicts. I ruled that the correction carries up to two bits at either slot count, and the second task restored every one-slot figure exactly.
A measurement task, BP-131, counted how often a block holds more branches than slots, how often a region’s shared block end is rewritten by another start, and how long a branch waits between resolution and commit. The counts showed that these directed programs cannot decide the slot questions, and I deferred them to compiled benchmarks.
Five tasks closed the remaining front-end debt. This post reports what was not expected: a T0 counter that was read as the wrong 3-bit value, a RAS undo that lost recursion levels, test rows that seeded states no prediction produces, slot-1 update paths whose removal passed the full regression, and a decode speed-up that was a compiler cache.
Two system tasks replaced the configuration and control signals that testbenches had driven individually with packages built from a new CSR definition. One program changed by 40 cycles because it had set a CSR state that no software write can produce.
Where the range starts
The previous bpu post ended with every predictor in use and every open
predictor RTL and verification item closed. The fetch series had built the
rest of the front end, and the regression ran 155 targets, including 36
tb_fe_top programs that run instructions through the whole front end
against a testbench backend.
Session 078 began with a scrub of the technical debt table, led by me, item by item. Several items closed by ruling with no task. Two decisions from that scrub shaped the range: the one-slot build (TD#1) would be made real, and the remaining front-end items would be closed before the CSR definition (TD#178). The scrub also created a second table, Performance and Verification Debt, for items that need benchmark data rather than a fix.
One source for the slot count
The ruling for BP-128 had four parts: the values are 1 and 2, with 2 the
default; the value is set in one place, from a macro; every lint and
simulation target of the bpu, FTQ and fe_top units passes at both values;
and every new target joins the regression.
bp_defines_pkg.sv now takes NUM_PRED_SLOTS from BP_NUM_PRED_SLOTS,
defaulting to 2. A package-scope check stops elaboration at any other
value. Verilator rejects an elaboration if with $error at package scope,
so the check is a constant function that reaches $fatal. A second macro,
BP_EXPECT_PRED_SLOTS, makes the package stop unless the slot count equals
it. Each one-slot target, <target>_1slot, sets both. To show that each
was built at one slot, the IA rebuilt all 106 with the slot macro removed,
and all 106 stopped with “NUM_PRED_SLOTS 2, expected 1”.
Ten modules lost their own slot parameter. Five of them had a literal
default of 2 and so ignored the package: tage, tage_cntrl, tage_table,
tage_bim and ittage_table. ittage.sv never overrode the last of these,
so ittage_table had been two-slot at any package value. loop_pred had a
literal 1 that bp_cluster overrode. ubtb kept a parameter, renamed
N_SLOTS with the package default, because tb_ubtb builds a uBTB at each
width in every build. The update queue write-port counts of TAGE, SC and
ITTAGE were literal 2 and now follow the slot count; sc.sv requires its
count to equal the slot count, so lint failed at one slot without this.
The IA swept the RTL of the three units for patterns that assume two slots without naming the parameter, read all 239 hits, and changed 37 sites. The largest were the copied slot-0 and slot-1 blocks in the TAGE, ITTAGE and SC tables, each now one generate loop. No port was added, removed or re-declared at either value.
At two slots, 150 of the 155 baseline logs were identical after removing
time and path lines. The other five were coverage targets whose line
totals fell because the copied blocks became loops; their check counts
were unchanged. At one slot every testbench passed. Each check that needs
two slots prints a SKIP line and is named with its count. Two checks in
tb_loop_pred had printed PASS at one slot without running anything, and
were fixed.
Every program retired exactly at one slot. The cost appeared where a block holds two conditionals, or a conditional and a jump:
| Program | Cycles, 2 slots | Cycles, 1 slot | Mispredicts, 2 / 1 |
|---|---|---|---|
twocond |
533 | 952 | 11 / 36 |
indir |
1164 | 1668 | 24 / 71 |
hist |
871 | 1057 | 22 / 42 |
multistart |
412 | 647 | 7 / 19 |
recur |
765 | 1139 | 28 / 24 |
The IA ran automated and took its design choices provisionally. Two
carried the rest of the range. The history bundle and FTQ rollback ports
stayed two wide at both values (D1). The FTB and uBTB entries kept two
conditional fields at one slot, so the second branch of a block is not
predicted and the backend corrects it (D2), which is where the one-slot
cycles come from. The planning assistant pointed out that a third, D4,
gated two loop predictor checks in loops that do not depend on having two
slots: at one slot the unslotted branch after the loop branch has its
resolution placed into the loop branch’s slot, and the loop predictor never
becomes trusted there. The IA had stated that link as inferred, not traced.
I ruled D1 to be fixed, deferred D2 with the block split to TD#179, and
accepted D3 to D5.
The history correction at one slot
BP-129 sized the history inputs and the FTQ rollback ports by
NUM_PRED_SLOTS, with a new package width PRED_CNT_BITS for the counts.
To make the second-branch logic exist only at two slots, the IA rewrote
bp_history’s two hand-written per-branch blocks as one loop over branches
with a new function, fold_all_step. That also did the cleanup recorded as
TD#82.
The task asked whether one case occurs: at one slot, a backend mispredict
of a branch that no slot held, so that ftq_npc’s correction is two bits,
a not-taken bit for slot 0 and then the mispredicted branch. It occurred 72
times in seven programs. A one-bit port cannot carry both, and the IA
listed four choices with their effect on the history. It built the only one
that fits the port and keeps the resolved direction as the newest history
bit: the mispredicted branch’s bit alone. The cost against the old two-wide
port was more mispredicts at one slot in six programs, from rvc 18 to 26
and indir 71 to 86.
I ruled that this was not acceptable for performance, and that the
correction carries up to two bits at either slot count: at most one
not-taken bit before the mispredicted branch’s bit. It is not one bit more
than the slot count; at two slots it stays two bits. BP-130 added
RB_CORR_BITS = 2 and RB_CNT_BITS = 2 to the package and widened the
correction path from ftq_npc through ftq, fe_top and bp_cluster
into bp_history, including the per-checkpoint bundle size ckpt_nb. The
prediction inputs stay sized by the slot count.
All 72 two-bit corrections then reached bp_history, and every one-slot
figure returned exactly to its value before BP-129. Nothing changed at two
slots. The history properties H1 and N11 now check every bit of the
correction; before BP-130 neither could see the older bit of a two-bit
correction at one slot, and each was shown firing on that bit by a fault on
a scratch copy. The IA also reported that bp_history_decisions.md 3.5
says the checkpoint needs no slot count, while the RTL has stored a
per-checkpoint bundle size since BP-121.
What the programs can decide
Before building the block split of ftb_decisions.md section 3, in which a
block ends after as many branches as there are slots, I asked for
measurements. BP-131 changed nothing in the tree. It added read-only
monitors to a scratch copy of tb_fe_top, confirmed that every program
gave its baseline figures with them, and counted per program at both slot
counts.
- Blocks with more branches than slots: 9 at two slots, all in
twocond, and 243 at one. No region in these programs holds three conditionals, so the FTB never had to replace a conditional field. At two slots the split would change almost nothing here. - A backend mispredict costs 11.1 cycles on average at two slots and 11.3 at one, with a maximum of 20, over the 21 programs without page walks or uncached fetch. The planning assistant had estimated 8 to 12.
- Block ends shared in a region: 73 of 260 FTB entries were hit by more than one block start, up to six. About 92% of the changes to an entry’s block end were made by a start other than the previous writer. In every over-capacity block where another start’s end was recorded, the end a split would write differs from it: 228 of 228 at one slot. With the current entry format, a split would add more rewriting of that shared value. This is the region sharing behind TD#168.
- Training from another branch’s metadata, through the placement rule for a resolution that matches no slot: 27 times at two slots and 312 at one.
- Wrong-path training could not be measured. The testbench backend resolves only retired instructions, in program order.
- Resolution to commit: 6.4 cycles on average at two slots. Even in that window, 1,566 of 2,846 branches had their FTB region read again before commit, and for 876 of them the resolution had already changed the entry. Training at commit would have shown those reads older state. A core with a reorder buffer has a longer window.
The counts also showed two behaviours the questions had not asked about. The uBTB stored one branch in both of its conditional fields 82 times at two slots, because it fills the field the update’s slot names without checking the other. The placement rule displaced a later branch while a slot was free 31 times at two slots.
I concluded that the tb_fe_top programs are directed tests, written to
reach particular cases, and not representative enough to decide the block
split, the entry format or the training point. Those decisions moved to
TD#179, in the new performance table, to be measured against compiled
benchmarks.[F1]
The last front-end debt
Five tasks closed what remained: BP-132 to BP-136. Most items closed as specified and are listed only in the technical debt table. The findings below are the ones that were not expected.
A decode speed-up that was a cache
TD#131 recorded that decode’s wall time fell from 90 to 29 seconds when
the unit moved from Verilator 5.020 to the project’s 5.048, with identical
check counts. The 5.048 installation sets OBJCACHE ?= ccache and the
5.020 installation leaves it empty. Without the cache, 5.048 was slower on
the same source: rtl/lib built in 13.6 seconds against 11.6. A decode
build takes about 42 seconds cold and 4 seconds warm. Decode times are now
compared only with the cache state stated.
The same task found two coverage tool problems outside its write access:
tools/cov_table.py measures TAGE coverage from tage.sv only, which
misses the arbitration that moved into tage_arb.sv in the same task, and
the bpu coverage targets run verilator_coverage 5.020 from the path on
data from the 5.048 build. It also removed GNU objcopy from the RVC
expander oracle; the regenerated oracle was byte-identical.
Items that were not defects
TD#79 said the RAS commit stack did not preserve recursion depth because its recursion field was never read. BP-133 found that the commit stack has no recursion compression: every committed call gets its own entry, so depth is kept by repetition, and the fallback is not degraded. The field was dead storage and was later removed. The planning text describing the limitation was wrong.
TD#89 and TD#90 asked for 22 bits per FTB entry to supply the SC update’s
branch PC slice and backward-branch sign. Both values have been formed by
ftq_upd_conv since BP-119, from the FTQ entry’s block PC, the slot
position and the resolved target, which the backend sends as the taken
target even for a not-taken branch. The rows predated the converter. No
FTB bits were added.
The T0 counter
T0, the TAGE base table, has 2-bit counters; the tagged tables have 3-bit
counters. BP-133 found one T0 update case undefined: T0 as the provider
when the use-alt-on-new-allocation counter picks it over a weak tagged
primary. The RTL never wrote T0 in that case. The IA built rows 19.1 to
19.8, in which T0 steps toward the resolved direction, with a second write
path into tage_bim. I kept the fix.
The same task found a defect on the prediction side. When T0 provides the prediction, its counter was zero-padded and then decoded as a 3-bit tagged counter. A T0 at 2’b11, strongly taken, read as 3’b011, weak not taken, and gave SC a negative TAGE term. Every T0-provided prediction was affected, and no test covered it. I ruled the mapping {ctr[1], ctr[0], ctr[0]}, which gives the exact 3-bit equivalents, and BP-134 built it as TD#182. One cluster test, G3, had been written around the defect and was reseeded.
The fix changed ten programs at two slots. hist went from 22 to 31
mispredicts, while ftbonly, indir and loops improved. Each program
still retired exactly; SC now overrides T0 in different cases. That is
TD#183, for the benchmark analysis.
BP-135 then checked the rows 19.x tests themselves. All eight seeded the
tagged primary at 3’b010, and use-alt-on-new-allocation cannot fire on
that value; four also gave a predicted direction that 010 cannot produce.
The results were right because the update path acts on the metadata as
given, but the states were ones no prediction reaches. After reseeding to
reachable states, all eight rows fail with the T0 path removed; the
original seeding had caught six. A new program, t0_uaon, reaches the case
end to end, 17 times at two slots and 16 at one. In 13 of the 17 events at
two slots T0 was already saturated, so only four of them distinguish the
fix from its absence.
The RAS recursion count
The RAS compresses a recursive call into a count on one entry. TD#78 had recorded that an undo of a pop that decremented a count did not restore it. BP-133 found the case reachable since BP-121: SC can make a return reachable at p2 and unreachable at p3, and the undo then runs. The IA registered what each p2 operation did to the counts and reversed it in the undo.
BP-134 added a program for it, rec_undo, in which a function calls
itself six deep and a conditional before each return is taken one time in
four. At two slots the program reaches the case ten times, and each undo
restores the count. With the fix removed on a scratch copy, the program’s
count check fails all ten, but its mispredicts do not rise. The counts were
already being lowered by wrong-path pops, because a mispredict restore
reloads the RAS pointers and not the counts held in the entries. That is
TD#184. At one slot the case cannot occur: a return never shares a block
with a branch before it, so the p3 view always matches p2.
BP-134 also found that an undo of a push that met a saturated count of 15 lowered a count the push had never raised. It was fixed with a registered flag.
A test-order dependence
102 of tb_tage’s 117 tasks changed their stimulus in the same time step
as the clock edge that sampled it, so the result depended on process order
within the time step. BP-134 moved every wait to the edge plus one time
unit. The results were unchanged except one count: a monitor’s prediction
grants went from 163 to 162, because a test entered on the edge had staged
its probe request twice. The race had made one test’s stimulus depend on
how the previous test ended.
Slot-1 updates no check reached
TD#74 asked what covers the slot-1 update paths of TAGE and ITTAGE. BP-135
removed each path on a scratch copy and ran the full regression. Removing
every TAGE slot-1 table write failed 15 checks. Removing only the slot-1 T0
writes, or every ITTAGE slot-1 write, passed all 272 targets. Only the
program figures moved: with no ITTAGE slot-1 updates, indir went from 22
to 70 mispredicts, and no check failed. The programs exercised those paths;
nothing compared their result.
BP-135 was written to report, not to fix, so the gap stayed open and BP-136
was needed. It runs tb_tage_manual’s counter, useful and new
use-alt-on-new-allocation tests on each slot, reading back the RAM copy of
the slot it drove and checking that the other slot’s copy keeps its seed.
It makes the epoch write visible by giving each slot a different epoch.
For ITTAGE, two new tb_ittage tests drive slot-1 updates through the
arbiter, the controller and the tables, one of them through the update
queue. Twenty-four mutations, one per path, each fail a check, and both
mutations that passed in BP-135 now fail. No RTL defect was found.
BP-136 found that tage_cntrl updates the use-alt-on-new-allocation
counter only when the alternate is a tagged table, while its rule document
also updates it with a T0 alternate. I ruled the RTL correct, and the
document was changed.
BP-135 also checked an ITTAGE interface note against the RTL and found four of its seven statements false; the useful and epoch writes open on the provider’s counter strobe, not on the primary selector alone. No RTL was wrong.
The CSR definition
The front end has two groups of inputs that will come from control and status registers. A configuration group holds the predictor enables and aging intervals and decode’s extension enables. A CSR group holds the privilege level, the virtualization bit, the address translation registers and the PMP registers. Until this range each testbench drove these as individual ports, with its own values.
csr_decisions.md, written in session 079, defines both groups. Two custom
machine-level CSRs hold the configuration: 0x7C0 the four predictor enables
and 0x7C1 the two aging intervals. The extension enables are not a CSR. The
standard CSRs are the implemented subset of satp, vsatp, hgatp, menvcfg,
henvcfg, pmpcfg0, pmpcfg2 and pmpaddr0 to 15, with the write rules the
privileged specification gives for them [1]. Addresses and bit positions
were checked against Spike’s encoding.h [2] and the riscv-opcodes CSR
table [3]. Three group structs, fe_cfg_t, fe_csr_t and ptw_csr_t,
carry the members to fe_top, the ITLB and the walker.
SYS-001 built csr_pkg with the layouts and the structs, the struct ports
on fe_top, the ITLB, the PMP checker and the walker, and csr_tb_pkg, a
testbench shadow of every CSR that applies the write rules and packs the
groups. All 15 testbenches that drove a group member now take it from
csr_tb_pkg. The shadow is a class with static members. Written as plain
package functions, it was inlined at every call site and the tb_fe_top
C++ compile ran past five minutes before it was stopped.
The inventory showed five testbenches resetting to values different from
tb_fe_top’s, including tb_bp_cluster with the SC disabled. Each now
resets through csr_tb_pkg and then writes its own value.
One program changed. Case 3 of pt_ad_h had given the walker
henvcfg.ADUE = 1 with menvcfg.ADUE = 0. No CSR write can produce that state,
because henvcfg.ADUE reads 0 while menvcfg.ADUE is 0. Through csr_tb_pkg
the case now sees ADUE 0 at both stages, takes a VS-stage page fault in
place of a G-stage guest-page fault, and skips one G-stage translation.
pt_ad_h went from 1,435 to 1,395 cycles at both slot counts. The task had
defined any figure change as a defect, and the IA reported it as one. I
accepted it.
The IA also reported three rules from the privileged specification that
csr_decisions.md lacked: henvcfg.PBMTE reads 0 while menvcfg.PBMTE is 0,
a pmpcfg entry with R = 0 and W = 1 is reserved, and an entry’s pmpaddr is
not writable when the next entry is locked TOR. I added all three, and
SYS-002 built them. The planning assistant had predicted that the PBMTE
rule would change two tests, as ADUE had. Neither changed: the walker uses
henvcfg.PBMTE only for a VS-stage leaf’s PBMT, and in both tests that leaf
is 0.
SYS-001 had also cost coverage. The PMP checker tests now received pmpaddr as software reads it back, so they no longer exercised the checker’s own masking of the low address bits. SYS-002 restored it by writing and packing at a granule of zero, so the checker at its real granule receives the bits as written; removing the checker’s masking fails six of the new checks, while the old ones still pass. The NA4 half of the request could not be checked at that granule, because NA4 and NAPOT use the same mask there.
Experiment Summary
| Experiment | Description | Status | Checks | Runtime | Context |
|---|---|---|---|---|---|
| BP-128 | NUM_PRED_SLOTS 1 or 2 from one macro; one-slot targets | PASS | 261/261; 106 _1slot targets fail with the macro removed; 36 programs retire at 1 | 59m 13s | 54% |
| BP-129 | History and rollback ports sized by NUM_PRED_SLOTS | PASS | 261/261; 72 two-bit cases counted; H1, N11 fired at 1 | 68m, IA from timestamps | 54% header, 31% context report |
| BP-130 | Two-bit history correction at either slot count | PASS | 261/261; 72 of 72 reach bp_history; one-slot figures restored | 36m | 19% |
| BP-131 | Measurement: blocks over capacity, region sharing, resolve to commit | PASS | 261/261 at both ends; tree unchanged | 48m 27s | 32% |
| BP-132 | Clean-up: TD#49, 52, 77, 129, 130, 131, 147, 176, 177 | PASS | 265/265; t_fill_rpl 31 checks | 54m 34s | 36% |
| BP-133 | TD#18, 78, 79, 89, 90 | PASS | 265/265; rows 19.x fail 6 on the old RTL; tb_ras fails 4 on the old RTL | 46m 17s | 30% |
| BP-134 | TD#182 T0 mapping; RAS saturation; rec_undo; tb_tage race | PASS | 267/267; pred_t0_conf_tst fails 24 on the starting tree; rec_undo fails 10 of 10 without the TD#78 fix | 1h 46m 52s | 40% |
| SYS-001 | csr_pkg, csr_tb_pkg, struct ports, 15 testbenches | PASS | 270/270; tb_csr_tb_pkg 126; pt_ad_h 1435 to 1395 | 1h 24m 44s | 100%+5%, 1 compaction |
| SYS-002 | Three CSR-5 rules; PMP checker coverage | PASS | 270/270; tb_csr_tb_pkg 150; 6 new PMP checks fail under the masking mutation | 31m 14s | 25% |
| BP-135 | Clean-up batch; t0_uaon; TD#74 report | PASS | 272/272; sim_tage_manual 472; t0_uaon 17 and 16 events | 1h 23m 31s | 36% |
| BP-136 | Slot-1 TAGE and ITTAGE update checks | PASS | 272/272; 24 path mutations fail; both BP-135 passing mutations now fail | 50m 58s | 35% |
From BP-132 on, runtimes come from the task’s start and end timestamps. For BP-132 the IA corrected the tool’s 26m 10s, which ended the run at the first scheduled wakeup, to 54m 34s. BP-134 left the header status box unticked; I accepted the task.
Design Process Notes
The IA contribution
The IA showed every new check failing before its fix, on a scratch copy of the starting tree or by a mutation of the final one, and said which. It showed that each one-slot target was really built at one slot by removing the macro and watching all 106 stop. It attributed each figure change by removing one item from the final tree. In BP-131 it confirmed that its monitors left every program’s figures unchanged before reporting a count, and its before-and-after checksum of the repository found five files changed outside the task during the run, which it reported and did not revert.
It found defects while doing other work: the T0 decode while fixing the T0 update, the saturated undo-push while fixing the recursion undo, the uBTB duplicate and the placement displacement while counting something else, and three missing privileged-specification rules while building the CSR package. It also corrected the premises it was given. It showed TD#79 and TD#89 and TD#90 were not defects, that the PBMTE rule changed no test, and that four tests already reached the guest-page fault the task asked it to add.
Two tasks, BP-133 and BP-134, edited RTL while their own baseline regression was still running in the same tree. Each stopped the run, restored the files and ran the baseline again, and each said so. From SYS-002 on, every task required the baseline to finish before any edit.
The PA contribution
The planning assistant wrote the eleven task files and drafted the planning document changes after each task. Each task was written as numbered problems with acceptance criteria, and left method and order to the IA.
BP-135’s TD#74 problem was written to report only, so the slot-1 gaps it found could not be closed in that run, and BP-136 was needed. Every task file now carries the rule that a defect found is fixed in the run, with a check that fails first, and marked provisional where no planning document sets the fix.
Several task statements rested on readings the planning assistant had not checked. BP-133 was written from the debt table rows alone, and said so; three of its five items turned out not to need RTL. BP-135’s rows 19.x claim came from reading BP-133’s report and was confirmed by the IA. SYS-002’s prediction that the PBMTE rule would change two tests was taken from SYS-001’s report without reading how the walker uses PBMTE, and was wrong. Its request to check NA4 handling at the checker’s real granule asked for a distinction that does not exist there.
csr_decisions.md was rewritten several times before I accepted it. The
final form states the organization, the implemented bits and the
connection to the RTL, with no narrative, no history, no provenance
identifiers and the references at the end. That is now the form for every
planning document written for the IA.
My contribution
I ruled the slot count values, the one-source macro, and that every target passes at both values. I ruled BP-128’s D1 to be fixed, deferred D2 with the block split, and accepted D3 to D5. I rejected the one-bit history correction at one slot as costing too much and ruled the two-bit correction at either slot count. After BP-131 I ruled that the directed programs cannot decide the slot questions and moved them to compiled benchmarks under TD#179. I created the Performance and Verification Debt table.
I closed several items by ruling in the scrub, including TD#40, 43 and 97, and accepted BP-132’s provisional choices, including keeping the cluster’s boundary port names. I set BP-133’s five items with freedom to find, fix and measure; kept its T0 and RAS fixes; closed TD#79 and had the dead field removed; and closed TD#89 and TD#90 without FTB storage. I ruled the T0 mapping and opened TD#183 and TD#184.
For the CSR definition I ruled that it covers both groups, placed the
custom CSRs at 0x7C0 and 0x7C1, ruled that the extension enables are not a
CSR, that the ports become struct types and that the reset values come
from tb_fe_top. I accepted the pt_ad_h change and added the three
missing rules. I folded TD#74 into BP-135, kept it open after that task,
and ruled that the RTL is correct on the use-alt-on-new-allocation update.
The generalization
Three kinds of result in this range were not what a passing regression implies.
Tests exercised states the design cannot reach. The rows 19.x tests seeded
a primary counter that use-alt-on-new-allocation cannot fire on, and passed
because the update path does not re-check its inputs. pt_ad_h configured
a CSR state no software write can produce. Both gave correct results for
inputs that never occur.
Paths were exercised and not checked. With every ITTAGE slot-1 update
removed, a program’s mispredicts nearly tripled and all 272 targets still
passed. With the RAS undo fix removed, rec_undo’s mispredicts did not
move; only a direct count check saw it. A figure that moves is not a
failure unless a check compares it.
A question was beyond the tests. The block split, the entry format and the training point are performance decisions, and programs written to reach particular cases do not weigh them as a workload would.
In this project a test’s stimulus must be a state the design can produce, a path counts as covered only when removing it fails a check, and a measurement decides a design question only when its workload represents the target.
What comes next
At the close of the range the slot count works at 1 and 2, every front-end technical debt item outside the performance table is closed, and the regression runs 272 targets. The front end takes its configuration and CSR state from packages built from one definition. The slot decisions, the T0 mapping’s effect on SC and the RAS recursion counts on wrong paths wait on compiled benchmarks and a backend model (TD#179, TD#183, TD#184). The rest of session 080 built a front-end demonstration, the subject of the next post.
Technical Debt Referenced
The table below reports status as of the close of session 080. Later experiments outside the range have since changed the state of some items.
| # | Item | Resolution path |
|---|---|---|
| 1 | NUM_PRED_SLOTS = 1 reduction. | CLOSED BP-128. One macro; 106 _1slot targets. |
| 18 | T0 update side undefined. | CLOSED BP-133. Rows 19.1 to 19.8; tests reseeded by BP-135. |
| 49 | TAGE and ITTAGE queue-status pin names. | CLOSED BP-132. Cluster boundary names kept by ruling. |
| 52 | TAGE and ITTAGE arbitration in the top modules. | CLOSED BP-132. tage_arb.sv, ittage_arb.sv. |
| 74 | Slot-1 TAGE and ITTAGE update path coverage. | CLOSED BP-136. 24 path mutations fail. Not covered: ITTAGE uaon[1], slot-1 prediction-side effects. |
| 77 | Absolute paths in prompts. | CLOSED BP-132. Every path is a record of a past run. |
| 78 | RAS undo-pop does not reverse a recursion pop. | CLOSED BP-133. Exercised end to end by rec_undo (BP-134). |
| 79 | RAS commit-stack recursion depth. | CLOSED BP-133, not a defect. commit_rctr removed by BP-134. |
| 82 | bp_history per-branch blocks. | CLOSED BP-129. One loop, fold_all_step. |
| 89 | SC branch_range from the FTB. | CLOSED BP-133 without FTB storage. Formed by ftq_upd_conv. |
| 90 | SC backwards_branch from the FTB. | CLOSED BP-133 without FTB storage. |
| 129 | Top-level Makefile targets. | CLOSED BP-132. all and cov retired; clean reaches every unit. |
| 130 | Regression report gaps. | CLOSED BP-132. |
| 131 | Unexplained decode speed-up. | CLOSED BP-132. ccache in the 5.048 installation. |
| 147, 176 | Out-of-repository tool path. | CLOSED BP-132. RVA_EXT_TOOLS in rtl/Vars.mk. |
| 168 | FTB entry shares one jump field per region. | KEPT by ruling; measured again by BP-131.[F2] |
| 177 | L1I install-time replacement gate untested. | CLOSED BP-132. t_fill_rpl. |
| 178 | CSR definition. | CLOSED SYS-001. csr_decisions.md, csr_pkg, csr_tb_pkg. |
| 179 | One- and two-slot performance: block split, entry shape, region sharing, uBTB duplicate fill, placement, training point. | OPEN, DEFERRED to compiled benchmarks.[F1] |
| 182 | T0 counter decoded as a 3-bit tagged counter. | CLOSED BP-134. {ctr[1], ctr[0], ctr[0]}. |
| 183 | hist mispredicts after the T0 mapping. | OPEN, performance. |
| 184 | RAS recursion counts lowered on wrong paths. | OPEN, performance. Measure first. |
References
[1] RISC-V Privileged Architecture, ISA manual v20260120, https://docs.riscv.org/reference/isa/v20260120/priv/priv-csrs.html, for the CSR fields and write rules csr_decisions.md implements.
[2] Spike RISC-V ISA simulator, riscv/encoding.h and riscv/csrs.cc,
https://github.com/riscv-software-src/riscv-isa-sim, against which the CSR
addresses and bit positions were checked, and whose pmpcfg write handling
the NA4 and R = 0, W = 1 rules follow.
[3] riscv-opcodes, csrs.csv, https://github.com/riscv/riscv-opcodes, for
the CSR addresses.
-
Putting the Loop Predictor in Use and Closing the Predictor Debt (BLOG_bpu_21), for the predictor state this range starts from.
-
Building the Fetch Unit and Running the Front End End to End (BLOG_fetch_4), for the
tb_fe_topprograms, the end-state task method and the L1I replacement reversion that TD#177 records.
Footnotes
[F1] BP-138 (session 081) changed part of TD#179: a jump behind as many conditionals as there are slots now ends the predicted block at the jump, and the next block places it in slot 0. The other items remain open.
[F2] TD#168 was closed by BP-140 (session 081), which gives each block start its own FTB entry.
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.