UCIe · Module 27
Architecture Review Checklist
The pre-RTL gate — which decisions must be settled before anyone writes a line of RTL, what counts as an answer rather than an intention, the nine areas a chiplet architecture review must close, the three unanswered questions that reliably reach silicon, and what it means to fail a gate on purpose.
Module 26 read architectures that shipped. Module 27 is the seven gates a design passes before it becomes one of them — and this is the first, held before any RTL exists.
1. The One-Sentence Model
An architecture review passes when every question whose wrong answer would require an RTL rewrite has a written, owned, testable answer — and it fails when any such question has an intention instead.
The gate exists because of a cost asymmetry. A decision changed now costs a paragraph. The same decision changed after RTL, verification and integration costs a schedule — and after tapeout it may cost a respin (25.10 §5).
And the failure mode this gate exists to catch is not a wrong answer. Wrong answers get found by simulation. Unanswered questions do not — each side of an interface quietly picks a defensible reading, both implementations verify against their own reading, and the disagreement surfaces at integration (26.5 §21). §15 is that failure caught at this gate, in the only place it is cheap.
2. What This Gate Owns — and What It Does Not
Module 27 is seven gates. This is the first, and its scope is strictly the decisions that must exist before RTL.
| Gate | Owns | Not this chapter |
|---|---|---|
| 27.1 — Architecture (this chapter) | decisions whose wrong answer forces an RTL rewrite | — |
| 27.2 — RTL | whether the code implements the decisions correctly | coding structure, resets, CDC in code, lint |
| 27.3 — Verification | whether the DV plan can find the bugs that matter | coverage closure, checker independence |
| 27.4 — Performance | whether measured performance meets the budget | profiling, counter analysis |
| 27.5 — Integration | whether the package-level contracts close | supplier sign-off, bring-up sequencing |
| 27.6 — Debug | triage when a link fails | first-divergence procedure |
| 27.7 — Interview | last-mile revision | — |
Three boundaries worth stating explicitly, because they are where review scope creeps.
This gate does not review code, because there is none. "Is the FIFO deep enough?" is a 27.2 question. "What outstanding depth does the latency budget require, and who owns that number?" is this gate's — and it is the question that makes the FIFO question answerable at all.
This gate does not measure performance; it commissions the budget (§11). You cannot measure what has not been built. You can absolutely decide what number would count as success, and that decision is worthless if it arrives after RTL.
And this gate does not check that assertions pass. It commissions them (§21). An architecture decision that no assertion can check is a decision nobody can be held to — which is a review finding, not a verification finding.
3. What Belongs to This Gate — the Irreversibility Test
Not every decision belongs here. Applying the test is how a review stays finishable.
| Ask | If yes | If no |
|---|---|---|
| Would changing this later force an interface change? | this gate | later gate |
| Would changing it later invalidate verification already written? | this gate | later gate |
| Does another die, team or supplier depend on the answer? | this gate | later gate |
| Does it determine what a legal transaction means? | this gate | later gate |
| Is it a sizing choice inside one module, with a stable interface? | 27.2 | — |
| Is it a coding-style or structure choice? | 27.2 | — |
| Is it a coverage or checker choice? | 27.3 | — |
Three readings.
Row 3 is the highest-yield question in the table. Anything another party depends on is a contract, and contracts are settled here or discovered at integration (25.10 §6). A decision entirely inside one module, behind a stable interface, is genuinely not this gate's business.
Row 4 catches the subtle ones. "Does a completion mean the data is visible, or that it was accepted?" changes what a legal transaction means. It looks like a detail; it is a semantic decision that every consumer encodes, and changing it later invalidates every consumer.
And the anti-pattern this test exists to prevent is the review that reads the whole architecture document. A gate that reviews everything closes nothing. Nine areas, each with a small number of questions whose wrong answers are expensive, is a review that finishes in a day and is worth holding.
4. What Counts as an Answer
This is the part most checklists omit, and it is what separates a gate from a meeting.
| Not an answer | An answer |
|---|---|
| "we'll follow the spec" | the specific clause, and what it leaves to the implementation |
| "it's handled" | the mechanism, and where it is written down |
| "the same as last project" | the decision restated, with what changed |
| "we'll tune it in bring-up" | the range, and what happens at each end of it |
| "obviously it retains" | retains, written down, agreed by the peer's owner |
| "performance should be fine" | a number, its derivation, and the measurement that will confirm it |
| "DV will catch it" | the named test or assertion that would catch it |
Four readings.
Every left-hand phrase is socially an answer and technically nothing. They end conversations, which is exactly why they are dangerous — the question is marked closed and no decision was made.
Row 1 deserves particular care in a standards context, and I have to be honest about my own position here (§5). A specification defines what is required, and it deliberately leaves implementation choices open. "We'll follow the spec" answers conformance; it does not answer which permitted option we chose — and the peer needs the second answer, not the first.
Row 5 is 26.5 §21's hang in embryo. "Obviously it retains" is one team's reading stated with confidence. The other team's "obviously it replays" is equally confident. The word obviously in a review is a signal to slow down, not to move on.
And row 7 inverts responsibility in a way that quietly guarantees the escape. DV finds bugs in implementations of decisions. It does not find missing decisions — there is nothing to compare against, so the checker encodes the same assumption the RTL does (25.8 §11).
5. Sourcing and Scope
6. Area A — Partition and Boundaries
| # | Question | A passing answer contains | Wrong answer costs |
|---|---|---|---|
| A1 | Why is the boundary here and not one block over? | the pressure it serves — node, reticle, power, reuse (25.7 §6) | a re-partition after RTL |
| A2 | What crosses, and with what dependency depth? | traffic classes, and which ones a consumer stalls on (25.7 §10) | latency-sensitive traffic on a bulk path |
| A3 | Is any coherent traffic crossing? | yes/no, and if yes, the ordering and completion guarantees (26.1 §10) | a hang class nobody designed for |
| A4 | Which boundaries do not exist, and why? | the declined cuts, with reasons (26.1 §12) | a missing path found at integration |
| A5 | How many boundary contracts does each die own? | the count, and whether it changes with die count (26.1 §12) | reuse story silently broken |
| A6 | What is the software-visible partition? | one device, or n — and whether that is selectable (26.5 §20) | a driver model change late |
Three readings.
A3 is the question that changes the difficulty class of everything downstream. Bulk data crossing a boundary tolerates loss with a retry. Coherent traffic does not: a dropped request is a stalled core, not a retry (26.1 §10). If A3 is "yes" and nobody said so, the completion, ordering and retention requirements were never derived — and they are all in area E.
A4 asks about absences, which no document naturally contains. 26.1 §12's declined CCD-to-CCD mesh is a real decision with real reasoning behind it. A review that only examines what exists cannot examine what was left out, and a missing path is discovered when someone needs it.
And A6 reaches further than it looks. Whether a package presents as one device or several is a driver and product decision as much as a hardware one, and 26.5 §20 shows two shipping products choosing oppositely. Deciding it late means a driver model change late.
7. Area B — Protocol and Layer Mapping
| # | Question | A passing answer contains | Wrong answer costs |
|---|---|---|---|
| B1 | Which protocol(s) map onto this link, and which revision? | named protocols; the specification revision, recorded | an incompatibility found at interop |
| B2 | Where are the layer responsibilities drawn? | what the adapter owns vs what the protocol layer owns (25.3) | duplicated or absent reliability |
| B3 | Where is reliability provided, and by whom? | one named owner, once (25.10 §7) | two retry layers, or none |
| B4 | If multiple protocols share the link, what arbitrates? | the policy, and its fairness/starvation properties | starvation of one protocol |
| B5 | What must this block NOT assume about the peer? | the explicit list — §8's identity model, §10's retention | assumptions that differ silently |
| B6 | Which permitted options did we choose? | option-by-option, with the clause reference | "we follow the spec" (§4 row 1) |
Three readings.
B3 is the highest-consequence single question in this area. Reliability provided twice is not safer than once — it doubles latency, complicates identity, and creates two different views of what a retransmission means. Reliability provided zero times is a silent data-loss path. Both come from the same failure: nobody was named.
B5 is the question that prevents 26.5 §21. "What must this block not assume about the peer?" forces the assumptions to be spoken. An assumption spoken in a review is a contract; the same assumption unspoken is a defect waiting for integration.
And B6 is where §5's limitation becomes a practical instruction. Specifications leave options open on purpose. The review's job is to record which option was chosen and where the other side agreed — and "we'll follow the spec" is precisely the answer that hides an unmade choice.
8. Area C — Identity and Lifetime
| # | Question | A passing answer contains | Wrong answer costs |
|---|---|---|---|
| C1 | What identifies a transaction semantically? | the field, its width, and its allocation authority | identity derived from transport |
| C2 | Is there a generation or reuse counter? | yes, with width and wrap behaviour (25.5 §11) | stale completion retires a live entry |
| C3 | When may an identifier be reused? | the precise release condition | reuse while live → silent corruption |
| C4 | Does identity survive a transport retry? | yes, stated, with the attempt counter separate | duplicate semantic operations |
| C5 | Does identity survive a route change or failover? | yes, with route held separately + epoch (26.5 §19) | failover causes the hang |
| C6 | How many identifiers exist, and is that the concurrency limit? | the count, tied to area D's budget | the §17 bug |
Three readings.
C1 through C5 are one question asked five ways, and that repetition is deliberate. Semantic identity must be independent of every physical fact — which link, which attempt, which route, which epoch. Every violation in Modules 25 and 26 is a physical fact leaking into a logical identity (26.1 §15, 26.5 §19).
C3 is where the corruption lives. "When may an identifier be reused?" has an obvious answer — when it completes — that is wrong across a reset. 26.5 §9's timeline is exactly this: a die restarts allocation while the peer still holds the old identities.
And C6 is a hinge between two areas. The identifier count is the concurrency limit, whether or not anyone wrote it down as one. If area D's budget needs more outstanding operations than area C provides, the design cannot reach its bandwidth target — and §17 is that mismatch shipping because the two numbers were owned by different people.
9. Area D — Flow Control and Concurrency
| # | Question | A passing answer contains | Wrong answer costs |
|---|---|---|---|
| D1 | What is the flow-control mechanism, per traffic class? | credits or otherwise, and the conservation rule (25.6) | overflow, or permanent stall |
| D2 | What is the credit conservation invariant? | one sentence, checkable (25.6 §10) | slow credit leak → hang at hour 9 |
| D3 | What outstanding depth does the target rate require? | rate × round-trip, with the latency it assumes (26.4 §14) | §17 — the path idles |
| D4 | What latency did that assume, and who owns it? | a number with an owner and a source | budget silently invalid |
| D5 | Can any class starve another? | the fairness bound, or an explicit "no bound" | a starved class at integration |
| D6 | What happens when a credit return is lost? | the recovery mechanism, or the guarantee it cannot be | unrecoverable stall |
Three readings.
D3 and D4 are one question split in two on purpose. The depth follows from the latency, so a depth without its assumed latency is a number nobody can check. D4 asks who owns the latency figure — because when silicon latency differs from the model, the depth must be revisited, and that only happens if someone owns it. §17 is what happens when nobody does.
D2 asks for a checkable sentence, not a description. "Credits are returned when the buffer frees" is a description. "Credits outstanding + credits available = the configured total, always" is an invariant an assertion can carry (25.6 §10) — and §21 commissions it.
And D6 is asked because the answer is usually "that cannot happen". That is a fine answer if it is a guarantee with a mechanism behind it. It is not fine as an assumption — a lost credit return is an unrecoverable, silent stall, and it is worth knowing at this gate whether the design has a defence or a guarantee.
10. Area E — Reset, Recovery and Retention
| # | Question | A passing answer contains | Wrong answer costs |
|---|---|---|---|
| E1 | What reset scopes exist, and who owns each? | the five scopes plus diagnostic clear (26.5 §7) | link reset clears semantic state |
| E2 | What survives a link reset? | the explicit retained/lost table (26.5 §8) | 26.5 §9's corruption |
| E3 | What survives a transport recovery — retained or replayed? | one answer, agreed by BOTH sides | 26.5 §21 — the hang |
| E4 | Does first-fault capture survive every reset short of system? | yes, stated (21.7 §14) | the reset destroys the evidence |
| E5 | Can a die reset alone? Which peers must know? | the recovery domain (26.5 §20) | inconsistent state survives |
| E6 | Are outstanding obligations allowed to block a low-power entry? | yes/no, stated (26.2 §10) | orphaned transaction, silent hang |
Three readings, and E3 is the most important line in this chapter.
E3 is the question that produced 26.5 §21's flagship failure, and it is worth stating why it escapes. It has two defensible answers. Each side picks one, each verifies against its own VIP which encodes its own assumption, and each passes. The disagreement is invisible until the dies meet. No amount of unit testing finds it, because there is nothing locally wrong.
Which makes it the canonical architecture-gate item. It costs one line here. It costs a cross-company debug at integration, with two teams each holding proof their die is correct. §15 shows both readings written out as RTL, which is the most convincing way to see that neither is a bug.
And E4 is the one reviewers skip because it sounds like a debug concern. It is not: if a link reset clears first-fault capture, the reset destroys the evidence for the fault that caused it. You then permanently observe the second failure and never the first (21.7 §14) — and that is an architecture decision, made now, that determines whether silicon is debuggable at all.
11. Area F — Configuration and Ownership
| # | Question | A passing answer contains | Wrong answer costs |
|---|---|---|---|
| F1 | Who is the configuration owner? | exactly one named authority (25.10 §11) | two owners = unspecified system |
| F2 | Is commit atomic across dies? | shadow → prepare → all-ack → swap (26.5 §17) | a split-configuration window |
| F3 | Is there a configuration epoch? | yes, with width and stale-request handling | silent misinterpretation |
| F4 | May a change commit with work outstanding? | no, with the quiesce mechanism named | stale requests serviced under new config |
| F5 | What are the validity rules? | the constraints, checked before prepare (26.3 §11) | an invalid configuration commits |
| F6 | When is a resource published to software? | the full readiness conjunction (26.5 §13) | "fails on first access after boot" |
Three readings.
F1 has exactly one acceptable shape of answer: a name. Two configuration owners is not a race condition — it is an unspecified system, because the resulting interleaving is in nobody's specification and therefore nobody's sign-off covers it.
F4's answer should be "no", and the interesting part is the mechanism. Quiescing is what makes an epoch check an assertion rather than a repair path (26.5 §17). A design that commits without quiescing needs runtime detection of stale requests forever; one that quiesces needs an assertion that never fires.
And F6 is the readiness question, which belongs here rather than in integration because publishing early is an architecture decision about what a resource manager is told. 26.5 §15's intermittent first-access failure is decided at this gate, months before anyone can observe it.
12. Area G — Performance Budget
This gate does not measure performance — 27.4 does. It decides what would count as success, and derives what the architecture must provide.
| # | Question | A passing answer contains | Wrong answer costs |
|---|---|---|---|
| G1 | What is the target rate, and for which workload phase? | a number and the phase it applies to (26.4 §16) | a budget for a workload that never runs |
| G2 | Per direction, or aggregate? | per direction, explicitly (26.4 §16) | one direction starved by 2× |
| G3 | Useful bytes, or bytes on the wire? | useful, with the overhead accounted separately | retries counted as work (26.3 §16) |
| G4 | What is the assumed round-trip latency, and its source? | a number, an owner, and how it will be confirmed | area D's depth is invalid |
| G5 | Which node is expected to bind? | the predicted minimum (26.3 §8) | scaling the wrong resource |
| G6 | What happens when a participant degrades? | the adaptation mechanism (26.3 §14) | one slow die halves the package |
| G7 | Which counters will confirm all of the above? | the named counter set (26.4 §21) | unfalsifiable budget |
Three readings.
G2 and G3 are where budgets are quietly wrong by integer factors. "400 GB/s" without a direction can be off by 2×; without "useful", retries inflate it further (26.3 §16). A budget that does not survive those two clarifying questions was never a budget.
G5 is the question that stops a roadmap from disappointing. Naming the node you expect to bind is a falsifiable prediction (26.3 §10). If the prediction is wrong, you learn it in 27.4 rather than after building more of the wrong resource.
And G7 is what makes this whole area real. A budget with no counter to confirm it cannot be failed, so it will not be. Naming the counters here is what lets 27.4 be a gate rather than an opinion — and it is an architecture decision, because counters have to be designed in.
13. Area H — Observability and DFx
Commissioned here, used by 27.6. Nothing in this area can be added after tapeout.
| # | Question | A passing answer contains | Wrong answer costs |
|---|---|---|---|
| H1 | Is there sticky FIRST-fault capture, not last? | first, with an overflow count (21.7 §14) | the cause is overwritten by the symptom |
| H2 | Can diagnostics be cleared without disturbing function? | yes, separate control (25.10 §14) | cannot re-arm on a live system |
| H3 | Are staged event counters present, and readable in order? | the stage list (25.9 §12) | bisection instead of a register read |
| H4 | Do traces carry semantic ID, generation, config epoch, route epoch? | all four (26.5 §20) | cross-die correlation impossible |
| H5 | Is there a common time base across dies? | timestamp or shared milestone counter | traces cannot be interleaved |
| H6 | Is there a readiness high-water record? | per die (26.5 §14) | bring-up failures cannot name themselves |
| H7 | Are counters saturating, and is saturation reported? | yes (26.4 §12) | a wrapped counter lies |
Three readings.
H1's distinction between first and last is the difference between a debuggable and an undebuggable part. A last-error-wins register reports the final consequence of a cascade (25.9 §14) — almost always a timeout, which tells you nothing.
H5 is skipped most often and is a hard prerequisite for everything else in H. Without a correlated time base, two dies' traces cannot be aligned — and every interesting multi-die failure is a cross-die correlation problem (26.5 §20).
And the whole area shares one property that makes it belong at this gate: none of it can be added later. A missing counter is a respin. The cost of adding every item in this table at design time is negligible; the cost of needing one and not having it is measured in weeks of silicon debug.
14. Area I — Integration and Supplier Contracts
Commissioned here, closed by 27.5. This gate's job is to ensure the contracts exist and are owned.
| # | Question | A passing answer contains | Wrong answer costs |
|---|---|---|---|
| I1 | Is every cross-die contract written, versioned and owned? | the contract matrix (25.10 §7) | integration surprises |
| I2 | Does the peer's capability model state retention across recovery? | E3's answer, from the peer | 26.5 §21 |
| I3 | Does it state who owns reliability? | B3's answer, from the peer | two retry layers, or none |
| I4 | Does it state configuration ownership? | F1's answer, from the peer | two config owners |
| I5 | What is the interop test plan, and against what? | named counterpart or model | conformance without interoperability |
| I6 | Which decisions are irreversible after which milestone? | the milestone map (25.10 §5) | a change proposed after it is possible |
Two readings.
I2 through I4 are deliberately the same questions as E3, B3 and F1 — asked of the peer instead of ourselves. That duplication is the entire point. A contract is not settled when we have an answer; it is settled when both sides have the same answer, in writing. 26.5 §21's two teams each had a confident answer to E3.
And I6 turns the gate into a schedule instrument. Knowing which decisions close at which milestone is what allows a review to say "this must be settled now" with authority rather than as a preference (25.10 §5).
15. What an Unanswered Question Looks Like in RTL
Area E3 asks: does outstanding work survive a transport recovery — retained, or replayed? Here are both teams' RTL. Read them and try to decide which one has the bug.
// DIE A's requester. Reading: recovery is a TRANSPORT event; semantic
// obligations survive it. So the entry is kept and the response is awaited.
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int e = 0; e < N_ENTRY; e++) txn_live_q[e] <= 1'b0;
end else begin
if (accept_fire) txn_live_q[alloc_idx] <= 1'b1;
if (cmpl_fire) txn_live_q[cmpl_idx] <= 1'b0;
// Recovery deliberately does NOT touch txn_live_q. The peer still owes us
// a response, and a transport event cannot discharge a semantic obligation.
end
end// DIE B's completer. Reading: recovery may have lost in-flight state, so the
// safe action is to DISCARD un-started work and let the requester re-issue.
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int w = 0; w < N_WORK; w++) work_valid_q[w] <= 1'b0;
end else begin
if (rx_accept_fire) work_valid_q[rx_idx] <= 1'b1;
if (done_fire) work_valid_q[done_idx] <= 1'b0;
// Recovery discards un-started work. Completing against state that may
// have been corrupted in flight would be worse than making the peer retry.
if (recovery_done)
for (int w = 0; w < N_WORK; w++)
if (!work_started_q[w]) work_valid_q[w] <= 1'b0;
end
endArchitecture. Two blocks, two teams, one interface. Each implements a coherent and defensible policy, and each is accompanied by a clear comment explaining why it is the safe choice.
State. A's live bits and B's valid bits describe the same obligations from two sides, and after a recovery they disagree.
Event. recovery_done. A does nothing. B clears.
Contract. There is no contract — that is the finding. Each block encodes an assumption the specification did not settle, and each team's VIP encodes the same assumption, so both verify clean.
Failure — the timeline. 26.5 §21 has it in full; the short form is: A waits for a response B has discarded; B waits for a re-issue A will never send; the link is healthy and ACTIVE; both dies hang. The mirror-image mismatch is worse — two completions for one operation, and corruption if the entry was reallocated.
Root cause. Neither block is a bug. The defect is an unanswered E3, and it is invisible to every technique that examines one die at a time.
DV/debug. Two counters on each side — obligations_outstanding_at_recovery, reissued_after_recovery, completed_after_recovery — make the disagreement explicit in one read per die. And the test that would have caught it is 25.8 §17's: inject recovery with obligations outstanding, assert exactly one completion per semantic operation. Both teams wrote drained-pipeline recovery tests, which pass on both.
16. Corrected — the Decision, Then the RTL
// CORRECT. ILLUSTRATIVE. E3 answered: obligations SURVIVE transport recovery.
// Both sides now implement one policy, and the policy is CHECKABLE — which is
// what turns a review decision into something a gate can hold someone to.
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int e = 0; e < N_ENTRY; e++) txn_live_q[e] <= 1'b0;
obligations_at_recovery_q <= '0;
end else begin
if (accept_fire) txn_live_q[alloc_idx] <= 1'b1;
// Retire ONLY on a completion matching a live entry AND its generation
// (area C2) — so a stale completion cannot retire a reallocated slot.
if (cmpl_fire && txn_live_q[cmpl_idx] && (txn_gen_q[cmpl_idx] == cmpl_gen))
txn_live_q[cmpl_idx] <= 1'b0;
// Observability commissioned by area H: snapshot what was owed at the
// moment of recovery, on BOTH sides. This one number is what makes a
// retention disagreement a one-read diagnosis instead of a joint debug.
if (recovery_done)
obligations_at_recovery_q <= count_live(txn_live_q);
end
end
// MANDATORY. English: a transport recovery never clears a live semantic
// obligation. This is E3's answer expressed as a property — the form in which
// a decision becomes enforceable rather than merely recorded.
a_recovery_preserves_obligations: assert property (
@(posedge clk) disable iff (!rst_n)
(recovery_done && txn_live_q[chk]) |=> txn_live_q[chk]
);
// MANDATORY. English: each accepted transaction is completed exactly once.
// Catches the mirror-image mismatch, where the peer replays and the requester
// also re-issues, producing two completions for one operation.
a_exactly_one_completion: assert property (
@(posedge clk) disable iff (!rst_n)
(cmpl_fire && (cmpl_id == chk_id)) |=>
!(cmpl_fire && (cmpl_id == chk_id) && (cmpl_gen == $past(cmpl_gen)))
);Architecture. One policy, implemented identically on both sides, plus the assertion that makes it enforceable and the counter that makes it diagnosable.
State. Live bits, generation, and a recovery snapshot. The snapshot exists because area H said so, not because this module needed it — which is how a good architecture gate produces debuggability as a by-product.
Event. Retirement requires both a live entry and a generation match (area C2). Recovery touches neither.
Contract. The decision could have gone the other way. "Discard and replay" is equally implementable — with a different assertion (recovery clears un-started work) and a different requester obligation (re-issue on recovery). What is not acceptable is the two sides choosing separately.
Failure. The residual risk is a policy stated in the architecture document and not propagated to a later supplier or a derivative product. That is exactly what area I2 and version control on the capability model are for (25.10 §7).
DV/debug. These two assertions belong in both dies' environments, phrased identically. An assertion written once and shared between the two teams is a contract with teeth — and it is far cheaper than the joint debug it replaces.
17. Wrong Architecture — a Budget Without Its Concurrency
// WRONG. ILLUSTRATIVE. Area G stated a bandwidth target. Area D never derived
// the outstanding depth it requires, and area C sized the ID table from what
// the early model needed. The parameter is the whole bug.
localparam int N_ID = 8; // BUG: no derivation, no owner, no stated latency
assign can_issue = (id_busy_count_q < N_ID);Architecture. One parameter. There is no code to review here — which is precisely why 27.2 will not catch it. This is an architecture defect that happens to be spelled as a number.
State. A busy count and a comparison. Both correct.
Event. Issue is gated when all IDs are in use.
Contract. Area G promised a rate; area D never converted it into a depth; area C's table was sized against model latency and nobody owned the assumption (D4).
Failure — the arithmetic (26.4 §14). Target 400 GB/s of 64-byte transfers; silicon round trip 500 ns.
Required = (400/64) ops/ns × 500 ns ≈ 3125 outstanding.
Delivered with 8 = 8 / 500 ns × 64 B ≈ 1.02 GB/s.
A factor of about 390 — and every mechanism in the design works perfectly.
Failure — why nothing looks wrong. No queue is full. The link is idle. Memory is idle. The source is ready and stalled. The classic bottleneck signature is something is backed up, and here nothing is (26.4 §15).
Root cause. A concurrency budget was never derived from the bandwidth budget, and the number that resulted was sized against a latency that did not survive to silicon. The gate question that catches it is D3 — and it catches it for the price of one line of arithmetic.
Failure — the timeline. The reason this reaches silicon is that every milestone before silicon confirms it.
| Milestone | Round-trip latency | 8 IDs deliver | Observed | What the team concludes |
|---|---|---|---|---|
| architecture | never derived | — | — | D3 not asked; no number exists to be wrong |
| behavioural model | ~40 ns | 8/40 ns × 64 B = 12.8 GB/s | model is not rate-accurate | nothing to see |
| RTL + fast memory model | ~50 ns | 10.2 GB/s | "close enough at this stage" | defer to emulation |
| emulation, small config | ~120 ns | 4.3 GB/s | scaled down anyway | "it will scale" |
| first silicon | ~500 ns | 1.02 GB/s | 0.3 % of a 400 GB/s target | "the link is broken" |
| bring-up week 1 | — | — | link trains, no errors, nothing full | suspect memory, then software |
| bring-up week 3 | — | — | someone reads cycles_stalled_no_free_id | the parameter |
First divergence. Not a cycle — a milestone. The design was never measured against a rate target at realistic latency, and each stage's number was explicable in that stage's terms. The first unexplainable number is the first silicon number, by which point the ID table is a fixed array in a taped-out part.
Root cause. A concurrency budget was never derived from the bandwidth budget (D3), and the number that resulted was sized against a latency nobody owned (D4). The parameter is downstream of two unasked questions.
And the reason 27.2 cannot save you (27.2 §15): the RTL is correct. localparam int N_ID = 8; and a comparison. An RTL review checks that code implements decisions — it cannot check a decision that was never made. What it can check is A4: a sizing parameter with no derivation comment is a finding on sight, and that is the last net before silicon.
DV/debug. Instrument cycles_stalled_no_free_id as a first-class counter (area H3). It is the only counter that distinguishes this from every other cause of low bandwidth, because it is the only one that reads high while all the utilisation counters read low. In the table above it is the difference between week 3 and week 1.
18. Wrong Architecture — Two Configuration Owners
F1 asks for a name. This is what happens when the honest answer was "both of us, sort of" and nobody wrote it down.
The setup. Die A hosts the platform management function. Die B hosts the accelerator and its local resource manager. Both were specified as able to apply a configuration change — A because it owns the platform view, B because it owns the resource. Neither specification says the other exists.
// WRONG. ILLUSTRATIVE. Each die commits locally when it is ready. Both blocks
// are correct implementations of their own die's specification, and neither
// author had any reason to believe another agent could commit.
// DIE A
always_ff @(posedge clk_a or negedge rst_a_n) begin
if (!rst_a_n) cfg_active_a_q <= CFG_DEFAULT;
else if (platform_change_req) cfg_active_a_q <= platform_cfg; // commits
end
// DIE B
always_ff @(posedge clk_b or negedge rst_b_n) begin
if (!rst_b_n) cfg_active_b_q <= CFG_DEFAULT;
else if (local_change_req) cfg_active_b_q <= local_cfg; // also commits
endArchitecture. Two independent commit paths, no epoch, no prepare phase, no arbitration — because from each die's point of view there is nothing to arbitrate with.
State. Two active-configuration registers that are supposed to describe the same system.
Event. Either die's local change request, at any time, without notifying the other.
Contract. It violates F1 (one owner), F2 (atomic commit), F3 (no epoch) and F4 (no quiesce) — and it violates them by omission rather than by error.
Failure — the timeline. Both dies change configuration in the same window, with traffic in flight.
| Cycle | Die A | Die B | In flight | Observable |
|---|---|---|---|---|
| 0 | cfg = X | cfg = X | 3 transactions under X | consistent |
| 10 | platform requests Y; commits | cfg = X | 3 under X | A is on Y, B is on X |
| 11 | cfg = Y | local requests Z; commits | 3 under X | A on Y, B on Z, traffic assumes X |
| 12 | interprets responses per Y | interprets requests per Z | — | three different configurations live |
| 13–20 | — | — | in-flight work resolves under the wrong map | no error raised anywhere |
| 25 | A's view: request routed per Y | B's view: serviced per Z | — | wrong destination, valid data |
| 40 | — | — | — | corruption, or a lost transaction |
| later | A reports cfg = Y | B reports cfg = Z | — | the two dies disagree, permanently |
First divergence: cycle 10 — the moment A commits without B knowing. Everything after it is consequence.
Root cause. Two configuration owners is not a race condition; it is an unspecified system (27.1 §11 F1). The interleaving of two independent commits is in neither die's specification, so neither die's sign-off covers it, and neither team can be shown to be wrong.
Why it survives standalone verification completely. Each die's environment models the other side as a passive peer that does not initiate configuration changes — because that is what its specification implies. Both dies pass. The system has no owner for the behaviour that fails.
Corrected — and the fix is a review decision before it is RTL.
// CORRECT. ILLUSTRATIVE. F1 answered: die A is the sole configuration owner.
// B REQUESTS; only A commits; the epoch makes any stale interpretation
// detectable rather than silent. The mechanism is 26.5 §17's two-phase commit;
// what this gate contributes is the DECISION that there is one owner.
logic [EPOCH_W-1:0] cfg_epoch_q;
logic [31:0] rejected_local_commit_q;
always_ff @(posedge clk_a or negedge rst_a_n) begin
if (!rst_a_n) begin
cfg_active_a_q <= CFG_DEFAULT;
cfg_epoch_q <= '0;
end else if (commit_fire && all_peers_prepared && (outstanding_total == '0)) begin
cfg_active_a_q <= cfg_shadow_q;
cfg_epoch_q <= cfg_epoch_q + 1'b1;
end
end
// Die B may only REQUEST. A local commit attempt is refused and COUNTED —
// so a supplier who ships a self-committing die is detected, not accommodated.
always_ff @(posedge clk_b or negedge rst_b_n) begin
if (!rst_b_n) rejected_local_commit_q <= '0;
else if (local_commit_attempt)
rejected_local_commit_q <= rejected_local_commit_q + 32'd1;
end
// MANDATORY. English: configuration becomes active only through the single
// configuration owner. Catches a second committer directly — including one
// introduced later by a derivative product or a new supplier.
a_single_config_owner: assert property (
@(posedge clk_a) disable iff (!rst_a_n)
$changed(cfg_active_a_q) |-> (is_config_owner && commit_fire)
);
// MANDATORY. English: no die other than the owner ever drives a commit.
// This is the property that would have failed on cycle 11 of the timeline.
a_no_foreign_commit: assert property (
@(posedge clk_b) disable iff (!rst_b_n)
!local_commit_attempt
);
// MANDATORY. English: the epoch advances only with nothing outstanding, so
// no in-flight work can be interpreted under two configurations (F4).
a_commit_quiesced: assert property (
@(posedge clk_a) disable iff (!rst_a_n)
$changed(cfg_epoch_q) |-> ($past(outstanding_total) == '0)
);Architecture. One committer, one epoch, a quiesce condition, and a counter for the case the decision says cannot happen.
State. The epoch is the field that turns a silent misinterpretation into a detectable one; rejected_local_commit_q is the field that catches a future violation of a decision made today.
Event. Commit requires three conditions together — owner, all peers prepared, nothing outstanding. Any one of them missing reproduces a variant of the timeline.
Contract. a_no_foreign_commit looks trivially true and is the most valuable assertion here. It encodes the decision in the die that is not the owner — so if a derivative product or a new supplier ships a self-committing B, it fails immediately instead of producing §18's timeline in a lab six months later.
Failure. The residual risk is the decision not propagating to a later supplier — which is exactly what area I4 asks about, and why 27.1 §20's capability record returns X_TWO_CONFIG_OWNERS as a design-time function call rather than an integration discovery.
DV/debug. rejected_local_commit_q should be zero forever. Non-zero is not a tuning signal — it is proof that a die is violating the ownership decision, and it names which one.
19. Two Questions, Two Costs
§15 and §18 are the two failures this gate exists for, and they share a shape worth naming.
| §15 — retention unanswered (E3) | §18 — ownership unanswered (F1) | |
|---|---|---|
| the gate question | what survives a recovery? | who commits configuration? |
| number of defensible answers | two | one, but nobody chose |
| each die in isolation | correct | correct |
| each die's VIP | encodes its own assumption | models the peer as passive |
| standalone sign-off | passes | passes |
| failure at integration | hang, or duplicate completion | corruption under split config |
| cost here | one line | one name |
| cost at integration | cross-team debug, both sides "correct" | cross-team debug, neither side wrong |
Three readings.
Row 5 is why these are architecture findings and not verification findings. Both dies pass, and they pass legitimately. There is no test either team could have written against their own die that would fail — 27.1 §4 row 7's "DV will catch it" is structurally false for this entire class.
Row 2 shows the two shapes an unanswered question takes. E3 has two good answers and needs a choice. F1 has one right answer — exactly one owner — and needs a name. A review must recognise both: "which one?" and "who?" are different failures of the same kind.
And rows 7–8 are the gate's whole economic case. One line and one name, versus two cross-team debugs in which nobody can be shown to be wrong. That asymmetry is why an architecture gate is worth a day of senior time, and why holding it after RTL exists (§22) recovers none of the value.
20. What a Passing Review Produces
// ILLUSTRATIVE (§15). A passing architecture review produces a machine-readable
// capability record, not just a document. Every field here is the answer to a
// numbered gate question, and the peer's copy must AGREE (area I).
typedef struct packed {
logic [15:0] contract_version; // I1 — versioned, or it is not a contract
logic [7:0] spec_revision; // B1 — recorded, never assumed
// Area E — the questions that produced §15
logic retains_across_recovery; // E3 — the one-line answer
logic requires_reissue; // E3's complement; must be !retains
logic obligations_block_low_power; // E6
// Area B / C
logic provides_link_retry; // B3 — reliability owner
logic [7:0] sem_id_width; // C1
logic [3:0] generation_width; // C2
// Area D / G
logic [15:0] max_outstanding; // C6 = D3's derived depth
logic [15:0] assumed_rt_latency_ns; // D4 — the assumption, made visible
// Area F
logic is_config_owner; // F1 — exactly one side may set this
logic [7:0] cfg_epoch_width; // F3
} block_capability_t;
// A passing review means this predicate returns COMPATIBLE for the real peer's
// record — checked at design time, not discovered at integration.
function automatic compat_e compatible(block_capability_t us, them);
if (us.spec_revision != them.spec_revision) return X_SPEC_REVISION;
if (us.retains_across_recovery !=
them.retains_across_recovery) return X_RECOVERY_MODEL; // §15
if (us.requires_reissue == us.retains_across_recovery) return X_SELF_INCONSISTENT;
if (us.provides_link_retry && them.provides_link_retry) return X_DOUBLE_RETRY; // B3
if (!us.provides_link_retry && !them.provides_link_retry) return X_NO_RELIABILITY;
if (us.is_config_owner && them.is_config_owner) return X_TWO_CONFIG_OWNERS; // F1
if (!us.is_config_owner && !them.is_config_owner) return X_NO_CONFIG_OWNER;
if (us.max_outstanding == 16'd0) return X_NO_CONCURRENCY_BUDGET;
if (us.assumed_rt_latency_ns == 16'd0) return RISK_NO_LATENCY_BASIS; // §17
return COMPATIBLE;
endfunctionArchitecture. The review's output as data, so compatibility is a function call rather than a conversation.
State. One record per block, versioned. contract_version is what makes a derivative product safe — a block reused with an unversioned contract carries assumptions nobody re-checked.
Event. Evaluated at design time, at supplier onboarding, and again whenever either side revises.
Contract. The symmetric checks are the valuable ones. X_DOUBLE_RETRY and X_NO_RELIABILITY are both failures of B3, and only one of the two is ever noticed informally — everyone spots missing reliability, and nobody spots two layers of it until latency is inexplicably high.
Failure. RISK_NO_LATENCY_BASIS is deliberately a risk rather than an incompatibility. A missing latency assumption does not make two blocks incompatible; it makes the concurrency budget unfounded (§17). A review that cannot distinguish "incompatible" from "unfounded" produces either false alarms or false confidence.
DV/debug. This record belongs in the trace header as well as in the review. When a package misbehaves, the first question is which contract versions are actually present — and that should be readable from silicon rather than reconstructed from documents.
21. Assertions This Gate Commissions
An architecture decision that no assertion can check is a decision nobody can be held to. The gate's output includes the list.
| Decision | Property to commission | Catches |
|---|---|---|
| E3 — retention across recovery | recovery never clears a live obligation (§16) | 26.5 §21's hang |
| C2/C4 — identity and generation | completion requires live entry and generation match | stale completion retiring a live entry |
| C5 — identity survives route change | a live entry's route epoch never changes | 26.5 §19's rejection burst |
| D2 — credit conservation | outstanding + available = configured total | slow credit leak (25.6 §10) |
| F2/F4 — atomic commit | epoch changes only with nothing outstanding | split-configuration window |
| F6 — readiness | never publish before the full conjunction | 26.5 §15's first-access failure |
| B3 — one reliability owner | structural — checked by §20, not by SVA | double retry |
| G6 — degradation adaptation | bounded imbalance while all targets healthy | 26.3 §13's collapse |
Three readings.
Row 7 is included precisely because it has no SVA. "Exactly one side provides reliability" is a structural property of a pair, not a temporal property of a signal. Trying to write it as an assertion produces something vacuous; §20's compatibility predicate is the right instrument. Knowing which decisions are checkable by assertion and which are not is itself a review skill.
Row 4 is the one that fails at hour nine. A credit leak of one per million transactions is invisible in every simulation and fatal in a long soak (25.6 §10). The conservation invariant is cheap and catches it in the first directed test.
And the reason this table belongs to the architecture gate is that it is a commissioning list, not a verification plan. 27.3 owns whether the DV plan is adequate. This gate owns the statement that these particular properties exist because these particular decisions were made — so that if a decision changes, the assertion list changes with it.
22. How the Review Itself Fails
Three failure modes, all common, none of which look like failures at the time.
| Failure mode | What it looks like | Why it is fatal |
|---|---|---|
| the review that reviews everything | a full read-through of the architecture document | runs out of time before the expensive questions (§3) |
| the review that accepts intentions | every item marked closed | §4's left column, all of it |
| the review that has no failure state | approval, always | a gate that cannot fail is not a gate |
And the fourth, which is subtler and worth its own treatment: the review held too late. A review after RTL exists cannot fail an architecture decision, because failing it now costs a rewrite that nobody will authorise. The decisions get ratified rather than reviewed — the meeting happens, the document is signed, and the gate did nothing.
Three readings.
A gate must be able to fail, and failing one must be survivable. If failing is catastrophic, reviewers will not fail it — they will record a concern and approve. The useful form is a conditional pass: the gate closes with named open items, named owners and named dates, which is a real state with real consequences and is not the same as approval.
"Reviewed everything" and "closed the expensive questions" are different achievements, and only the second is worth a day of senior time. §3's irreversibility test is what keeps the agenda short enough to finish.
And the strongest single practice is to require the answer to be written down before the meeting. A review that collects answers live collects the first defensible thing anyone says. A review that examines pre-written answers spends its time on the two or three that are actually contested — which is where the value is.
23. Red Flags
Phrases that should stop a review rather than close an item.
| Phrase | What it usually means | Ask instead |
|---|---|---|
| "obviously it retains" | E3 was never agreed with the peer | "where is their answer written?" |
| "we follow the spec" | a permitted option was never chosen (B6) | "which option, which clause?" |
| "same as last project" | the assumptions were not re-checked | "what changed?" |
| "we'll tune it in bring-up" | no derivation exists (D3, §17) | "what range, and what happens at each end?" |
| "DV will catch it" | there is nothing to compare against | "which test, which assertion?" |
| "that can't happen" | a guarantee or an assumption — unclear which | "guaranteed by what?" |
| "it's just a parameter" | §17 shipped exactly like this | "derived from what, owned by whom?" |
| "the other team handles that" | possibly nobody handles it | "named person, or nobody" |
| "we'll add counters if we need them" | a respin, later (area H) | "which counters, at what cost now?" |
Two readings.
Every phrase here is a conversation-ender, and that is what makes them dangerous. They are socially satisfying and technically empty. A review's most valuable skill is noticing that an item was closed without being answered.
And "that can't happen" deserves the most patience. It is frequently correct — some things really are guaranteed by construction. The follow-up is not skepticism, it is specificity: guaranteed by what? A mechanism is an answer; a belief is not. The difference is exactly 26.5 §21's two teams, both of whom were certain.
24. Common Misconceptions
"An architecture review checks the architecture is good." §1: it checks that every expensive question has a written, owned, testable answer. Quality is judged continuously; this gate closes decisions.
"A wrong answer is the main risk." §1: wrong answers are found by simulation. Unanswered questions are not — both sides pick defensible readings and both verify clean.
"Following the specification is a complete answer." §4, B6: a specification defines what is required and deliberately leaves options open. The peer needs to know which option you chose.
"Sizing is an RTL concern." §17: N_ID = 8 is spelled as a parameter and is an architecture defect — no derivation, no stated latency, no owner. 27.2 will not catch it.
"Performance belongs in the performance review." §12: 27.4 measures; this gate decides what would count as success and derives what the architecture must provide. A budget written after RTL is a description.
"Observability can be added later." §13: none of area H can be added after tapeout. A missing counter is a respin.
"We both need to be able to configure it." §18: two configuration owners is not a race condition — it is an unspecified system. Neither die's sign-off covers the interleaving, so neither team can be shown to be wrong. One owner, by name; everyone else requests.
"Two retry layers are safer than one." B3, §20: they double latency, complicate identity, and create two views of what a retransmission means. X_DOUBLE_RETRY is a failure, exactly like X_NO_RELIABILITY.
"A conditional pass is a failure." §22: a gate that cannot fail is not a gate. A conditional pass with named items, owners and dates is a real state — and it is what makes failing survivable enough to be honest.
"Review everything to be thorough." §3, §22: a review that reviews everything closes nothing. The irreversibility test is what keeps it finishable.
25. Understanding Check
26. Summary
Seven things.
The gate closes decisions, it does not judge quality (§1). Every question whose wrong answer forces an RTL rewrite needs a written, owned, testable answer.
The dangerous failure is the unanswered question, not the wrong one (§1, §15). Wrong answers are found by simulation; unanswered ones are found at integration, after both sides verified their own reading clean.
Nine areas, filtered by irreversibility (§3, §6–§14): partition, protocol mapping, identity, flow control, reset and retention, configuration, performance budget, observability, integration contracts. Everything else belongs to a later gate.
Three questions reliably reach silicon when skipped (§19). E3 — what survives a recovery (§15). D3/D4 — the concurrency the budget requires and who owns its latency assumption (§17). And F1 — who commits configuration (§18). The first two cost a joint debug and 99.7 % of the bandwidth; the third costs corruption that neither die's sign-off covers.
A passing review produces artefacts, not approval (§20–§21): a versioned capability record whose compatibility is a function call, and a commissioned assertion list tied to the decisions that produced it.
And the economic case is the asymmetry (§19): one line and one name, versus two cross-team debugs in which nobody can be shown to be wrong. A gate that cannot fail is not a gate (§22). A conditional pass with named items, owners and dates is a real state — and reviews held after RTL exists ratify rather than review.