Skip to content

UCIe · Module 27

Performance Review Checklist

The pre-tapeout performance audit — deriving the ceiling before any measurement, the counter pipeline that attributes every lost cycle to a named cause, why offered traffic and retry bytes silently double a throughput report, torn snapshots that produce impossible ratios, and the credit window that caps a correct design at 40 % of its target.

27.3 asked whether the design can be proven correct. This gate asks whether it can meet its workload — and whether you can explain where every lost cycle went.

1. The One-Sentence Model

Performance closure means every lost service opportunity has an owner: if useful throughput sits below the derived ceiling, the missing cycles must be attributable to a named cause, and "overhead" is not a cause.

The gate's structure follows from that. First derive a ceiling from rate, width, direction and payload fraction (§6) — before any measurement, so the measurement has something to be compared against. Then account for the gap with counters that separate offered from accepted, physical from useful, and stall from idle (§8). A number without an attribution is a rumour.

And the failure this gate exists to catch is a design that is correct, well-verified, and structurally incapable of hitting its target (§17). Not a bug — a ceiling. No amount of arbitration tuning, buffer resizing or RTL optimisation moves it, because the limit is a resource that was sized before anyone wrote code.

2. What This Gate Owns

GateAsksNot this chapter
27.1 — Architecturewhat are we building?the budget's derivation — commissioned there
27.2 — RTLdid we implement it safely?counter code correctness
27.3 — Verificationcan we prove it correct?functional closure
27.4 — Performance (this chapter)can it meet the workload, with margin, explainably?
27.5 — Integrationcan the pieces work together?package seams
27.6 — Debugcan we diagnose it in silicon?first-failure capture, trace

Three boundaries.

27.1 §12 commissioned the budget; this gate audits whether it was met. That division matters: a target invented at this gate is not a target, it is a rationalisation. If no derived budget exists from the architecture gate, this review cannot pass — there is nothing to measure against, and §6 explains why deriving it now is not a repair.

27.2 reviews whether the counter RTL is correct; this gate reviews whether the counters mean what the report claims. A saturating counter implemented perfectly (27.2's concern) can still be counting the wrong event (§10) — and only this gate asks what event it counts.

And this gate does not own silicon observability, which is 27.6's. But every counter this chapter requires must exist in hardware to be readable after tapeout, so §22 hands the list over explicitly rather than assuming someone else asked.

3. Gate Decisions Applied to Performance

27.3 §3 defined PASS / CONDITIONAL PASS / FAIL. Here is what each means when the subject is a number.

DecisionIn this gate
PASStarget met on the defined workload, with documented margin, and the gap to ceiling fully attributed
CONDITIONAL PASStarget met but margin is thin and the sensitivity analysis (§21) bounds the risk, with a named owner and a closure precondition
FAILtarget not demonstrated · gap to ceiling unattributed · measured on a workload that is not the target workload · counters not trusted (§10–§13) · no margin analysis

Three readings.

"Gap unattributed" is a FAIL on its own, even when the target is met. A design hitting its number with 40 % of the ceiling unexplained has 40 % of its behaviour outside anyone's model — and the first workload change moves it in a direction nobody can predict. Meeting a target by accident is not closure.

"Counters not trusted" is the row that turns §10 and §11 into gate-failing findings. If the throughput report is built on a counter that increments on valid rather than on transfer, the number is not low or high — it is meaningless, and every conclusion drawn from it is void.

And there is no CONDITIONAL PASS for "we'll measure it in silicon". That is 27.3 §3's mechanical test failing: 27.5 and the product schedule proceed on the assumption that performance closes, and discovering §17's ceiling after tapeout invalidates all of it. Undemonstrated is FAIL.

4. Statement Versus Evidence

Review statement (worthless)Evidence (accepted)
"bandwidth meets spec"offered / accepted / transmitted / useful-delivered / retired counters captured under the target workload; ceiling derived; bottleneck boundary identified; margin quantified
"the link is 98 % utilised"utilisation split into unique useful bytes, retry bytes and control bytes (§11)
"latency is 200 ns"the distribution: min, median, tail, and the queueing-versus-service split (§14)
"we have enough buffering"occupancy high-water correlated with arrival rate, service rate and stall cause (§18)
"performance is fine under retry"the retry-rate assumption, its source, and measured useful throughput at that rate (§20)
"we hit the target"on which workload, in which window, with what margin (§7, §21)
"we'll tune it later"nothing — §17 cannot be tuned

Three readings.

Every accepted answer names a workload and a window. A throughput number without both is unfalsifiable — peak over a 100-cycle window is a different claim from sustained over a millisecond, and teams present the first while meaning the second.

Row 2 is the most common way a performance report misleads while being arithmetically correct. 98 % utilisation is a true statement about the wire. If 30 % of those bytes are retransmissions, useful throughput is 68 % and the link is more congested than the number suggests, not less (§11).

And row 7 is the reason this gate exists before tapeout rather than after. Some performance limits are not tunable — §17's is a fixed resource multiplied by a fixed latency. The review either finds it now or the product ships with it.

5. Sourcing and Scope

6. Area A — Derive the Ceiling First

Before any measurement is presented, the review must see a ceiling derived from first principles. Otherwise a measured number has nothing to be compared against, and any value can be made to sound acceptable.

#Must be definedEvidence that countsFAIL if
A1signalling rate and widththe numbers, with their sourceassumed
A2direction — per direction or aggregatean explicit statementambiguous (26.4 §16)
A3payload fraction — useful bytes per transferred bytethe framing/overhead derivationraw rate used as useful
A4protocol and framing overheaditemised, with its sourcefolded into a fudge factor
A5the workload the ceiling applies tothe traffic profile (§7)"generic traffic"
A6the observation windowa duration, statedpeak presented as sustained
A7the derivation is reproducible on paperthe arithmetic, showna tool output nobody can re-derive

Worked derivation — illustrative numbers throughout (§5).

Given. A link direction carrying 64-byte payloads, each framed with 8 bytes of header and control. The per-direction transfer capability is 400 GB/s of wire bytes. Target workload: read-dominated, so this direction carries responses.

Step 1 — payload fraction. 64 / (64 + 8) = 0.889.

Step 2 — useful ceiling. 400 GB/s × 0.889 = 355.6 GB/s of useful payload.

Step 3 — express it in operations. 355.6 GB/s ÷ 64 B = 5.56 G operations/s.

Step 4 — state the window. Sustained over ≥ 1 ms, not peak over a burst.

Three readings.

A2 is where budgets are wrong by exactly 2×, and the error is invisible because both readings of an aggregate figure are plausible. The review must see the word "per direction" in writing (26.4 §16) — and a read-dominated workload loads the two directions completely differently from a write-dominated one.

A3's payload fraction is the difference between a wire number and an application number. In the worked example it is 11 % of the ceiling, immediately — and a team that compares measured useful throughput against the raw rate will conclude they are 11 % short before considering anything else.

And A7 exists because a ceiling nobody can re-derive cannot be challenged. If the derivation is a spreadsheet cell, the review cannot audit it. Four steps on a whiteboard can be checked by anyone in the room, which is the whole point of doing it first.

7. Area B — Define the Workload

#Must be trueEvidence that countsFAIL if
B1the target workload is written downthe traffic profile: sizes, mix, burstiness, direction"typical traffic"
B2the read/write mix is statedthe ratio, and which phase it applies tounstated
B3burstiness is characterisedarrival distribution, not just mean ratemean rate only
B4measurements use that workloadthe test configurationmeasured on a different profile
B5phase behaviour is representedper-phase profiles if the workload has phasesone averaged profile
B6worst-case as well as nominalboth measurednominal only

Three readings.

B4 is the most common invalidating finding in a performance review, and it is easy to miss because the number looks good. A design measured on uniform, fully-pipelined, single-size traffic will exceed a target that its real workload misses — and the presentation shows a passing number either way. The review question is "is this the workload from B1?"

B3 matters because burstiness is invisible in a mean. Two workloads with identical average rates stress buffering, arbitration and the outstanding window completely differently — and the bursty one is the one that finds §18's occupancy problems.

And B5 is where averaged profiles hide the real limit (26.4 §16). A workload with a read-heavy phase and a write-heavy phase loads opposite directions in each. A single averaged profile is a workload that never runs, and it is comfortable in both directions while the real phases are not.

8. Area C — The Accounting Pipeline

The instrument that makes §1's attribution possible. Seven service-rate counters read in order, plus five loss-cause counters.

StageCountsThe first stage whose rate falls below target is the bottleneck
offeredwork the source wanted to issuebelow target → the source is the limit
acceptedwork admitted (valid && ready)offered ≫ accepted → downstream is refusing
transmittedphysical units put on the wireincludes retries
retriedretransmitted units§11 — must be separable
deliveredunits received
useful deliveredunique payload bytes, oncethis is the number in the report
retiredsemantically completeddelivered ≫ retired → the consumer is the limit

And the loss-cause counters, which are what turn a gap into an attribution.

CounterAttributes lost cycles to
idle with work availablearbitration or scheduling loss
no-credit cyclesflow control (25.6)
downstream-stall cyclesreceiver backpressure
recovery cyclesfault handling (§20)
stalled on no free IDthe outstanding window (§16–§17)

Three readings.

The gap between offered and accepted and the gap between delivered and retired bracket the design. If offered is already below target, no amount of internal optimisation helps — the workload or the source is the limit, and that is a B-area finding rather than a design one.

"Idle with work available" is the counter most often missing and the one that names arbitration loss. Cycles where the resource was free and work was waiting are pure scheduling loss. Without it, those cycles land in an unattributed remainder and get labelled "overhead" — which §1 rejects.

And "stalled on no free ID" is the only counter that identifies §17's failure, because that failure makes nothing look full: link idle, receiver idle, credits available, source ready and blocked. Every utilisation counter reads low and none of them explains why.

9. Illustrative RTL — the Performance Counter Bank

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE. A synthesisable counter bank implementing §8. The design
// decisions being reviewed are: ACCEPTED events only, useful separated from
// physical, loss causes counted separately, saturating arithmetic, and ONE
// next-state expression per register (27.2 §8).
localparam int CW = 48;
 
logic [CW-1:0] offered_q, accepted_q, transmitted_q, retried_q;
logic [CW-1:0] delivered_q, useful_bytes_q, retry_bytes_q, retired_q;
logic [CW-1:0] idle_with_work_q, no_credit_q, dnstream_stall_q;
logic [CW-1:0] recovery_cyc_q,  no_free_id_q;
logic [15:0]   occupancy_q, occ_high_water_q;
logic          ctr_saturated_q;          // sticky: any counter pinned
 
function automatic logic [CW-1:0] sat_add(logic [CW-1:0] a, logic [CW-1:0] b);
  sat_add = ((a + b) < a) ? {CW{1'b1}} : (a + b);      // saturate, never wrap
endfunction
 
always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n || ctr_clear) begin
    offered_q <= '0; accepted_q <= '0; transmitted_q <= '0; retried_q <= '0;
    delivered_q <= '0; useful_bytes_q <= '0; retry_bytes_q <= '0; retired_q <= '0;
    idle_with_work_q <= '0; no_credit_q <= '0; dnstream_stall_q <= '0;
    recovery_cyc_q <= '0; no_free_id_q <= '0;
    occupancy_q <= '0; occ_high_water_q <= '0; ctr_saturated_q <= 1'b0;
  end else begin
    // ---- service-rate stages. NOTE: 'offered' is the ONLY counter that may
    // increment on a non-accepted event, and it exists precisely so the
    // offered-vs-accepted gap is measurable (§8).
    if (src_valid)                  offered_q     <= sat_add(offered_q,     1);
    if (src_valid && src_ready)     accepted_q    <= sat_add(accepted_q,    1);
    if (tx_fire)                    transmitted_q <= sat_add(transmitted_q, 1);
 
    // A retransmission is transmitted work and NOT useful work (§11).
    if (tx_fire && tx_is_retry) begin
      retried_q     <= sat_add(retried_q,     1);
      retry_bytes_q <= sat_add(retry_bytes_q, CW'(tx_bytes));
    end
 
    if (rx_fire)                    delivered_q <= sat_add(delivered_q, 1);
 
    // USEFUL bytes counted at RETIREMENT of a unique semantic operation, so a
    // replayed delivery cannot inflate it (27.3 §22's mutation target).
    if (retire_fire) begin
      retired_q      <= sat_add(retired_q,      1);
      useful_bytes_q <= sat_add(useful_bytes_q, CW'(retire_bytes));
    end
 
    // ---- loss causes. These are CYCLE counters and must be mutually
    // exclusive by construction, or the attribution double-counts.
    if (!tx_fire) begin
      if      (in_recovery)                 recovery_cyc_q   <= sat_add(recovery_cyc_q,   1);
      else if (!credit_avail)               no_credit_q      <= sat_add(no_credit_q,      1);
      else if (!dnstream_ready)             dnstream_stall_q <= sat_add(dnstream_stall_q, 1);
      else if (!id_avail && work_pending)   no_free_id_q     <= sat_add(no_free_id_q,     1);
      else if (work_pending)                idle_with_work_q <= sat_add(idle_with_work_q, 1);
      // else: genuinely nothing to do — not a loss, and deliberately uncounted.
    end
 
    // ---- occupancy: ONE signed next-state expression (27.2 §8).
    occupancy_q <= occupancy_q + 16'(accept_fire) - 16'(retire_fire);
    if (occupancy_q > occ_high_water_q) occ_high_water_q <= occupancy_q;
 
    if ((transmitted_q == {CW{1'b1}}) || (useful_bytes_q == {CW{1'b1}}))
      ctr_saturated_q <= 1'b1;
  end
end

Architecture. Thirteen counters in two groups — service rates that localise the bottleneck by first divergence, and loss causes that attribute the remaining gap. Together they answer §1.

State. All 48-bit and saturating. occ_high_water_q survives the moment because the pathological occupancy may be transient and an average will hide it.

Event. accepted uses valid && ready; offered deliberately does not — the gap between them is the measurement, and §10 is what happens when someone confuses which is which. useful_bytes increments at retirement, not at delivery, so a replay cannot inflate it.

Contract. The loss-cause counters must be mutually exclusive, which is why they are a priority chain rather than parallel ifs. If a cycle can increment two of them, the attribution sums to more than the gap and the analysis is unusable. The priority order encodes a claim — recovery dominates credit, which dominates backpressure — and that order is a review item, because it decides what a cycle is blamed on.

Failure. The realistic mistakes are all in the event definitions: counting src_valid as throughput (§10), counting retries as useful (§11), and counting idle cycles with no work as loss — the last inflates the apparent loss and sends the analysis chasing cycles the workload never wanted.

DV/debug. The identity that validates the whole bank: transmitted = useful_ops + retried and total_cycles = tx_fire_cycles + Σ(loss causes) + genuine_idle. If those do not close, the counters are wrong and every conclusion drawn from them is void — which is §3's "counters not trusted" FAIL.

10. Wrong RTL — Offered Counted as Throughput

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG. ILLUSTRATIVE. The throughput counter increments on valid. This is the
// same defect as 27.2 §7 with a different consequence: there, a metric was
// inflated; here, the PERFORMANCE SIGNOFF is inflated.
always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n) throughput_ops_q <= '0;
  else if (src_valid) throughput_ops_q <= throughput_ops_q + 48'd1;  // BUG
end

Architecture. One counter, feeding the reported throughput number.

State. throughput_ops_q.

Event. src_validan offering. Under backpressure the source holds valid with the same object, and each held cycle counts again.

Contract. The review consumes this as operations completed. It is cycles of offering.

Failure — the timeline. ready low 50 % of the time, in a simple alternating pattern.

Cyclesrc_validsrc_readyReal transfersCounterReported rate
01001
11112
21013
31124
1000500 transfers10002× actual

The signoff reports exactly twice the achieved throughput, and the error scales with backpressure — so the measurement is most wrong under the load conditions the target was written for.

Root cause. valid means "I have something"; valid && ready means "it moved". One token, and the performance gate is decided on the wrong number.

And the reason it passes review is that the counter is otherwise excellent: 48-bit, saturating, correctly reset, single next-state expression. 27.2 would pass it on code quality. Only this gate asks what event does it count?

Corrected.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// CORRECT. Count the accepted event, and keep 'offered' as a SEPARATE counter
// so the gap between them stays visible (§8) instead of being lost.
always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    offered_ops_q <= '0; accepted_ops_q <= '0;
  end else begin
    if (src_valid)                 offered_ops_q  <= sat_add(offered_ops_q,  1);
    if (src_valid && src_ready)    accepted_ops_q <= sat_add(accepted_ops_q, 1);
  end
end
 
// MANDATORY. English: the accepted count never exceeds the offered count.
// A cheap structural check that catches an accepted counter wired to the
// wrong event, and it holds for every workload.
a_accepted_le_offered: assert property (
  @(posedge clk) disable iff (!rst_n)
    accepted_ops_q <= offered_ops_q
);

DV/debug. The review question is one line: "show me the increment condition for every counter in the report." And the corroboration is arithmetic — if accepted / offered is 1.00 under a workload known to apply backpressure, one of the two counters is wrong.

11. Wrong RTL — Retry Bytes Counted as Useful

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// WRONG. ILLUSTRATIVE. Useful throughput is derived from bytes placed on the
// wire. Correct when nothing is ever retransmitted — which is exactly the
// condition under which nobody notices.
always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n) useful_bytes_q <= '0;
  else if (tx_fire) useful_bytes_q <= useful_bytes_q + CW'(tx_bytes);   // BUG
end

Architecture. One byte counter at the transmit boundary.

State. useful_bytes_q, which is actually physical bytes.

Event. Every transmission, including retransmissions.

Contract. The report presents this as application-visible throughput.

Failure — the arithmetic. A link running at 98 % wire utilisation with a 30 % retransmission rate.

QuantityValueWhat it means
wire utilisation98 %the link is nearly saturated
of which retransmitted30 %the same payloads, again
reported "useful" throughput98 %wrong
actual unique useful throughput≈ 68 %what the application sees
the report's conclusion"link is at capacity; add bandwidth"wrong action
the correct conclusion"fix the retransmission cause"

And the failure inverts the diagnostic signal, which is the truly dangerous part. As link quality degrades, retransmissions rise, and this counter goes up — so worsening conditions make the performance report look better. A metric that improves as the system degrades is worse than no metric.

Root cause. Physical bytes and unique useful bytes are different quantities, and one counter was asked to be both (26.3 §16).

Corrected. §9's bank separates them: retry_bytes_q at transmit when tx_is_retry, and useful_bytes_q at retirement of a unique semantic operation — so a replay cannot contribute to useful throughput by construction.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// MANDATORY. English: physical transmitted units equal unique useful units
// plus retried units. This identity is what makes the utilisation split
// trustworthy; if it does not close, the report is void (§3).
a_transmit_accounting_closes: assert property (
  @(posedge clk) disable iff (!rst_n)
    transmitted_q == (retired_q + retried_q)
);

DV/debug. The review artefact is the three-way split of wire bytes: unique useful, retry, control. A presentation showing one utilisation number has not done this analysis, and "the link is 98 % utilised" should trigger the question immediately (§4 row 2).

12. Area D — Snapshot Atomicity

Counters are read by software. If they are read one at a time while traffic runs, the set describes a state that never existed.

#Must be trueEvidence that countsFAIL if
D1counters are captured atomicallythe snapshot mechanismsequential reads of live counters
D2the snapshot carries an epoch or timestampthe epoch registersamples cannot be correlated
D3a valid bit indicates capture completedthe valid flagtorn reads unnoticed
D4saturation is reported, not silentthe sticky saturation bita pinned counter lies
D5the identity checks of §9 hold on the snapshotthe closure arithmeticthe snapshot is inconsistent

Two readings.

D1's failure produces impossible ratios rather than wrong ones, which is a mercy if anyone checks. Read accepted before a rollover and delivered after, and the arithmetic yields more delivered than accepted — visibly absurd. The dangerous case is a small skew that yields a ratio which is merely wrong and entirely plausible.

And D4 matters because a saturated counter is indistinguishable from a busy one. A 48-bit counter at a few GHz will not saturate in a test; a 32-bit byte counter can saturate in seconds, and the resulting flat line reads as a throughput plateau. A sticky saturation bit costs one flop and prevents a wrong diagnosis.

13. Illustrative RTL — the Snapshot Bank

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE (§9). Live counters are copied into a shadow bank in ONE cycle
// on request, so software reads a consistent set. The epoch lets two snapshots
// be differenced correctly, which is how a RATE is obtained from counters.
localparam int N_CTR = 13;
 
logic [CW-1:0] live_ctr   [N_CTR];   // driven by §9's bank
logic [CW-1:0] snap_ctr   [N_CTR];
logic [31:0]   snap_epoch_q, snap_cycle_q;
logic          snap_valid_q;
logic [31:0]   cycle_q;
 
always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    snap_valid_q <= 1'b0; snap_epoch_q <= '0; snap_cycle_q <= '0; cycle_q <= '0;
    for (int i = 0; i < N_CTR; i++) snap_ctr[i] <= '0;
  end else begin
    cycle_q <= cycle_q + 32'd1;
 
    // ONE cycle, ALL counters. This is what makes the set consistent — a
    // multi-cycle copy reintroduces exactly the tear it exists to prevent.
    if (snap_req) begin
      for (int i = 0; i < N_CTR; i++) snap_ctr[i] <= live_ctr[i];
      snap_cycle_q <= cycle_q;          // the window's end, for rate arithmetic
      snap_epoch_q <= snap_epoch_q + 32'd1;
      snap_valid_q <= 1'b1;
    end
    // Software clears valid AFTER reading the whole bank, so a snapshot taken
    // mid-read is detectable: epoch changed => discard and retry.
    else if (snap_ack) begin
      snap_valid_q <= 1'b0;
    end
  end
end
 
// MANDATORY. English: the snapshot epoch never changes while a snapshot is
// being consumed without software observing it. Software reads epoch, then the
// bank, then epoch again; unequal epochs mean the read was torn (D2).
a_snap_stable_while_valid: assert property (
  @(posedge clk) disable iff (!rst_n)
    (snap_valid_q && !snap_req) |=> $stable(snap_epoch_q)
);

Architecture. A shadow bank copied in one cycle, plus an epoch and a cycle stamp. The cycle stamp is what converts two snapshots into a rate — without it, software has counts and no window.

State. N_CTR shadow registers, an epoch, a cycle stamp, a valid bit.

Event. A single-cycle capture on snap_req. A loop that copies one counter per cycle would reintroduce the tear — the review item is whether the copy is genuinely simultaneous.

Contract. Software's protocol is read epoch → read bank → read epoch, discarding if the epochs differ. That protocol must be documented with the register map, or software will read the bank and trust it.

Failure. Without the cycle stamp, two snapshots taken at unknown times give counts without a rate — and someone will divide by an assumed window and report a number that is off by however much the assumption was wrong.

DV/debug. Run §9's closure identities on the snapshot, not on the live counters (D5). A snapshot that fails transmitted == retired + retried proves the capture is not atomic, regardless of what the RTL looks like.

14. Area E — Latency Is a Distribution

#Must be trueEvidence that countsFAIL if
E1the distribution is reported, not just the meanmin, median, tail bucketsmean only
E2queueing and service latency are separablethe two components measuredone aggregate number
E3loaded latency is measured, not just unloadedmeasurement at target offered rateunloaded only
E4recovery outliers are visibletail bins, or a separate recovery-latency counteroutliers averaged away
E5latency classes are separated where the architecture defines themper-class measurementmixed classes
E6the measurement point is definedthe two events bounding the intervalambiguous endpoints

Three readings.

E1 is the item that decides whether a latency claim means anything. A mean hides the tail completely, and the tail is what a dependent workload experiences (26.3 §10's dependency-depth argument). A system with a 200 ns mean and a 20 µs tail behaves like a 20 µs system for anything that cannot proceed until the operation completes.

E2 separates two problems with opposite fixes. Service latency is the pipeline's inherent cost — reduced by design changes. Queueing latency is congestion — reduced by rate control, more resources, or a better arbiter. An aggregate number cannot tell you which you have, so it cannot tell you what to do.

And E6 is the item that makes two measurements comparable. "Latency" measured from request issue to response retirement is a different number from wire-to-wire, often by a large factor. Two teams reporting "latency" without defining endpoints are reporting different quantities, and the review will compare them anyway.

15. Illustrative RTL — Latency Buckets

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ILLUSTRATIVE (§9). A full histogram is expensive; coarse logarithmic buckets
// capture the DISTRIBUTION SHAPE at a fraction of the cost. The engineering
// content here is the TRADE-OFF, not the code.
localparam int N_BUCKET = 8;                  // <32, <64, <128 ... >=2048 cycles
 
logic [CW-1:0] lat_bucket_q [N_BUCKET];
logic [31:0]   lat_max_q, lat_min_q;
logic [CW-1:0] lat_sum_q;                     // with 'retired' gives the mean
logic [31:0]   start_time_q [N_ID];           // per outstanding operation
 
function automatic int unsigned bucket_of(logic [31:0] cyc);
  // Log2 bucketing: cheap (a priority encoder), and the shape is what matters.
  if      (cyc <   32) bucket_of = 0;
  else if (cyc <   64) bucket_of = 1;
  else if (cyc <  128) bucket_of = 2;
  else if (cyc <  256) bucket_of = 3;
  else if (cyc <  512) bucket_of = 4;
  else if (cyc < 1024) bucket_of = 5;
  else if (cyc < 2048) bucket_of = 6;
  else                 bucket_of = 7;         // the TAIL bucket — E1, E4
endfunction
 
always_ff @(posedge clk or negedge rst_n) begin
  if (!rst_n) begin
    for (int b = 0; b < N_BUCKET; b++) lat_bucket_q[b] <= '0;
    lat_max_q <= '0; lat_min_q <= 32'hFFFF_FFFF; lat_sum_q <= '0;
  end else begin
    if (issue_fire) start_time_q[issue_id] <= cycle_q;
 
    if (retire_fire) begin
      automatic logic [31:0] lat = cycle_q - start_time_q[retire_id];
      lat_bucket_q[bucket_of(lat)] <= sat_add(lat_bucket_q[bucket_of(lat)], 1);
      lat_sum_q <= sat_add(lat_sum_q, CW'(lat));
      if (lat > lat_max_q) lat_max_q <= lat;      // the worst case, kept
      if (lat < lat_min_q) lat_min_q <= lat;      // the unloaded floor
    end
  end
end

Architecture. Eight logarithmic buckets, a running sum, and min/max. Eight counters plus a per-ID timestamp array give the distribution's shape, the mean, and both extremes — enough to answer E1 without a full histogram.

State. The per-ID start_time_q array is the real cost: N_ID × 32 bits. That is the instrumentation trade-off, and the review should see it costed rather than assumed free.

Event. Timestamp at issue, measure at retirement — the semantic completion, not the transport one.

Contract. cycle_q - start_time_q must handle wrap. At 32 bits and a few GHz that is seconds, which is fine for a test and not fine for a long soak. The review item is whether the wrap behaviour was considered or merely survived.

Failure. Two realistic mistakes. A too-coarse top bucket hides the tail's shape — everything over 2048 cycles is one number, so a 3000-cycle tail and a 300 000-cycle tail look identical. And lat_max_q alone, without buckets, cannot distinguish one pathological outlier from a systematically slow class.

DV/debug. lat_min_q is the unloaded floor and lat_bucket_q under load is the queueing story (E2). The difference between them is the queueing component, obtained without a second measurement setup — which is why min is worth its one register.

16. Area F — The Outstanding Window

A path with sufficient bandwidth at every stage still idles if too little is in flight.

required_outstanding ≈ target_rate × round_trip_latency

Units are where this goes wrong. Express the rate in operations per cycle and latency in cycles, and the product is a dimensionless count of operations.

#Must be trueEvidence that countsFAIL if
F1the required window is derivedthe arithmetic, shownnot derived
F2the latency it assumes is stated and owneda number with a source and a nameassumed
F3the provided window ≥ required, with marginthe comparisonprovided < required
F4it was re-derived against silicon latency, not model latencythe re-derivationmodel-only (27.1 §17)
F5stalled on no free ID is measuredthe counter's value under loadthe failure is invisible
F6the window is reached in testthe coverage bin at max (27.3 §17 H2)boundary never exercised

And F5 is the item that makes this findable at all, because §17's failure makes nothing look full.

17. Flagship — a Correct Design That Cannot Reach Its Target

The scenario that defines this gate. Illustrative numbers (§5).

The claim. Target throughput is 1 object per cycle. The RTL is correct, fully verified, and has passed 27.2 and 27.3. Measured throughput is 0.4 objects per cycle. The team's proposal: tune the arbiter.

The derivation the review does, on the whiteboard, in two minutes.

Step 1 — what limits concurrency. The interface has 8 credits. One credit is consumed per object issued and returned when that object's resource is released.

Step 2 — how long a credit is unavailable. Round-trip reuse latency is 20 cycles: issue → traverse → process → release → credit return → available again.

Step 3 — the ceiling. With 8 credits each unavailable for 20 cycles, the sustained issue rate is

8 credits ÷ 20 cycles = 0.4 objects/cycle.

Step 4 — compare. Target 1.0. Ceiling 0.4. The design is at 100 % of its ceiling and 40 % of its target.

Proposed fixEffect on the ceiling
tune the arbiternone — arbitration is not the constraint
deepen internal FIFOsnone — nothing is backing up
raise the clock 10 %none in objects/cycle; the ratio is unchanged
optimise the datapathnone — the datapath is idle 60 % of the time
more credits (8 → 20)ceiling → 1.0
shorter round trip (20 → 8)ceiling → 1.0

Four readings, and this is why the gate exists before tapeout.

Nothing is broken. No bug, no failing assertion, no coverage hole. The design achieves exactly its ceiling — it is a perfect implementation of an insufficient resource allocation.

The signature is that nothing looks full (§8, F5). Link idle 60 % of cycles, receiver idle, internal queues shallow, source ready and blocked. Every utilisation counter reads low, which is why teams reach for the arbiter — it is the only mechanism they can see. stalled on no free credit is the single counter that names it.

Only two things move the ceiling, and both are architecture changes: more credits (buffering at the far end, an interface-visible parameter) or shorter round-trip latency (a pipeline or protocol change). Both are 27.1 decisions, and after tapeout the first is a fixed array and the second is a fixed pipeline.

And the review question that catches it is 27.1 §9 D3, asked months earlier: "what outstanding depth does the target rate require, and what latency does that assume?" Two minutes of arithmetic at the architecture gate; a respin after it. The gate decision here is FAIL — §3's "target not demonstrated", with no CONDITIONAL PASS available because no mitigation exists within the current silicon.

18. Area G — Interpreting Occupancy

High occupancy is ambiguous. It means healthy pipelining or a bottleneck, and the two need opposite responses.

ObservationPlusMeans
high occupancyhigh service ratehealthy — the pipeline is full and working
high occupancylow service rate, downstream stall highdownstream bottleneck
high occupancylow service rate, no-credit highflow control is the limit
low occupancylow service rate, no-free-ID high§17 — the window
low occupancylow service rate, offered lowthe source; a B-area finding
high water mark high, average lowbursty arrivals — B3

Three readings.

Occupancy alone is not evidence — it needs arrival rate, service rate and stall cause beside it to be interpretable. That is why §9's bank pairs occupancy_q with the loss-cause counters rather than reporting it standalone.

Row 6 is the burstiness signal, and it is the reason high-water marks are kept. A high-water mark far above the average means the buffer is sized for bursts, not for the mean — and a workload characterised only by its mean rate (B3) will under-size it.

And rows 2 and 3 look identical in a queue-depth graph. The distinguishing evidence is which loss counter is high, which is the entire argument for counting stall causes separately rather than counting "stalled".

19. Area H — Arbitration, Fairness and Head-of-Line Blocking

#Must be trueEvidence that countsFAIL if
H1traffic mixes are tested, not single classesthe multi-class measurementsingle-class only
H2per-class latency and grant rate are measuredper-class countersaggregate only
H3a bulk class cannot starve a latency-sensitive onethe per-class tail under mixed loaduntested
H4fairness ageing resets on service, not on grantthe RTL (21.5 §29)starvation hidden
H5the fairness bound is derived, not tunedits derivationraised until it stops firing
H6head-of-line blocking is measured where queues are sharedthe blocked-behind counterunmeasured

Three readings.

H1 is the systematic gap. A design measured one traffic class at a time can meet every individual target and fail catastrophically on the mix — which is the only configuration the product ever runs.

H4 is a 27.2-flavoured finding surfacing here because its consequence is a performance one. A grant into a blocked path is not service (21.5 §29). Ageing that resets on grant makes a starving requester look healthy — and it hides starvation more effectively the busier the system gets, which is when starvation matters.

And H5 is the discipline item. A fairness bound raised each time the assertion fires is a bound that cannot detect the bug it exists for (21.6 §29). The bound comes from the architecture's fairness contract, or it is not a bound.

20. Area I — Performance Under Fault and Recovery

#Must be trueEvidence that countsFAIL if
I1the assumed retry/error rate is stated with its sourcethe assumption and its basisassumed zero implicitly
I2useful throughput is measured at that rate, not only at zerothe measurementsteady-state only
I3recovery duration is measuredthe recovery-cycle counter (§8)estimated
I4traffic resumption behaviour is measuredthroughput ramp after recoveryassumed instant
I5the retry-rate sensitivity is characterisedthroughput vs retry rate, several pointsone point
I6a degraded participant's effect is bounded26.3 §14's adaptationone straggler halves the system

Three readings.

I1's implicit-zero assumption is extremely common and rarely written down. A design that meets its target at a 0 % retry rate and has never been measured at 1 % has an unknown margin — and §11's counter defect means the report may not even show the degradation.

I5 matters because the relationship is usually non-linear. Retries consume bandwidth and hold outstanding resources, so they attack both terms of §16's window arithmetic simultaneously. Throughput can fall much faster than the retry rate rises, and one measured point cannot reveal that.

And I6 hands off cleanly to 26.3 §18: in a multi-participant system, an even work distribution turns one degraded participant into a system-wide collapse. The review item is whether any adaptation exists, not whether degradation is prevented.

21. Area J — Margin and Sensitivity

Passing exactly at the requirement is a FAIL in everything but arithmetic.

#Must be trueEvidence that countsFAIL if
J1margin is quantifiedmeasured vs target, as a number"we meet it"
J2margin's assumptions are statedwhich conditions the margin holds underunstated
J3sensitivity is characterisedthe table below, measuredno sensitivity analysis
J4worst-case corner is includedthe corner measurementnominal only
J5the analysis is reproduciblethe configurations and seedsone-off runs

The sensitivity table a review should demand — and each row must be measured, not estimated.

PerturbationQuestion
clock −5 %does the target still hold?
round-trip latency +20 %§16 — does the window still suffice?
workload burstierdoes buffering still absorb it?
retry rate +1 %§20 I5
one participant degraded26.3 §18
a different read/write mix26.4 §16 — the other direction

Three readings.

Row 2 is the sensitivity that most often converts a PASS into a FAIL, because latency is the term that arrives last and worst. Silicon latency exceeding the model's is the normal case (27.1 §17), and it multiplies directly into the required window.

J3 is a FAIL criterion on its own. A design with no sensitivity analysis has a number and no understanding of what preserves it — so the review cannot distinguish comfortable headroom from a result that collapses under a 5 % perturbation.

And this gate deliberately does not prescribe a margin percentage. The right margin depends on how well the workload is known, how much latency uncertainty remains, and how much of the gap to ceiling is attributed. A design with a fully attributed gap and a characterised workload can pass on less margin than one with 40 % unexplained.

22. Area K — Silicon Observability of Performance

Handed to 27.6, but commissioned here — because a counter that does not exist in hardware cannot be read after tapeout.

#Must be trueFAIL if
K1every counter in §8 exists in hardware, not only in the testbenchsimulation-only instrumentation
K2the snapshot mechanism (§13) is in hardwaresoftware reads live counters
K3counters are clearable without disturbing functioncannot re-arm on a live system
K4saturation is reporteda pinned counter reads as a plateau
K5latency buckets (§15) are present or explicitly waived with a reasontail invisible in silicon
K6the register map documents the read protocol (§13)software trusts a torn read

And K1 is the item most often discovered too late. A performance analysis built entirely on testbench probes produces a beautiful pre-silicon story and nothing to measure against in the lab — at which point the first performance question about real silicon has no instrument to answer it.

23. The Performance Gate Checklist

Thirty-six items. The reasoning for each is in the referenced section.

Ceiling and workload

#QuestionFAIL if
1Is a ceiling derived before any measurement is shown?derived after, to match the result
2Is the derivation reproducible on paper?a spreadsheet nobody can audit
3Per direction or aggregate — stated?ambiguous (§6 A2)
4Is the payload fraction accounted separately from raw rate?raw rate compared to useful
5Is the target workload written down?"typical traffic"
6Were measurements taken on that workload?measured on a different profile
7Is burstiness characterised, not just the mean rate?mean only
8Is the observation window stated?peak presented as sustained
9Are per-phase profiles used where the workload has phases?one averaged profile

Counters and trust

#QuestionFAIL if
10Show the increment condition for every counter in the reportany counts valid without ready (§10)
11Are offered and accepted counted separately?one counter for both
12Are retry bytes separable from useful bytes?§11 — utilisation inflated
13Is useful throughput counted at retirement?counted at transmit
14Does transmitted == retired + retried close?identity fails → report void
15Are loss-cause counters mutually exclusive?attribution double-counts
16Is idle-with-work counted?arbitration loss unattributable
17Is stalled-on-no-free-ID/credit counted?§17 invisible
18Are counters saturating, with saturation reported?a pinned counter reads as a plateau

Attribution

#QuestionFAIL if
19Is the entire gap from ceiling to measured attributed?any residual labelled "overhead"
20Which stage's rate falls first?not identified
21Do the loss-cause cycles sum to the gap?they do not close
22Is the bottleneck boundary named, not just the subsystem?vague

Snapshots and latency

#QuestionFAIL if
23Are counters captured atomically?sequential live reads (§12)
24Does the snapshot carry an epoch and cycle stamp?rates cannot be computed
25Do §9's identities hold on the snapshot?capture is not atomic
26Is latency reported as a distribution?mean only
27Are queueing and service latency separable?one aggregate number
28Is latency measured under load?unloaded only
29Are the measurement endpoints defined?ambiguous interval

Window, arbitration, faults, margin

#QuestionFAIL if
30Is the required outstanding window derived and compared to provided?not derived (§16)
31Was it re-derived against silicon latency?model latency only
32Are traffic mixes measured, with per-class latency?single-class only
33Does fairness ageing reset on service, not grant?starvation hidden
34Is the retry-rate assumption stated, and throughput measured at it?implicit zero
35Is margin quantified with its assumptions?"we meet it"
36Is there a sensitivity analysis across §21's rows?none → FAIL

And run items 10, 14, 19, 30 and 36 first. They take under an hour and they decide most gates — a report built on untrusted counters (10, 14), with an unattributed gap (19), no window derivation (30) or no sensitivity (36) cannot pass regardless of the number it shows.

24. How the Performance Review Itself Fails

Failure modeLooks likeWhy it is fatal
reviewing the number, not the countersa passing throughput figure§10/§11 — the number may be meaningless
deriving the ceiling after the measurementeverything is consistentthe ceiling was fitted to the result
accepting "overhead" as an attributiona tidy pie chart with a large slice§1 — the gap has no owner
measuring on a convenient workloada comfortable marginthe product runs a different one
passing at exactly the requirementtarget met§21 — no margin, no sensitivity
deferring to siliconschedule preserved§3 — §17 is not tunable after tapeout

Two readings.

Row 2 is subtle and common. A ceiling derived after the measurement will be constructed, unconsciously, to make the measurement look reasonable. Deriving it first is not a formality — it is what makes the comparison honest, which is why §6 is the first area rather than a supporting one.

And row 6 is the failure that produces §17 in silicon. "We'll characterise it on real hardware" is reasonable-sounding and, for a resource-limited ceiling, catastrophic: the window is a taped-out array and the round trip is a taped-out pipeline. The measurement will be accurate and the finding unactionable.

25. Red Flags

Heard in reviewUsually meansAsk
"the link is 98 % utilised"retry bytes counted as useful"what fraction is retransmission?"
"we hit the target"workload or window undefined"on which workload, over what window?"
"the rest is overhead"the gap has no owner"which counter accounts for those cycles?"
"we'll tune the arbiter"§17 — possibly not the constraint"what is the credit-window ceiling?"
"latency is about 200 ns"mean presented as the story"what is the tail?"
"buffers are big enough"occupancy uninterpreted"high-water, with which stall cause?"
"it's fine at steady state"retry rate assumed zero"measured at what retry rate?"
"we measured it in the testbench"K1 — no silicon instrument"which register reads that in the lab?"
"the counters are in the report"increment conditions unaudited"show me every increment condition"
"we're 2 % over target"no margin, no sensitivity"what happens at latency +20 %?"

And the highest-yield question in the room is the third one. "Which counter accounts for those cycles?" applied to every slice of the gap. A slice with no counter behind it is not an attribution, and §1 says the gate cannot pass on it.

26. Common Misconceptions

"High wire utilisation means high throughput." §11: at 30 % retransmission, 98 % utilisation is 68 % useful — and the metric rises as the link degrades.

"The throughput counter is simple, it can't be wrong." §10: incrementing on valid rather than valid && ready reports under 50 % backpressure, and it is most wrong under the load the target was written for.

"We meet the target, so performance is closed." §3: an unattributed gap is a FAIL even with the target met — 40 % of the behaviour is outside anyone's model.

"Overhead explains the rest." §1: overhead is a label, not a cause. Every lost cycle needs a counter behind it.

"Mean latency is the headline number." §14: a 200 ns mean with a 20 µs tail behaves like a 20 µs system for any dependent workload.

"Low throughput with everything idle means the arbiter." §17: it means a concurrency window. Arbitration tuning, deeper FIFOs and datapath optimisation move the ceiling by zero.

"More credits is always a tuning knob." §17: it is an interface-visible architecture parameter, fixed at tapeout.

"A faster clock fixes throughput." §17: in objects per cycle, a clock change leaves the credit-window ratio unchanged.

"We'll measure it in silicon." §24: accurate, and unactionable for a resource-limited ceiling.

"Passing at the requirement is passing." §21: without margin and sensitivity, you do not know whether a 5 % perturbation removes it.

27. Understanding Check

28. Summary

Seven things.

Derive the ceiling first (§6). Rate, width, direction, payload fraction, workload, window — four steps on a whiteboard, before any measurement, or the comparison is fitted to the result.

Every lost cycle needs an owner (§1, §8). Seven service-rate stages plus five loss causes, mutually exclusive, summing to the gap. "Overhead" is a label, not a cause.

Audit what the counters count (§10–§11). valid instead of valid && ready reports under 50 % backpressure; retry bytes counted as useful make 98 % utilisation mean 68 % — and make the metric rise as the link degrades.

Snapshots must be atomic (§12–§13), with an epoch and a cycle stamp, and §9's identities must close on the snapshot — otherwise the capture is not atomic whatever the RTL looks like.

Latency is a distribution (§14–§15), separable into queueing and service, measured under load, with defined endpoints. A mean hides the tail, and the tail is what dependent work experiences.

Some ceilings are not tunable (§17). 8 credits ÷ 20-cycle round trip = 0.4 objects/cycle, against a target of 1.0, with nothing full and no bug anywhere. Arbitration, FIFOs, datapath and clock move it by zero.

And margin without sensitivity is not margin (§21). Six perturbations, measured — especially latency +20 %, which multiplies straight into the outstanding window.