Skip to content
VLSI Mentor

CXL · Module 30

Integration Review Checklist

Two correct blocks, one broken interface. Eleven review dimensions — multi-bit domain crossings, reset release order, shared parameters, handshake semantics, byte order, width adaptation, credit ownership, decode overlap, gate quiescence, tie-off defaults and revision skew — each with the contract at risk and the evidence to demand from both teams.

Every review in this module so far has examined one thing: an architecture, an RTL block, a testbench, a coherency protocol, a measurement. This chapter examines the space between two things, and it is the only review where both subjects can pass their own review and the result still fails.

The review question this chapter turns on, asked once per dimension:

Whose assumption is this, and does the other side know about it?

An integration defect is not a bug in either block. It is a contract that was never written down, read differently by two teams who were each internally consistent, and discovered at the point where their work meets.

1. Nobody Owns The Space Between

The structural reason integration is hard is an ownership gap, and it is worth stating plainly before any of the review items.

QuestionOwner
Is block A correct?A's designer, A's DV, A's review
Is block B correct?B's designer, B's DV, B's review
Do A and B agree about the interface?often nobody

Both blocks pass. Both DV environments are green. Neither team is wrong. The defect lives in a shared assumption that neither environment models, because each one models its own side and stubs the other.

That is why every model in this chapter is built twice as two READINGS of a shared contract rather than as a correct build and a broken one. The weak build is not a mistake anybody would defend in isolation — it is a defensible reading of an under-specified contract.

2. How To Use This Chapter

Each of the eleven review dimensions below is a working review item, and every one answers the same eight questions:

FacetWhat it settles
Under reviewthe shared assumption being examined
Contract at riskwhat breaks if the two sides differ
Where it liveswhich side's code, or neither
Evidence to demandwhat the reviewer should ask both teams for
What escapesthe failure that reaches integration
How DV proves itthe stimulus that needs both sides present
Telemetrywhat exposes the disagreement after tapeout
Misleading evidencewhat makes two disagreeing blocks look fine

3. The One-Sentence Model

An integration review is sound when both blocks passed their own reviews, when every multi-bit domain crossing is qualified by a single synchronised signal, when the reset release order is stated and enforced, when every shared parameter is compared at the boundary, when the address map is disjoint, and when every defaulted input publishes the value it is running with — and "both blocks passed" is bit 0.

4. What This Chapter Owns

GroundOwner
Reviewing the architecture before RTL exists30.1
Reviewing the RTL inside one block30.2
Reviewing the environment that judges one block30.3
Reviewing coherency invariants across agents30.4
Reviewing the numbers a design publishes30.5
Reviewing a failing link30.7
Reviewing the boundary between two blocksthis chapter

The boundary with 30.2 is the one worth stating. That chapter reviews a register assigned twice inside one block. This one reviews a register whose meaning is assigned twice, once by each side — and no amount of reading either block finds it.

5. Teaching-Model Boundary And Source Discipline

Every model in this chapter is a teaching model. Each isolates one integration property so it can be examined, mutated and broken on purpose. None is a production CXL controller, a clock-domain-crossing library, or an implementation of any specification flow.

Nothing here states a normative CXL detail. No opcode, packet layout, bit position, field width, response encoding, snoop encoding, retry rule, timeout constant, latency figure, revision number or register definition from the specification appears anywhere in this chapter. The review dimensions — domain crossing, reset order, parameter agreement, handshake semantics — are general integration properties that any multi-block design must satisfy, and they are examined in their general form deliberately, so the technique transfers.

Claim classHow it is marked
General integration reasoningstated plainly
Teaching abstractiondeclared in the model header
Illustrative parameterevery concrete figure in a model or table
Simulator-derived resultquoted from a run and asserted
Derived arithmeticshown with its inputs

One modelling choice needs stating up front. Metastability cannot be simulated, so the domain-crossing model does not try. It models the consequence — a word assembled from two source values when bits resolve at different times — using an explicit skew input on a single clock. That is a teaching abstraction, declared in the model header, and it isolates the property a review can actually act on.

6. Review Item 1 — Does The Synchroniser Cover The Whole Bus?

Under review. Every signal crossing a clock domain.

Contract at risk. That the destination sees a value the source actually held.

Where it lives. The number of synchronisers, against the number of bits.

The failure. A two-flop synchroniser makes one bit safe to sample in a foreign domain. It does nothing for a bus. Each bit resolves independently, so on the cycle the source changes several bits at once the destination can latch a combination that never existed — a value assembled from the old word and the new one.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 1 - the synchroniser protects one bit, and the bus has eight.
//
// A two-flop synchroniser makes ONE bit safe to sample in a foreign clock
// domain. It does nothing for a bus. Each bit resolves independently, so on the
// cycle the source changes several bits at once the destination can latch a
// combination that never existed on the source side - a value assembled from
// the old word and the new one.
//
//   BAD  : one synchroniser per bit on a multi-bit bus
//   GOOD : synchronise a single qualifier, and pass the bus through a
//          structure that is stable while the qualifier crosses
//
// TEACHING MODEL. Both domains are modelled on one clock with an explicit skew
// input, because the point is the ASSEMBLY of a value that never existed, not
// metastability itself - which cannot be simulated. It is not a production CXL
// block and contains no opcode, layout, encoding or timing from any
// specification.
module domain_hop #(parameter int PER_BIT_SYNC = 0) (
  input  logic clk, rst_n,
  input  logic [7:0] src_val,
  input  logic       src_update, skew_bit3, sample_now,
  output logic [7:0] dst_val, stable_val, truth_val,
  output logic       value_existed, hop_ok,
  output logic [7:0] n_samples, n_impossible,
  output logic       cdc_err
);
  logic [7:0] prev_q, cur_q, dst_q, stable_q;
  logic [7:0] assembled;

  // What the source actually held, before and after the update.
  assign truth_val = cur_q;

  // The per-bit crossing: bit 3 lands one cycle late relative to the rest, so
  // the destination assembles a word from two different source values.
  assign assembled = {cur_q[7:4], skew_bit3 ? prev_q[3] : cur_q[3], cur_q[2:0]};

  assign dst_val    = dst_q;
  assign stable_val = stable_q;
  // The word the destination holds is one the source genuinely presented.
  assign value_existed = (dst_q == cur_q) || (dst_q == prev_q);
  assign hop_ok = value_existed;
  // SAFETY VIOLATION: the destination latched a word that never existed on the
  // source side - neither the old value nor the new one.
  assign cdc_err = sample_now && !value_existed;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      prev_q <= 8'd0; cur_q <= 8'd0; dst_q <= 8'd0; stable_q <= 8'd0;
      n_samples <= 8'd0; n_impossible <= 8'd0;
    end else begin
      if (src_update) begin
        prev_q <= cur_q;
        cur_q  <= src_val;
      end
      // The whole review point. Per-bit synchronisers let the skewed bit
      // through on its own; the qualified path holds the whole word.
      dst_q    <= (PER_BIT_SYNC != 0) ? assembled : cur_q;
      stable_q <= cur_q;
      if (sample_now) begin
        n_samples <= n_samples + 8'd1;
        if (!value_existed) n_impossible <= n_impossible + 8'd1;
      end
    end
  end
endmodule

The measurement. The source holds F0, then updates to 0F. One bit arrives a cycle late:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
source F0 then 0F, bit 3 late : qualified=0f per_bit=07

F0 has bit 3 clear and 0F has it set. A destination taking bit 3 from the old word and the rest from the new one assembles 0000_0111 = 07 — a value the source never held in either state. The qualified path delivers the whole new word; the per-bit path delivers a word that never existed.

The run also drives the benign case: with the two source words equal, the skew assembles the same value and nothing is impossible. A skew is only dangerous while two words differ, which is why a slow-changing bus can carry this defect for years.

Evidence to demand. For every crossing, how many bits and how many synchronisers. If the second number is not one, ask what qualifies the bus.

What escapes. A configuration word, an address, a length field or a pointer read as a value that was never written — presenting as data corruption far from the crossing.

How DV proves it. Change several bits in one cycle and skew one of them. A test that changes one bit at a time cannot find this, and one-bit-at-a-time is what a directed test naturally does.

Telemetry. A count of destination values outside the legal set, where one exists. For an arbitrary bus there is none, which is exactly why this must be caught in review.

Misleading evidence. A synchroniser on every bit, which looks more careful than one synchroniser and is the defect.

A block diagram of an eight-bit bus crossing a clock domain as the source changes from F0 to 0F. A per-bit synchroniser lets one late bit through on its own and the destination assembles 07, a value the source never held. A single qualified path delivers the whole new word.source: F0 then0Fseveral bits change atoncea synchroniserper biteach resolves aloneone qualifier,bus heldthe word crosses wholedestination reads07never existeddestination reads0Fa word the source held12

Figure 1 — the per-bit path is the one that looks more careful. Eight synchronisers protect eight bits individually and protect the word not at all. The qualified path has one synchroniser and delivers a value that existed.

7. Review Item 2 — Who Leaves Reset First?

Under review. Every reset in a multi-block design.

Contract at risk. That no interface is used before both ends of it exist.

Where it lives. Nowhere — which is the point. Each block's reset is correct on its own.

The failure. A consumer that leaves reset first will see a producer that is still resetting, and whatever it samples in that window is not a protocol violation by either side — it is a window nobody specified.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 2 - two blocks, two resets, and nobody owns the order.
//
// Each block's reset is correct on its own. What no block owns is the ORDER in
// which they release. A consumer that leaves reset first will see a producer
// that is still resetting, and whatever it samples in that window is not a
// protocol violation by either side - it is a window nobody specified.
//
//   BAD  : two independent resets, each locally correct
//   GOOD : a stated release order, enforced by a sequencer, and a checkable
//          "both out of reset" qualifier that gates the interface
//
// TEACHING MODEL. Sequential.
//   Safety : no interface traffic is honoured while either side is in reset.
module reset_ordering #(parameter int NO_RELEASE_ORDER = 0) (
  input  logic clk, rst_n,
  input  logic release_a, release_b, traffic, report_now,
  output logic out_a, out_b, both_ready, traffic_honoured,
  output logic [7:0] n_traffic, n_honoured, n_unsafe,
  output logic rst_err
);
  logic a_q, b_q;

  assign out_a = a_q;
  assign out_b = b_q;
  // The truth, computed the same way in BOTH builds.
  assign both_ready = a_q && b_q;
  // The whole review point: whether the interface is gated on both sides being
  // out of reset, or only on the local side.
  assign traffic_honoured = (NO_RELEASE_ORDER != 0) ? (traffic && b_q)
                                                    : (traffic && both_ready);
  // SAFETY VIOLATION: traffic was honoured while one side was still resetting.
  assign rst_err = traffic_honoured && !both_ready;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      a_q <= 1'b0; b_q <= 1'b0;
      n_traffic <= 8'd0; n_honoured <= 8'd0; n_unsafe <= 8'd0;
    end else begin
      if (release_a) a_q <= 1'b1;
      if (release_b) b_q <= 1'b1;
      if (traffic) begin
        n_traffic <= n_traffic + 8'd1;
        if (traffic_honoured) n_honoured <= n_honoured + 8'd1;
        if (rst_err)          n_unsafe   <= n_unsafe + 8'd1;
      end
    end
  end
endmodule

The measurement. Block B releases, block A does not, traffic arrives:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
B released, A still in reset : both_gated=0 local_gated=1

The local-gate build honours traffic into a block that is still resetting. It is not wrong about anything it can see: B is out of reset, and B is the block it is part of. The both-sides build refuses, and the run asserts that once A releases, both builds honour the same traffic legitimately.

Evidence to demand. The release order, written down, and the qualifier that enforces it. "They come out of reset together" is a claim about timing that nothing in either block guarantees.

What escapes. A first transaction lost, corrupted or double-counted on every power-up, in a window too narrow to notice and too repeatable to be random.

How DV proves it. Release the two ends in each order and drive traffic in the gap. A single-block environment cannot construct this, which is why it survives both reviews.

Telemetry. A sticky "traffic seen while not both ready" bit. It must read permanently zero, and it costs one flop.

8. Review Item 3 — Do The Two Sides Agree On The Parameter?

Under review. Every parameter, define or constant appearing on both sides of an interface.

Contract at risk. The width, depth or count the interface is built around.

Where it lives. Two instantiations, in two files, usually in two repositories.

The failure. A parameter on both sides of an interface is a shared contract, and nothing in the language enforces that the two instantiations agree. Each block elaborates cleanly, each is internally consistent, and the mismatch is visible only where they meet.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 3 - two blocks built from the same parameter, with different values.
//
// A parameter that appears on both sides of an interface is a shared contract,
// and nothing in the language enforces that the two instantiations agree. Each
// block elaborates cleanly, each is internally consistent, and the mismatch is
// visible only where the two meet - usually as a truncated field or an
// off-by-a-power-of-two address.
//
//   BAD  : two instantiations, two parameter values, no check
//   GOOD : publish each side's value and compare them at the boundary
//
// TEACHING MODEL.
module param_agreement #(parameter int SKIP_THE_COMPARE = 0) (
  input  logic clk, rst_n,
  input  logic       check_now,
  input  logic [7:0] width_a, width_b, payload,
  output logic [7:0] delivered, truncated_by, agreed_width,
  output logic       agreed, reported_ok,
  output logic [7:0] n_checks, n_mismatch,
  output logic       cfg_err
);
  logic [7:0] narrow;

  assign agreed = (width_a == width_b);
  // Whatever the two sides agree on is the width that actually applies: the
  // narrower one, because the wider side's extra bits have nowhere to go.
  assign narrow = (width_a < width_b) ? width_a : width_b;
  assign agreed_width = narrow;
  assign delivered    = (payload > narrow) ? narrow : payload;
  assign truncated_by = (payload > narrow) ? (payload - narrow) : 8'd0;
  // The whole review point.
  assign reported_ok = (SKIP_THE_COMPARE != 0) ? 1'b1 : agreed;
  // SAFETY VIOLATION: the two sides disagree and the boundary reports agreement.
  assign cfg_err = check_now && !agreed && reported_ok;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      n_checks <= 8'd0; n_mismatch <= 8'd0;
    end else if (check_now) begin
      n_checks <= n_checks + 8'd1;
      if (!agreed) n_mismatch <= n_mismatch + 8'd1;
    end
  end
endmodule

The measurement. Side A built for 16, side B for 12, a payload of 14:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
A=16 B=12 payload=14 : agreed=0 delivered=12 truncated=2 reported=1

The narrower side is what actually applies, so 14 is delivered as 12 and two units are silently truncated. Both builds compute the same applied width and the same truncation; only one of them reports that the sides disagree.

The run drives the case where they agree too — and a payload of 14 into an agreed width of 12 still truncates by two. That is a sizing decision, not a mismatch, and keeping the two findings separate is the point of publishing both numbers.

Evidence to demand. Both values, side by side, as numbers. Not the parameter name — the value each side elaborated with.

What escapes. A field truncated at the boundary, or an address wrong by a power of two, presenting as a data bug in whichever block is easier to blame.

Telemetry. Publish each side's value in a readable register and compare them in hardware. A one-bit mismatch flag is the cheapest integration check that exists.

9. Review Item 4 — What Does ready Mean?

Under review. Every two-wire handshake.

Contract at risk. Whether a beat transferred.

Where it lives. Two documents, each internally consistent.

The failure. One side believes ready means "I will take it if you present it". The other believes it means "I have taken it". Both are defensible readings of two wires, and where they meet a beat is dropped every time the consumer deasserts ready after asserting it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 4 - two readings of the same handshake.
//
// One side believes `ready` means "I will take it if you present it". The other
// believes `ready` means "I have taken it". Both are defensible readings of a
// two-wire handshake, both blocks are internally consistent, and where they
// meet a beat is dropped every time the consumer deasserts ready in the cycle
// after asserting it.
//
//   BAD  : each side documents its own reading, neither reads the other's
//   GOOD  : one written rule - a beat transfers when valid AND ready are both
//           high on the same rising edge - and a lost-beat counter that proves it
//
// TEACHING MODEL. Sequential.
//   Safety : every beat the producer counts as sent is a beat the consumer
//            counts as received.
module handshake_reading #(parameter int READY_MEANS_WILLING = 0) (
  input  logic clk, rst_n,
  input  logic valid, ready, report_now,
  output logic producer_sends, consumer_takes,
  output logic [7:0] n_sent, n_taken, n_lost,
  output logic       agreed, hs_err
);
  // The consumer's reading is fixed: a beat transfers on valid AND ready.
  assign consumer_takes = valid && ready;
  // The whole review point: the producer's reading of the same two wires.
  assign producer_sends = (READY_MEANS_WILLING != 0) ? valid : (valid && ready);
  assign agreed = (n_sent == n_taken);
  // SAFETY VIOLATION: the producer believes it sent more than the consumer took.
  assign hs_err = report_now && (n_sent != n_taken);

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      n_sent <= 8'd0; n_taken <= 8'd0; n_lost <= 8'd0;
    end else begin
      if (producer_sends) n_sent  <= n_sent + 8'd1;
      if (consumer_takes) n_taken <= n_taken + 8'd1;
      if (producer_sends && !consumer_takes) n_lost <= n_lost + 8'd1;
    end
  end
endmodule

The measurement. valid held for three cycles with ready high on one:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3 cycles of valid, 1 of ready : strict_sent=1 willing_sent=3 taken=1

One beat transferred and the willing reading counts three sends. Two beats are lost that neither side calls an error — the producer believes it sent them and the consumer never saw them, and there is no counter anywhere that disagrees unless somebody compares the two.

Evidence to demand. One written rule: a beat transfers when valid and ready are both high on the same rising edge. And a lost-beat counter that proves both sides implement it.

What escapes. Silent data loss proportional to how often the consumer toggles ready — which is to say, proportional to load.

How DV proves it. Hold valid and pulse ready for one cycle in three. A test with ready tied high transfers every beat and both readings agree.

Telemetry. Sent and taken, published by both sides. Their difference is the number of lost beats, and either number alone proves nothing.

10. Review Item 5 — Which End Is Bit Zero?

Under review. Every payload crossing a boundary.

Contract at risk. That the bytes arrive in the order they were sent.

Where it lives. A convention, which is exactly the kind of thing two teams settle differently without either noticing.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 5 - the same byte, numbered from the other end.
//
// Bit and byte ordering is a convention, and a convention is exactly the kind
// of thing two teams settle differently without either noticing. Nothing
// elaborates wrongly, no assertion fires, and the payload arrives with its
// bytes in the wrong order - which looks like data corruption rather than an
// integration defect, and is debugged as one.
//
//   BAD  : each side applies its own convention
//   GOOD : one written convention at the boundary, and a known pattern that
//          is asymmetric under reversal so the mistake cannot hide
//
// TEACHING MODEL.
module byte_order #(parameter int REVERSE_AT_BOUNDARY = 0) (
  input  logic clk, rst_n,
  input  logic        transfer, check_now,
  input  logic [15:0] payload,
  output logic [15:0] delivered, expected, reversed,
  output logic        order_ok, is_symmetric,
  output logic [7:0]  n_transfers, n_wrong,
  output logic        order_err
);
  // Byte-swapped form of the same word.
  assign reversed  = {payload[7:0], payload[15:8]};
  // The convention the boundary is specified to use.
  assign expected  = payload;
  // The whole review point.
  assign delivered = (REVERSE_AT_BOUNDARY != 0) ? reversed : payload;
  assign order_ok  = (delivered == expected);
  // A palindromic pattern cannot detect a reversal. Publishing this is the
  // review point about test patterns, not a property of the design.
  assign is_symmetric = (payload[15:8] == payload[7:0]);
  // SAFETY VIOLATION: the delivered word is not the word that was sent.
  assign order_err = check_now && !order_ok;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      n_transfers <= 8'd0; n_wrong <= 8'd0;
    end else if (transfer) begin
      n_transfers <= n_transfers + 8'd1;
      if (!order_ok) n_wrong <= n_wrong + 8'd1;
    end
  end
endmodule

The measurement. A payload of A1B2 across a boundary that reverses:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
payload A1B2 : straight=a1b2 reversed=b2a1 symmetric=0

Nothing elaborates wrongly and no assertion fires. The payload arrives with its bytes swapped, which looks like data corruption rather than an integration defect — and is debugged as one, usually for a while.

The test pattern is part of the review

The run drives C3C3 as well, and the model publishes whether a pattern is palindromic:

A symmetric pattern cannot detect a reversal. C3C3 reversed is C3C3, the reversing build looks entirely correct, and its error count does not move. A test pattern of repeated bytes — which is the easiest pattern to generate — makes this defect invisible.

Evidence to demand. The convention, written once, at the boundary. And the test pattern, which must be asymmetric under reversal.

What escapes. Byte-swapped payloads, diagnosed as corruption.

Telemetry. A known asymmetric pattern in a scratch register, readable from both sides. Reading it back the wrong way round is a one-instruction diagnosis.

11. Review Item 6 — Where Does The Remainder Go?

Under review. Every width converter, packer and unpacker.

Contract at risk. That every beat accepted is eventually emitted.

Where it lives. The end-of-transfer path.

The failure. A narrow-to-wide adapter accumulates beats until it has a full wide word. When a transfer ends on a partial word, that remainder has to go somewhere: flushed with a byte-enable, held for the next transfer, or silently dropped. The third requires no code at all, which is why it is the common one.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 6 - the width converter, and the beat that is left over.
//
// A narrow-to-wide adapter accumulates beats until it has a full wide word.
// When the transfer ends on a partial word, that remainder has to go somewhere:
// flushed with a byte-enable, held for the next transfer, or silently dropped.
// The third is the common one, because it requires no code at all.
//
//   BAD  : emit only on a full word; the remainder is never emitted
//   GOOD : flush the partial word at end-of-transfer, with a valid-byte count
//
// TEACHING MODEL. Sequential.
//   State remembered : the partial word and how many narrow beats it holds.
//   Safety           : every narrow beat accepted is eventually emitted.
module width_adapt #(parameter int DROP_REMAINDER = 0) (
  input  logic clk, rst_n,
  input  logic       beat_valid, end_of_transfer, report_now,
  input  logic [3:0] beat_data,
  output logic [7:0] wide_out,
  output logic [1:0] held_count,
  output logic       wide_valid, flushed, remainder_lost,
  output logic [7:0] n_in, n_out_beats, n_dropped,
  output logic       residue_err
);
  logic [7:0] acc_q;
  logic [1:0] cnt_q;
  logic       full, partial_at_end;

  assign wide_out   = acc_q;
  assign held_count = cnt_q;
  assign full           = beat_valid && (cnt_q == 2'd1);
  assign partial_at_end = end_of_transfer && (cnt_q != 2'd0);
  // The whole review point: what happens to a partial word at the end.
  assign flushed    = (DROP_REMAINDER != 0) ? 1'b0 : partial_at_end;
  assign wide_valid = full || flushed;
  assign remainder_lost = (DROP_REMAINDER != 0) && partial_at_end;
  // SAFETY VIOLATION: narrow beats were accepted and never emitted.
  assign residue_err = report_now && (n_in != (n_out_beats + {6'd0, cnt_q}));

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      acc_q <= 8'd0; cnt_q <= 2'd0;
      n_in <= 8'd0; n_out_beats <= 8'd0; n_dropped <= 8'd0;
    end else begin
      if (beat_valid) begin
        n_in  <= n_in + 8'd1;
        acc_q <= {acc_q[3:0], beat_data};
        cnt_q <= (cnt_q == 2'd1) ? 2'd0 : (cnt_q + 2'd1);
      end
      // Each emitted wide word carries two narrow beats; a flush carries the
      // one that is held.
      if (full)                 n_out_beats <= n_out_beats + 8'd2;
      else if (flushed)         n_out_beats <= n_out_beats + {6'd0, cnt_q};
      if (end_of_transfer) begin
        if (remainder_lost) n_dropped <= n_dropped + {6'd0, cnt_q};
        cnt_q <= 2'd0;
      end
    end
  end
endmodule

The measurement. Three narrow beats into a two-to-one adapter:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3 beats in : flushing_flush=1 dropping_lost=1

Three in and two out. The flushing build emits the remainder and the conservation equation holds — three beats in, three accounted for. The dropping build emits two, loses one, and nothing reports it.

The run drives the even case too: four beats make two whole words, nothing is held, and the dropping build loses nothing. A transfer length that happens to be even is the case that hides this, and round numbers are what directed tests use.

Evidence to demand. What happens to a partial word at end-of-transfer, in one sentence, plus the conservation equation: beats in equals beats out plus beats held.

What escapes. The tail of every odd-length transfer, silently.

How DV proves it. An odd-length transfer, and a continuous conservation check. Even lengths cannot find it.

Telemetry. Beats in, beats out, beats held — and the equation evaluated in hardware.

12. Review Item 7 — Who Initialises The Credits?

Under review. Every credit-based or token-based interface.

Contract at risk. That advertised capacity matches real capacity.

Where it lives. The reset behaviour of two blocks, neither of which can see the other's.

The failure. A credit interface needs somebody to load the initial count, and exactly one somebody. If both sides do it, capacity is advertised twice and the producer sends more than the queue can hold. If neither does, the producer holds zero credits and the link never starts — which presents as a dead interface rather than as an integration defect.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 7 - who initialises the credits?
//
// A credit-based interface needs somebody to load the initial credit count at
// reset, and exactly one somebody. If both sides do it, the consumer's capacity
// is advertised twice and the producer sends more than the queue can hold. If
// neither does, the producer holds zero credits and the link never starts -
// which presents as a dead interface rather than as an integration defect.
//
//   BAD  : "the other side initialises the credits"
//   GOOD : one named owner, and a published credit count both sides can read
//
// TEACHING MODEL. Sequential.
//   Safety   : advertised credits never exceed the consumer's real capacity.
//   Liveness : the link starts - ASSUMING somebody initialises.
module credit_owner #(parameter int BOTH_INITIALISE = 0) (
  input  logic clk, rst_n,
  input  logic       init_a, init_b, send, credit_return, report_now,
  input  logic [7:0] capacity,
  output logic [7:0] credits, n_sends, n_refused, n_overrun,
  output logic       can_send, overrun, link_dead,
  output logic       credit_err
);
  logic [7:0] cr_q;
  logic       loaded_q;

  assign credits  = cr_q;
  assign can_send = send && (cr_q != 8'd0);
  // The consumer cannot hold more than its capacity, whatever was advertised.
  assign overrun   = (cr_q > capacity);
  assign link_dead = loaded_q && (cr_q == 8'd0) && (capacity != 8'd0);
  // SAFETY VIOLATION: more credits are outstanding than the consumer can hold.
  assign credit_err = report_now && overrun;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      cr_q <= 8'd0; loaded_q <= 1'b0;
      n_sends <= 8'd0; n_refused <= 8'd0; n_overrun <= 8'd0;
    end else begin
      // The whole review point, stated explicitly. An initialisation LOADS the
      // capacity, so a re-initialisation after a retrain reloads rather than
      // accumulating. The two-owner defect is that the second owner ADDS its
      // advertisement on top of the first one's.
      if (init_a) begin cr_q <= capacity; loaded_q <= 1'b1; end
      else if (init_b && (BOTH_INITIALISE != 0)) begin
        cr_q <= cr_q + capacity; loaded_q <= 1'b1;
      end else if (init_b && (BOTH_INITIALISE == 0)) begin
        loaded_q <= 1'b1;   // the non-owner acknowledges and loads nothing
      end else begin
        case ({can_send, credit_return})
          2'b10:   cr_q <= cr_q - 8'd1;
          2'b01:   cr_q <= (cr_q == 8'hFF) ? 8'hFF : (cr_q + 8'd1);
          default: cr_q <= cr_q;
        endcase
      end
      if (send && (cr_q != 8'd0)) n_sends   <= n_sends + 8'd1;
      if (send && (cr_q == 8'd0)) n_refused <= n_refused + 8'd1;
      if (overrun)                n_overrun <= n_overrun + 8'd1;
    end
  end
endmodule

The measurement. A consumer capacity of 4, with both sides initialising:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
capacity 4, both sides init : one_owner=4 two_owners=8

Four more beats can be sent than there is anywhere to put them. The single-owner build advertises 4 and stays within capacity; the two-owner build advertises 8 and is in overrun from the moment it starts.

The model carried an unstated contract of its own

Writing this model surfaced the chapter's own subject one level down. cr_q <= cr_q + capacity and cr_q <= capacity are identical from reset, because the counter is zero there. They differ on a re-initialisation — a retrain, a soft reset, a renegotiation — and the model had never said which it meant.

It now says. An initialisation loads the capacity, so a retrain reloads rather than accumulating; the two-owner defect is that the second owner adds on top of the first. The run drives a double initialisation and asserts the count stays at 4.

Evidence to demand. The named owner, and what a re-initialisation does to an existing count. Two questions, and the second one is the one nobody asks.

What escapes. A queue overrun under load, or a link that never starts — and the second is diagnosed as a bring-up problem for days.

Telemetry. Advertised credits and real capacity, both readable. A dead-link bit for the case where nobody loaded anything.

13. Review Item 8 — Do Two Decoders Claim The Same Address?

Under review. The address map.

Contract at risk. That one request reaches one target.

Where it lives. Two decoders, each correct against its own region.

The failure. Where regions overlap, both decoders assert, two targets respond to one request, and the requester gets whichever answer arrives first — stable in simulation and dependent on routing in silicon.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 8 - two decoders that both claim the same address.
//
// Each block's address decode is correct against its own region. What no block
// owns is the question of whether the regions overlap. Where they do, both
// decoders assert, two targets respond to one request, and the requester gets
// whichever answer arrives first - which is stable in simulation and depends on
// routing in silicon.
//
//   BAD  : each block decodes its own region
//   GOOD : one map, an overlap check across every pair, and a
//          multiple-hit detector that must read permanently zero
//
// TEACHING MODEL.
module decode_overlap #(parameter int NO_OVERLAP_CHECK = 0) (
  input  logic clk, rst_n,
  input  logic       request, report_now,
  input  logic [7:0] addr, a_lo, a_hi, b_lo, b_hi,
  output logic       hit_a, hit_b, multiple_hit, decode_ok,
  output logic [7:0] n_requests, n_multiple, n_unclaimed,
  output logic       decode_err
);
  logic regions_overlap;

  assign hit_a = request && (addr >= a_lo) && (addr <= a_hi);
  assign hit_b = request && (addr >= b_lo) && (addr <= b_hi);
  assign multiple_hit = hit_a && hit_b;
  // Whether the MAP itself is malformed, independent of any one request.
  assign regions_overlap = (a_lo <= b_hi) && (b_lo <= a_hi);
  // The whole review point: whether the boundary reports a malformed map.
  assign decode_ok = (NO_OVERLAP_CHECK != 0) ? 1'b1 : !regions_overlap;
  // SAFETY VIOLATION: two targets claimed one address and the map reported fine.
  assign decode_err = report_now && multiple_hit && decode_ok;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      n_requests <= 8'd0; n_multiple <= 8'd0; n_unclaimed <= 8'd0;
    end else if (request) begin
      n_requests <= n_requests + 8'd1;
      if (multiple_hit)        n_multiple  <= n_multiple + 8'd1;
      if (!hit_a && !hit_b)    n_unclaimed <= n_unclaimed + 8'd1;
    end
  end
endmodule

The measurement. Region A covers 0x100x3F and region B covers 0x300x5F:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
A=10..3F B=30..5F addr=38 : hit_a=1 hit_b=1 checked_ok=0 unchecked_ok=1

Both decoders claim 0x38. Both builds detect the multiple hit; only one of them reports that the map itself is malformed, which is the finding that fixes every address in the overlap rather than the one that was tested.

The run drives two boundary cases the campaign demanded. Regions that are merely adjacent — 0x2F and 0x30 — do not overlap and both builds agree. Regions that touch at exactly one address do overlap, and that single-address case is the one a < instead of a <= gets wrong.

Evidence to demand. The full map, and an overlap check across every pair — not a spot check on the addresses somebody happened to test.

What escapes. Two targets answering one request, with the winner decided by physical routing.

Telemetry. A multiple-hit counter. Permanently zero, and it costs an AND gate per decoder pair.

14. Review Item 9 — Is It Quiescent, Or Just Quiet?

Under review. Every clock gate, power gate and low-power entry.

Contract at risk. That responses still owed will arrive.

Where it lives. The gate decision, which is almost never made by the block being gated.

The failure. A gate applied while responses are outstanding does not corrupt anything. It simply stops them arriving, and the requester waits for a response from a block that is no longer clocked. The symptom is a hang and the cause is in a different file.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 9 - the block that was clock-gated with work still in it.
//
// Power management is an integration concern because the block being gated is
// rarely the block that decides to gate it. A gate applied while responses are
// still outstanding does not corrupt anything: it simply stops them arriving,
// and the requester waits for a response from a block that is no longer
// clocked. The symptom is a hang, and the cause is in a different file.
//
//   BAD  : gate on an idle signal that means "no new requests"
//   GOOD : gate on quiescence - no new requests AND nothing outstanding - and
//          publish the outstanding count so the gate decision is reviewable
//
// TEACHING MODEL. Sequential.
//   Safety : the block is never gated with work outstanding.
module gate_quiescence #(parameter int GATE_ON_IDLE_ONLY = 0) (
  input  logic clk, rst_n,
  input  logic issue, complete, no_new_requests, gate_req,
  output logic [7:0] outstanding, n_gates, n_orphaned,
  output logic       quiescent, gated, orphan_risk,
  output logic       gate_err
);
  logic [7:0] out_q;
  logic       gated_q;

  assign outstanding = out_q;
  // The truth, computed the same way in BOTH builds.
  assign quiescent = no_new_requests && (out_q == 8'd0);
  // The whole review point: what the gate decision is actually gated on.
  assign gated = (GATE_ON_IDLE_ONLY != 0) ? (gate_req && no_new_requests)
                                          : (gate_req && quiescent);
  assign orphan_risk = gated && (out_q != 8'd0);
  // SAFETY VIOLATION: the block was gated with responses still owed.
  assign gate_err = orphan_risk;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      out_q <= 8'd0; gated_q <= 1'b0; n_gates <= 8'd0; n_orphaned <= 8'd0;
    end else begin
      // One assignment to the outstanding counter, computed from both events.
      case ({issue, complete})
        2'b10:   out_q <= out_q + 8'd1;
        2'b01:   out_q <= (out_q == 8'd0) ? 8'd0 : (out_q - 8'd1);
        default: out_q <= out_q;
      endcase
      gated_q <= gated;
      if (gated && !gated_q) n_gates <= n_gates + 8'd1;
      if (orphan_risk)       n_orphaned <= n_orphaned + 8'd1;
    end
  end
endmodule

The measurement. Three requests issued, one completed, the requester stops issuing:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2 outstanding, no new requests : quiescence_gate=0 idle_gate=1

"No new requests" is true and the block is not idle. The idle-gated build gates and orphans two responses; the quiescence-gated build refuses until the outstanding count reaches zero, and the run asserts that it then gates legitimately.

Evidence to demand. The gate condition, in full. "Idle" is a word; the outstanding count is a number, and the second one is the condition.

What escapes. A hang after a low-power entry, reproducible only under the traffic pattern that leaves work outstanding at the idle threshold.

Telemetry. The outstanding count at the moment of gating. Permanently zero, and it makes the gate decision reviewable after the fact.

15. Review Item 10 — What Is That Unconnected Port Doing?

Under review. Every input with a default, and every port left unconnected at integration.

Contract at risk. That the block is running the configuration somebody chose.

Where it lives. A default written by the module's author, applied by the integrator's silence.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 10 - the port nobody connected, and the default that changed the design.
//
// An input left unconnected takes its default. A default is a decision, and it
// is usually made by whoever wrote the module rather than by whoever
// instantiated it. The integration is silent: the design elaborates, simulates
// and synthesises, and behaves as configured by a value nobody chose.
//
//   BAD  : an input with a default, left unconnected at integration
//   GOOD : publish the value the block is actually running with, and check it
//          against the value the integrator intended
//
// TEACHING MODEL.
module tie_off_default #(parameter int TRUST_THE_DEFAULT = 0) (
  input  logic clk, rst_n,
  input  logic       connected, check_now,
  input  logic [7:0] intended_mode, wired_mode, default_mode,
  output logic [7:0] effective_mode, reported_mode,
  output logic       agreed, observable,
  output logic [7:0] n_checks, n_silent,
  output logic       tie_err
);
  // What the block is actually running with.
  assign effective_mode = connected ? wired_mode : default_mode;
  // The whole review point: whether the block publishes it.
  assign reported_mode = (TRUST_THE_DEFAULT != 0) ? intended_mode : effective_mode;
  assign observable = (reported_mode == effective_mode);
  assign agreed     = (effective_mode == intended_mode);
  // SAFETY-OF-EVIDENCE VIOLATION: the block is running one configuration and
  // reporting another, so no integrator can tell.
  assign tie_err = check_now && !agreed && !observable;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      n_checks <= 8'd0; n_silent <= 8'd0;
    end else if (check_now) begin
      n_checks <= n_checks + 8'd1;
      if (!agreed && !observable) n_silent <= n_silent + 8'd1;
    end
  end
endmodule

The measurement. The integrator intended mode 3 and left the port unconnected:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
intended=3 default=1 unconnected : effective=1 published=1 echoed=3

Both builds run mode 1. The behaviour is identical; the difference is what the block says it is running. The publishing build reports 1 and the mismatch is visible to anybody who looks; the echoing build reports 3 — the intent — and no integrator can tell.

The integration is silent. The design elaborates, simulates and synthesises, and behaves as configured by a value nobody chose.

Evidence to demand. For every defaulted input, the value the block is actually running with, readable at runtime. Not the default in the source — the effective value in the instance.

What escapes. A fleet running a configuration nobody selected, where a bug that reproduces on one build and not another cannot be localised because the build cannot be read back.

Telemetry. A configuration-report register carrying every effective value. This item is telemetry, which is why it is the cheapest of the eleven to fix and among the most often missing.

16. Review Item 11 — Was The Peer Upgraded?

Under review. Every interface whose two ends can ship on different schedules.

Contract at risk. That a feature used is a feature the peer supports.

Where it lives. The negotiation, or its absence.

The failure. A block is upgraded and its neighbour is not. The interface is backward-compatible, which is true and is not the same as saying nothing changes: the newer side must detect the older peer and fall back, and a fallback that is not negotiated is a fallback that does not happen.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 11 - two revisions of one interface, integrated together.
//
// A block is upgraded and its neighbour is not. The interface is
// backward-compatible, which is true and is not the same as saying nothing
// changes: the newer side must DETECT the older peer and fall back, and a
// fallback that is not negotiated is a fallback that does not happen.
//
//   BAD  : assume the peer is the same revision
//   GOOD : exchange revisions, operate at the minimum, and publish the
//          negotiated revision so the fallback is visible
//
// TEACHING MODEL. Revision numbers here are illustrative integers, not CXL
// specification revisions.
module revision_skew #(parameter int SKIP_NEGOTIATION = 0) (
  input  logic clk, rst_n,
  input  logic       negotiate, use_feature, report_now,
  input  logic [7:0] rev_a, rev_b, feature_needs_rev,
  output logic [7:0] operating_rev, n_negotiations, n_unsupported,
  output logic       compatible, feature_ok, degraded,
  output logic       skew_err
);
  logic [7:0] lower;

  assign lower = (rev_a < rev_b) ? rev_a : rev_b;
  // The whole review point: the revision the link actually operates at.
  assign operating_rev = (SKIP_NEGOTIATION != 0) ? rev_a : lower;
  assign degraded   = (lower < rev_a) || (lower < rev_b);
  assign compatible = (operating_rev >= feature_needs_rev);
  assign feature_ok = use_feature && compatible;
  // SAFETY VIOLATION: a feature was used that the peer's revision cannot
  // support, because the link never negotiated down to it.
  assign skew_err = use_feature && (lower < feature_needs_rev) && feature_ok;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      n_negotiations <= 8'd0; n_unsupported <= 8'd0;
    end else begin
      if (negotiate) n_negotiations <= n_negotiations + 8'd1;
      if (skew_err)  n_unsupported  <= n_unsupported + 8'd1;
    end
  end
endmodule

The measurement. Side A at revision 3, side B at revision 1, a feature needing revision 2:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
A=3 B=1 feature needs 2 : negotiated_ok=0 unnegotiated_ok=1

Negotiating down operates at revision 1 and refuses the feature. Skipping negotiation operates at A's own 3, believes the feature is available, and uses something the peer cannot support. The run also drives the boundary — a peer exactly at the revision the feature requires, which is compatible — and the matched case where both sides agree and the feature is legitimately available.

Evidence to demand. The negotiated revision, published, and what the newer side does when it is lower than its own. "It is backward compatible" describes the specification, not the implementation.

What escapes. A feature used against a peer that cannot honour it, on exactly the mixed-revision systems that upgrades create.

Telemetry. The operating revision, readable from both ends, and a degraded-link flag. A fleet with mixed revisions and no way to read which is a fleet nobody can partition.

A waveform over eight cycles of two blocks leaving reset. Block B releases first and block A remains in reset for three more cycles. Traffic arriving in that window is honoured by a build gated on the local side alone and refused by a build gated on both sides.B out of resetB out of resettraffic arrivestraffic arrivesA out of resetA out of resetclkout_bout_atrafficlocal_okboth_okt0t1t2t3t4t5t6t7
Figure 2 — a teaching waveform, not normative CXL timing. The out_b row rises three cycles before out_a, which is the window nobody specified. At cycle 3 traffic arrives into it: the local_ok row goes high because block B is out of reset and block B is all the local gate can see, and the both_ok row stays low because block A is not. At cycle 6 the same traffic is honoured by both, legitimately. Nothing in either block is wrong in cycle 3 — the gap between the two reset releases belongs to neither of them.

17. The Review Assembled

Eleven dimensions, one summary — and the trap this module has found at every level, in its integration form.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// RTL 12 - an integration review assembled. Eleven review dimensions, one
// summary. "Both blocks passed their own review" is bit 0: two correct blocks,
// and one sixth of an integration review.
module intg_review_signoff #(parameter int BLOCKS_PASSED_IS_PROOF = 0) (
  input  logic clk, rst_n,
  input  logic        review,
  input  logic        blocks_passed, domains_qualified, resets_ordered,
  input  logic        params_compared, map_disjoint, ties_published,
  output logic [5:0]  fail_mask,
  output logic [15:0] conditions_met, sound_pct,
  output logic        sound,
  output logic [7:0]  n_reviews, n_sound, n_claimed,
  output logic        intg_err
);
  logic [31:0] s_q;
  logic        truly_sound, claimed;
  assign fail_mask[0] = ~blocks_passed;
  assign fail_mask[1] = ~domains_qualified;
  assign fail_mask[2] = ~resets_ordered;
  assign fail_mask[3] = ~params_compared;
  assign fail_mask[4] = ~map_disjoint;
  assign fail_mask[5] = ~ties_published;
  assign conditions_met = {15'd0, blocks_passed} + {15'd0, domains_qualified}
                        + {15'd0, resets_ordered} + {15'd0, params_compared}
                        + {15'd0, map_disjoint} + {15'd0, ties_published};
  assign s_q = ({16'd0, conditions_met} * 32'd100) / 32'd6;
  // No clamp: six one-bit values over six cannot exceed a hundred.
  assign sound_pct = s_q[15:0];
  assign truly_sound = (fail_mask == 6'd0);
  assign claimed = (BLOCKS_PASSED_IS_PROOF != 0) ? blocks_passed : truly_sound;
  assign sound = claimed;
  assign intg_err = review && !truly_sound && claimed;

  always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      n_reviews <= 8'd0; n_sound <= 8'd0; n_claimed <= 8'd0;
    end else if (review) begin
      n_reviews <= n_reviews + 8'd1;
      if (truly_sound) n_sound <= n_sound + 8'd1;
      if (claimed)     n_claimed <= n_claimed + 8'd1;
    end
  end
endmodule

The measurement. Two views of the same integration review:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
mask=000010 met=5 sound=83% / mask=111110 met=1 sound=16%

The first line is a real review with one finding open — bit 1, a multi-bit crossing that is not qualified. Five of six conditions established, one item to close.

The second line is what this chapter exists to prevent. Bit 0 is clear and every other bit is set: both blocks passed their own reviews, and nothing else was checked. No crossing qualified, no reset order stated, no parameter compared, no map checked, no tie-off published. Sixteen percent of an integration review, reported as an integration review — and it is the most defensible-sounding sign-off in this module, because everything it says is true.

A block diagram of an integration review sign-off. Both blocks passing their own reviews is one of six conditions. The other five are domain crossings qualified, reset order stated, parameters compared, address map disjoint, and tie-offs published. Bit zero alone yields sixteen percent; all six yield a sound review.both blocks passedbit 0crossings qualifiedbit 1reset order statedbit 2parameters comparedbit 3map disjointbit 4tie-offs publishedbit 5sign-offsix conditionsbit 0 only: 16 percenttwo correct blocksall six: soundan integration review12

Figure 3 — bit 0 is the one both teams can evidence. Each of the other five is a statement about the space between the blocks, which is why each needs an owner named before the review starts.

18. Quantitative Reasoning

Every figure here is a teaching parameter or a value derived from one and asserted by the testbench. None is a measurement of a real system, and none is a CXL specification number.

The assembled value. F0 is 1111_0000 and 0F is 0000_1111. Taking bit 3 from the old word and the other seven from the new gives 0000_0111 = 07. Both source words are legal and 07 is neither of them. In general, an n-bit bus with k bits skewed can assemble up to 2^k distinct words, of which at most two are values the source held.

How many synchronisers. A bus of n bits protected per-bit costs 2n flops and protects nothing about the word. A qualified crossing costs 2 flops for the qualifier plus the n flops of the holding register — fewer flops and a correct result at every width above one.

Parameter mismatch, derived. A = 16, B = 12, payload = 14. The applied width is min(16, 12) = 12, and the truncation is 14 − 12 = 2 units, a 14.3 percent loss of the payload. The general form is loss = max(0, payload − min(A, B)), which is why a mismatch is invisible whenever the payload happens to be small.

Handshake readings, derived. Three cycles of valid against one cycle of ready transfers 1 beat. The strict reading counts 1; the willing reading counts 3. The lost count is valid cycles − transfers = 2, and it scales with the fraction of cycles the consumer withholds readyzero on an idle link and proportional to load on a busy one.

Byte order. A1B2 reversed is B2A1two bytes moved, a hundred percent of the payload wrong. C3C3 reversed is C3C3zero percent wrong, from the identical defect. A palindromic test pattern has a detection probability of zero here; an asymmetric one has a detection probability of one. That factor is the whole value of choosing the pattern.

The remainder, derived. Three narrow beats into a two-to-one adapter make one whole wide word and a remainder of 1 beat. The flushing build emits 2 words for 3 beats in, and the conservation equation 3 = 2 + 1 holds with the flush counted. The dropping build emits 1 word, and 3 = 2 + 1 fails by one beat — 33 percent of the transfer, silently. The general form is lost = beats mod ratio, which is zero for exactly the transfer lengths a directed test picks.

Credits, derived. A capacity of 4 initialised by one owner advertises 4. Initialised by both it advertises 4 + 4 = 8 — an overclaim of 100 percent, and the producer can have four beats in flight with nowhere to put any of them.

Address overlap, derived. A covers 0x100x3F, which is 48 addresses. B covers 0x300x5F, also 48. The overlap is 0x300x3F = 16 addresses, one third of region A. An access at 0x38 is inside both. Regions that touch at exactly one address overlap by one address, which is the case a < instead of a <= gets wrong and the case a four-address spot check never drives.

Quiescence, derived. Three requests issued and one completed leaves 2 outstanding. "No new requests" is true and the block is not idle; gating orphans two responses, and each orphan is one requester waiting for something that will never arrive.

Tie-off, derived. Intended mode 3, default mode 1, port unconnected: the effective mode is 1. The behaviour difference is 100 percent of the configuration decision, and the observable difference between the two builds is zero — only the published value differs.

Revision skew, derived. A at 3, B at 1, a feature requiring 2. The negotiated revision is min(3, 1) = 1, and 1 is below 2, so the feature is unavailable. Skipping negotiation operates at A's own 3, and 3 is at or above 2, so the feature is believed available. The boundary is a peer at exactly 2, which is compatible — the case that separates a correct comparison from an off-by-one one.

The sign-off arithmetic. Six conditions; five met is 5 x 100 / 6 = 83 percent under integer division, and one met is 100 / 6 = 16 percent.

19. Verification Method

Order of work

compile → inspect warnings → legal baseline → reset → boundaries → simultaneous events → abuse and error cases → configuration contrasts → structural gates → PASS → mutation campaign

A mutation campaign on a failing baseline is invalid, and both campaigns in this chapter ran against a green one. The baseline was re-run after every testbench modification before the campaign was re-run.

Both sides are always present

This is the one methodological difference between this chapter and the rest of Module 30. Every model instantiates both readings of a shared contract against the same stimulus, because an integration defect cannot be exhibited by one side. A single-block environment stubs the other end, and a stub implements whichever reading its author holds — which is precisely the assumption under review.

Independent oracles

ModelOracle
domain hopF0 then 0F with bit 3 late → 07 assembled, 0F qualified
reset orderB out, A in reset, traffic → local gate honours, both-gate refuses
parameter agreementA 16, B 12, payload 14 → applied 12, truncated 2
handshake reading3 valid, 1 ready → 1 taken, 1 strict send, 3 willing sends
byte orderA1B2 → a1b2 straight, b2a1 reversed; C3C3 symmetric
width adapt3 beats, ratio 2 → 2 emitted flushing, 1 lost dropping
credit ownercapacity 4 → 4 one owner, 8 two owners; re-init stays 4
decode overlap10..3F and 30..5F, addr 38 → both hit; adjacent 2F/30 → neither
gate quiescence3 issued, 1 done → 2 outstanding, idle gate fires, quiescence gate does not
tie-offintended 3, default 1 → effective 1, published 1, echoed 3
revision skewA 3, B 1, feature needs 2 → negotiated 1, unavailable
sign-offfive of six → 83 percent; one of six → 16 percent

chkv prints got against expected, which is what lets an oracle be wrong out loud. In this chapter it caught three, all mine, recorded in section 20.

X and Z rejected explicitly

chk(c, …) tests c !== 1'b1, so an X-valued condition fails rather than passing. chkv(got, exp, …) reduces the result and reports an explicit X/Z failure before comparing. That rejection is load-bearing — the chapter after this one owes it three undriven outputs that -Wall did not mention.

Pulses are latched, never sampled

Every evidence output — cdc_err, rst_err, cfg_err, hs_err, order_err, residue_err, credit_err, decode_err, gate_err, tie_err, skew_err, intg_err — is caught by a continuous always @(posedge clk) monitor into a sticky bit, asserted outside any conditional, in a window containing a clock edge.

Stimulus never lands on the active edge, and reset is released after it

step_clk is @(posedge clk); #1;. Reset release lands one delta after the edge, carried forward from the race 30.5 exposed.

Safety, liveness and performance kept apart

Safety — a value delivered across a domain is a value the source held; no traffic is honoured while either side is in reset; advertised credits never exceed real capacity; one request reaches one target; the block is never gated with work outstanding; every beat accepted is eventually emitted.

Livenessone claim, and it is stated with its assumption. The credit link starts assuming somebody initialises it. That assumption is the review item.

Performance — nothing in this chapter is a performance claim. A dropped beat is a safety failure, not a slow one, and the distinction decides whether a finding blocks a tapeout.

20. Baseline Defects Found Before Mutation

RTL findings — one, and the campaign found it

An unstated contract inside the credit model, described in full in section 12. cr_q <= cr_q + capacity and cr_q <= capacity are identical from reset and differ only on a re-initialisation, and the model had never said which it meant. The chapter's own subject, one level down: a model about two sides disagreeing on a shared contract carried an unstated contract of its own.

Testbench defects — one

The domain-crossing sample window was one cycle early. The assembled word reaches the destination register on the crossing edge, so a sample_now asserted in that cycle reads the value from before it and the impossible-value counter never moved. Moved to the cycle after.

This is the same class of error as the wrong oracles below — not arithmetic, but when. Section 13 of 30.2 names it: a registered value is available the cycle after the condition that produced it, and every off-by-one in this chapter is that sentence unapplied.

Wrong oracles — three, all mine

ExpectedTruthWhy
the benign-skew case delivers 0F07prev only moves on an update, so the two source words were still different
wide_out holds 0x120x23wide_out is the live total, not the word emitted one beat earlier
the gate counter reads 10it counts a rising edge, so it moves on the next clock, not in the cycle the decision becomes true

The second is the one worth keeping. wide_out is named for what it outputs, and a register named for what it outputs is not a record of what it output. I read the name instead of the assignment, which is the same mistake in a testbench that a reviewer makes reading a signal list instead of the RTL.

Coverage gaps found by the structural gates

GateFindingClosed by
outscan32 unasserted output netsvalue assertions on every one
banned, excheck, splitcheck, domcheck, displaychecknone

Compiler-warning findings

Under -Wall the twelve models produce no truncation, select-width, signedness, cast, multiplication-width, shift-width or loop-width warnings. The only warning is a missing timescale on models with no delay constructs — inspected and recorded as benign.

Two width results were reasoned rather than trusted. The width adapter's accumulator is {acc_q[3:0], beat_data} — an 8-bit result from a 4-bit slice and a 4-bit input, exact by construction. The domain-crossing model's assembled is a three-part concatenation that must total exactly eight bits: [7:4] is four, the selected bit is one, [2:0] is three. The tool does not check that a concatenation matches its destination when the widths happen to agree, so both were counted by hand.

Simulator constraints

Icarus Verilog 13.0 rejects ref task arguments, carried forward from every chapter in this module.

21. Mutation Testing

98 mutations attempted, 98 non-equivalent, 98 killed. Zero unexplained survivors, zero equivalent mutants withdrawn.

Reported separatelyCount
Mutants attempted98
Withdrawn as equivalent0
Non-equivalent mutants98
Killed98
Unexplained survivors0
ModelDimensionMuts
m1domain crossing8
m2reset order7
m3parameter agreement7
m4handshake reading7
m5byte order7
m6width adaptation9
m7credit ownership10
m8decode overlap8
m9gate quiescence8
m10tie-off default7
m11revision skew8
m12review sign-off12

The first campaign, over models 1 to 6, produced zero survivors. The second, over models 7 to 12, produced six, every one classified before anything was changed.

One survivor was a defect in the model, not a gap in the stimulus

The credit mutation turning cr_q <= cr_q + capacity into cr_q <= capacity could not be killed, because the two are identical from reset. They differ only on a second initialisation, which nothing drove.

The correct response was not to add a stimulus case to a model whose meaning was undecided. The model was restructured so the two are explicit — an initialisation loads, the two-owner defect adds on top — and the re-initialisation case was then added and the mutation killed. A mutation that cannot be killed is sometimes telling you the model does not know what it means.

Three were a boundary the stimulus approached and never landed on

BoundaryWhat was drivenWhat was not
the credit saturation ceilingordinary countsa capacity of 255 with a return on top
regions overlapping by one addressa one-address gap between themregions sharing exactly one address
a region's first addressfour addresses inside and outsideexactly b_lo

This is the same family 30.5 produced, and the same rule kills it:

For every comparison in a model, drive the value below it, above it, and exactly on it.

The address-map case is the one that generalises furthest. Two regions that touch at exactly one address do overlap, and a spot check on four convenient addresses is path coverage rather than value coverage.

Two were checkers that did not look

The gate counter was held for two cycles, and the count was asserted after the first and never after the second — which is the only place a per-cycle counter differs from a per-edge one. The tie-off check counter was asserted only at reset, where both builds read zero.

Both are the same shape: a check placed where the two builds agree. It is the shape a vacuous checker takes when nothing about it looks wrong.

The classification rule

Never add an assertion for a survivor before classifying it.

ClassMeans, and what to do
Equivalentno input tells the two apart — withdraw it, never count a kill
Stimulus gapthe case is never driven — extend the stimulus
Missing checkerthe case is driven and nothing looks — add the checker
Vacuous checkerthe check cannot fail — fix the check, not the design
Model ambiguitythe model has not decided what it means — decide, then re-mutate
Unreachableits guard never holds — fix the guard
Maskedanother mechanism hides it — expose it, or say why you cannot
Coincidentalthe arithmetic happens to agree — change the stimulus
Missing configthe build that differs is never built — instantiate it
Otheranything else — state it precisely

The fifth row is new in this chapter, and it is the one that produced a better model rather than a better testbench.

22. Synthesis And Implementation Reality

A qualified crossing is cheaper than a per-bit one at every width above one. Two flops for the qualifier and a holding register the design already needs, against two flops per bit. The correct structure costs less than the incorrect one, which removes every argument for the incorrect one.

A reset sequencer is a small counter and a comparator, plus one "both released" net routed to the interface gate. It is the cheapest ordering guarantee available, and the alternative is a window whose width depends on reset-tree balancing.

A parameter comparison at the boundary is a register per side and an equality check. For a 16-bit parameter that is 32 flops and a comparator — less than any single one of the defects it catches costs to debug.

A lost-beat counter is one register and a two-input gate. It is the only structure that can distinguish two readings of a handshake in silicon.

Byte-order correctness is free. A reversal is wires. Its cost is entirely in the convention being written down, and it is the only item in this chapter with no gate count at all.

A flush path costs a byte-enable per narrow beat and one comparator. For a four-to-one adapter that is four bits and a small amount of control — against the tail of every odd-length transfer.

A credit-count publication register is the credit counter routed out. It already exists, and this item is the cost of a read port.

An overlap check is a pair of comparators per decoder pair, or nothing at all if the map is checked in a script before elaboration. The script is free and the hardware detector is a handful of gates — and only the second one survives a map edited after the script ran.

An outstanding-transaction counter exists in almost every design that can have transactions outstanding. Gating on it costs a zero-comparison. The expensive version of this item is the one where the counter does not exist, and then it is one register.

A configuration-report register is the effective values, routed to a read address. No logic at all. It is the cheapest item in this chapter and among the most often missing, because nothing fails without it.

A negotiated-revision register is two registers, a minimum, and a comparator. The minimum is two comparators wide at whatever the revision field is.

No area, frequency or power figures appear in this chapter, because none was measured.

23. Silicon Observability

TelemetryWhat it exposes
a count of destination values outside the legal seta multi-bit crossing assembling values the source never held
a sticky "traffic while not both ready" bitan unordered reset release, on any power-up that ever hit the window
each side's parameter value, publishedthe mismatch itself, comparable by anything that can read two registers
beats sent and beats taken, from both sidesa disagreement about what ready means, as a difference
a known asymmetric pattern in a scratch registera byte-order reversal, in one read
beats in, beats out and beats heldthe conservation equation, evaluated in hardware
advertised credits against real capacityan overclaim, and a dead-link bit for the zero-credit case
a multiple-hit counter per decoder pairtwo targets claiming one address
the outstanding count at the moment of gatinga gate decision that is reviewable after the hang
the effective value of every defaulted inputa fleet running a configuration nobody chose
the negotiated revision and a degraded flagwhich half of a mixed-revision fleet a bug lives in

Five of these must read permanently zero — impossible values, traffic before both-ready, multiple decoder hits, work outstanding at a gate, and lost beats. Each costs a flop or two, and each converts a defect that is debugged for a week into a register read.

The pattern to read on the sent-against-taken pair: the two numbers are equal on a link the consumer never backpressures, which is every link in bring-up. Their difference only appears under load, which is why the counter must exist before the load does.

The configuration report is the one to keep if only one survives area review. It has no logic, it makes every other finding localisable to a specific build, and it is the only item here that helps with defects this chapter does not cover.

A flowchart for an integration review. Both blocks passed, then every multi-bit crossing is qualified, the reset release order is stated, shared parameters are compared, the address map is disjoint, and tie-offs are published. Any failure ends in an interface nobody owns; passing all six ends in a sound review.yesyesyesyesyesboth blocks passedcrossingsqualified?reset orderstated?parameterscompared?map disjoint?tie-offspublished?review soundany no: aninterface nobodyowns
Figure 4 — the integration review as a flow. The first decision is the weak one and the only one many reviews reach: both blocks passed. The five below it are ordered by how early each defect corrupts the data — the crossing first, because a value that was never held is wrong before any protocol applies, then the reset window, then the shared parameters, then the map, and finally the tie-offs, which decide which configuration all the other answers were about.

24. DebugLabs

Lab 1 — A configuration word is occasionally a value nobody ever wrote

Symptom. A destination block occasionally reads a configuration value that appears in no source, no test and no trace. It happens roughly once per thousand updates and never in a directed test.

Evidence. The source register's history contains only legal values. The destination's history contains one that is not among them. The bus has a two-flop synchroniser on every bit.

Hypothesis. The word is being assembled from two source values.

Investigation. Compare the impossible value against the two source words on either side of the last update, bit by bit. Every bit of the impossible value matches one of them.

Root cause. Per-bit synchronisers on a multi-bit bus. Each bit resolves independently, and on an update where several bits change the destination latches a mixture.

Fix. One synchronised qualifier, with the bus held stable across the crossing.

Prevention. A review rule counting synchronisers against bits. If the count is not one, the crossing is unqualified. And a DV test that changes several bits in one cycle, which a one-bit-at-a-time directed test never does.

Silicon observability. A counter of destination values outside the legal set, where the legal set is enumerable. A single non-zero read is the whole diagnosis.

Lab 2 — The first transaction after every power-up is lost

Symptom. A link drops exactly one transaction per power-up. Never two, never zero. After the first, it runs for weeks.

Evidence. The loss is at power-up only. Both blocks pass their own reset tests. The reset trees are in different hierarchies.

Hypothesis. One block is honouring traffic before the other exists.

Investigation. Capture both reset releases against a free-running counter. B releases three cycles before A, and the first transaction arrives in the gap.

Root cause. No stated release order, and an interface gate qualified on the local side only.

Fix. A sequencer that releases in a stated order, and a "both released" qualifier on the interface.

Prevention. A DV case that releases the two ends in each order and drives traffic in the gap. A single-block environment cannot construct it, which is why it passed both reviews.

Silicon observability. A sticky "traffic seen while not both ready" bit. One flop, and it must read permanently zero.

Lab 3 — A length field is right up to 12 and wrong above it

Symptom. Transfers of 12 units or fewer are correct. Transfers of 13 or more lose the tail. The loss is always exactly the amount above 12.

Evidence. The producer's counters say it sent the full length. The consumer's say it received 12.

Hypothesis. The two sides were built with different widths.

Investigation. Read both elaborated parameter values. One is 16 and one is 12.

Root cause. A shared parameter with two values, and nothing that compares them.

Fix. Set them from one source, and publish both at the boundary with a mismatch flag.

Prevention. Ask for the value each side elaborated with, as a number. The parameter name being identical is what made this invisible.

Silicon observability. Both values in a readable register, and a one-bit mismatch flag. The cheapest integration check that exists.

Lab 4 — Throughput is fine and a fraction of beats never arrive

Symptom. A link loses a small fraction of beats. The fraction grows with load. Neither side reports an error.

Evidence. The producer's sent count exceeds the consumer's received count. Both counters are correct against their own definitions.

Hypothesis. The two sides disagree about what constitutes a transfer.

Investigation. Trace valid and ready together. Beats are lost exactly where ready falls in the cycle after it rose, with valid still high.

Root cause. One side counts a send when valid is asserted; the other transfers only when both are high on the same edge.

Fix. One written rule — a beat transfers when valid and ready are both high on the same rising edge — and a lost-beat counter proving both sides implement it.

Prevention. A DV case holding valid and pulsing ready for one cycle in three. A test with ready tied high makes both readings agree.

Silicon observability. Sent and taken, published by both sides. Either number alone proves nothing; their difference is the defect.

Lab 5 — Payloads arrive with their bytes swapped and only in one direction

Symptom. Data crossing one boundary is byte-reversed. The reverse direction is correct.

Evidence. The reversal is exact and total. The bring-up test suite passes, and it uses a repeated-byte pattern.

Hypothesis. The two sides apply different byte-order conventions, and the test pattern cannot see it.

Investigation. Drive an asymmetric pattern. It comes back reversed. Drive the repeated pattern again. It comes back correct.

Root cause. Two conventions, never written down, at a boundary that reverses.

Fix. One convention at the boundary, applied once.

Prevention. The test pattern is part of the review. A palindromic pattern has a detection probability of zero for this defect, and repeated bytes are the easiest pattern to generate.

Silicon observability. A known asymmetric pattern in a scratch register, readable from both sides. Reading it back the wrong way round is a one-instruction diagnosis.

Lab 6 — Odd-length transfers lose their tail

Symptom. Transfers whose length is a multiple of the width ratio are correct. Every other length loses a beat.

Evidence. Beats in and beats out differ by exactly length mod ratio.

Hypothesis. The adapter has no end-of-transfer path.

Investigation. Trace the accumulator across end-of-transfer. It holds a partial word, and the next transfer overwrites it.

Root cause. Emit-on-full-word with no flush. Writing no flush code produces exactly this, which is why it is common.

Fix. Flush the partial word with a valid-beat count.

Prevention. An odd-length transfer in the regression, and a continuous conservation check: beats in equals beats out plus beats held.

Silicon observability. The three counters and the equation evaluated in hardware.

Symptom. A requester waits forever for a response. It happens after a low-power entry, under a pattern that leaves work outstanding at the idle threshold.

Evidence. The clock gate asserted. Two responses were owed at that moment. The gating logic is in a different block from the gated one.

Hypothesis. The gate condition means "no new requests", not "nothing outstanding".

Investigation. Capture the outstanding count at the gate assertion. It is two.

Root cause. Gating on idle rather than on quiescence.

Fix. Gate on no-new-requests and an outstanding count of zero, and publish the count at gate time.

Prevention. Read the gate condition in full at review. "Idle" is a word; the outstanding count is a number, and only the number is a condition.

Silicon observability. The outstanding count at the moment of gating. Permanently zero, and it makes a hang diagnosable from a single register.

Lab 8 — A bug reproduces on one build and not on an identical one

Symptom. Two systems built from the same source behave differently. Neither build can be told apart from outside.

Evidence. The block's configuration report shows the intended mode on both. The behaviour differs.

Hypothesis. The report echoes intent rather than the effective value.

Investigation. Read the port connection in the integration netlist. On one system the mode input is unconnected and takes its default.

Root cause. A defaulted input, left unconnected, and a status register that reports what was intended rather than what is in force.

Fix. Publish the effective value, and connect the port.

Prevention. For every defaulted input, a readable register holding the value the instance is running with. Not the default in the source — the effective value in the instance.

Silicon observability. A configuration-report register carrying every effective value. This item is telemetry, which is why it is the cheapest of the eleven to fix.

25. Coverage Reasoning

Functional coverage on one block cannot cover an integration defect, because a single-block environment supplies the other side as a stub, and the stub implements its author's reading of the contract. The assumption under review is the one thing the environment cannot question.

Four coverage models are worth adding to any environment reviewed with this chapter:

Simultaneous-transition coverage on every crossing. Bins on the number of bits that changed in one cycle: one, two, and more than two. The bin a one-bit-at-a-time directed test can never hit is the defect, and it is the bin a random stimulus fills by default.

Reset-order coverage. A cross of release order against traffic presence in the gap. Four cells, of which two are only reachable in a two-block environment — which is the proof that the single-block environments were not enough.

Boundary coverage per comparison. For every comparison in the design, three bins: below, above, and exactly on. Section 21 records three survivors that this model would have caught before the campaign ran, and the address-map case is the one that recurs.

Handshake-shape coverage. A cross of valid duration against ready duty cycle. The cell that separates the two readings is "valid held, ready pulsed", and a test with ready tied high populates none of it.

The bin the weak build cannot hit is the most valuable bin in any model. In section 6 it is "destination value outside the legal set, with no source change". In section 12 it is "advertised credits above real capacity". In section 15 it is "published value differs from effective value". Each is unreachable in the sound build and trivial in the weak one, which makes the coverage report a direct test of the review item.

26. How This Appears In Real Engineering

Per-bit synchronisers on a bus are the commonest CDC finding there is, and they survive because they look more careful than the correct structure. Eight synchronisers are visibly more effort than one.

Reset ordering is not owned because reset is a block-level concern in every block-level review, and the order between blocks appears in no block's specification.

Parameter mismatches survive because the parameter names match. A reviewer reading two instantiations sees the same identifier on both and moves on; the values are in two different defaults files.

Handshake disagreements are almost always documentation defects. Both implementations are internally consistent with their own documents, and the two documents were written in different quarters.

Byte order is settled by convention and rarely written at the boundary, so each side applies the convention its author learned.

A width adapter with no flush is what you get by writing nothing. The full-word path is the obvious path, and the remainder needs code that nobody writes unless somebody asks for it.

Credit ownership is the classic two-team omission: each team assumes the other initialises, and the result is a dead link that is diagnosed as a bring-up problem rather than an integration one.

Address maps are checked by a script, and edited after the script ran. The hardware detector is what survives the edit.

Power-gating decisions are made by a power-management block that cannot see the gated block's outstanding count, unless somebody routed it, and routing it is an integration task.

Tie-off defaults exist so that a module can be instantiated without connecting everything, which is convenient exactly until a fleet is running a configuration nobody chose.

Revision skew is created by every staggered upgrade, and "backward compatible" is a statement about the specification rather than about either implementation.

27. Common Misconceptions

"Every bit has a synchroniser." Then no bit is safe as part of a word. One qualifier, or a structure that holds the bus stable.

"Metastability is the problem." Metastability is resolved by the synchroniser. The problem is that it is resolved independently per bit, which is a data-integrity problem, not a timing one.

"Both blocks come out of reset together." That is a claim about timing that nothing in either block guarantees. State the order and enforce it.

"The parameter is the same, it is the same name." Ask for the value each side elaborated with. A name is not a value.

"ready means it will take it." Or it means it has taken it. Both are defensible, which is why exactly one must be written down.

"The test passes with a repeated pattern." A palindromic pattern cannot detect a reversal. The pattern is part of the test, not a detail of it.

"The adapter works, we tested it." With an even length? Odd lengths are where the remainder goes somewhere.

"The other side initialises the credits." Both sides said that in one case and neither in the other. Name the owner.

"Each decoder is correct." Correct against its own region. Overlap is a property of the map, and the map has no owner.

"The block is idle, we can gate it." Idle means no new requests. Quiescent means nothing outstanding, and only the second one is safe.

"The port is unconnected, so it does nothing." It takes its default, which is a decision somebody else made. It does exactly one thing, and nobody chose it.

"The interface is backward compatible." The specification is. The implementation needs to detect the older peer and fall back, and a fallback that is not negotiated does not happen.

"Both blocks passed review." That is bit 0, and it is worth one sixth of an integration review.

28. Interview And Design-Review Questions

Crossings and reset

1. Why is a two-flop synchroniser insufficient for a bus? It makes one bit safe to sample in a foreign domain. On a bus each bit resolves independently, so the destination can latch a combination of the old and new words.

2. Source goes from F0 to 0F with bit 3 late. What can the destination read? 07 — bit 3 from the old word and the rest from the new. Neither source word.

3. What is the correct structure? Synchronise one qualifier, and hold the bus stable while it crosses. Or a gray code, or an asynchronous FIFO — all of them make one signal the thing that crosses.

4. Why does this defect survive a directed test? A directed test usually changes one bit at a time, and one bit at a time is safe.

5. When is a skew harmless? When the two source words are equal. A slow-changing bus carries this defect for years for exactly that reason.

6. Who owns the reset release order between two blocks? Usually nobody, which is the defect. Each block's reset is locally correct.

7. What does a consumer see if it releases first? A producer that is still resetting, and whatever it samples in that window is not a protocol violation by either side.

8. How do you enforce it? A sequencer with a stated order, and a "both released" qualifier gating the interface.

9. What telemetry proves it never happened? A sticky bit set by traffic seen while not both ready. One flop, permanently zero.

Shared contracts

10. Two blocks share a parameter. What enforces agreement? Nothing in the language. Each elaborates cleanly against its own value.

11. A is 16, B is 12, the payload is 14. What arrives? Twelve. The narrower side is what applies, and two units are truncated silently.

12. What evidence would you ask for in review? Both values, as numbers. Not the parameter name.

13. Give the two readings of ready. "I will take it if you present it" and "I have taken it". Both are defensible readings of two wires.

14. Three cycles of valid, one of ready. How many beats transferred? One. A side counting sends on valid alone counts three.

15. Why does this defect scale with load? Beats are lost when the consumer withholds ready, which it does more often as it gets busier. Zero on an idle link.

16. What single counter distinguishes the two readings in silicon? Sent minus taken. Neither number alone proves anything.

17. Why is a repeated-byte test pattern dangerous? It is palindromic, so a reversal at the boundary produces the identical value. Detection probability zero.

18. What does a byte-order defect look like in the lab? Data corruption, and it is debugged as one — usually for a while.

Structures at the boundary

19. Three beats into a two-to-one adapter. What must happen to the third? It is flushed with a valid-beat count, or held for the next transfer. Dropping it requires no code, which is why it is common.

20. State the conservation equation. Beats in equals beats out plus beats held. Evaluated continuously, it catches every variant of this.

21. Which transfer lengths hide the defect? Multiples of the ratio. Which is what a directed test uses.

22. Who initialises credits, and how many owners are correct? Exactly one, named. Two advertises capacity twice; zero leaves the link dead.

23. What does a zero-credit link look like? A dead interface, diagnosed as a bring-up problem rather than an integration defect.

24. What second question does the credit contract need? What a re-initialisation does to an existing count. A retrain must reload, not accumulate — and that is the question nobody asks.

25. Two decoders are each correct. What can still be wrong? The map. Overlap is a property of the pair and belongs to neither decoder.

26. Regions 10 to 3F and 30 to 5F. What does an access to 38 do? Both decoders assert. Two targets respond, and the requester takes whichever arrives first.

27. Why is that worse in silicon than in simulation? Simulation is deterministic. In silicon the winner depends on physical routing.

28. Regions touching at exactly one address — do they overlap? Yes, by one address. It is the case an off-by-one comparison gets wrong and a spot check never drives.

Power, defaults and revisions

29. Distinguish idle from quiescent. Idle means no new requests. Quiescent means no new requests and nothing outstanding.

30. What happens if you gate on idle with two responses owed? They never arrive. The requester hangs, and the cause is in the block that made the gating decision.

31. Why is that a hard debug? Nothing is corrupted and no protocol is violated. The symptom is a wait, and the cause is in a different file.

32. What register makes it a one-read diagnosis? The outstanding count captured at the moment the gate asserted. A hang gives you no other evidence — the block stopped being clocked, so nothing after that point was recorded — and this one register says whether the gate was legitimate.

33. An input is left unconnected. What is it doing? Taking its default — a decision made by whoever wrote the module, applied by whoever instantiated it staying silent.

34. What should a status register publish, intent or effect? The effective value. A register echoing intent makes the two builds indistinguishable from outside.

35. Why does this matter for a fleet? A bug that reproduces on one build and not another cannot be localised if the build cannot be read back.

36. What does "backward compatible" guarantee about an implementation? Nothing. It describes the specification. The newer side must detect the older peer and fall back.

37. A at revision 3, B at 1, a feature needing 2. What should happen? Operate at 1 and refuse the feature. Skipping negotiation operates at 3 and uses something the peer cannot honour.

38. What is the boundary case there? A peer at exactly the revision the feature requires. Compatible — and the case an off-by-one comparison gets wrong.

Method

39. Why can a single-block environment not find these? It stubs the other side, and the stub implements its author's reading of the contract — which is the assumption under review.

40. What single condition makes a mutation campaign invalid? A failing baseline. Every mutation then fails for the reason the baseline does.

41. A mutation cannot be killed and is not equivalent. Name a third possibility this chapter found. The model has not decided what it means. Two forms of "initialise" were identical from reset and the model had never said which it meant.

42. What is the right response to that? Decide, restructure the model, then re-mutate. Not a stimulus patch on an undecided model.

43. Three survivors here were one family. Which? A boundary the stimulus approached and never landed on.

44. State the rule that kills it. For every comparison, drive below it, above it, and exactly on it.

45. Two survivors were checkers that did not look. What shape did they take? A check placed where the two builds agree — after reset, or after the first cycle of a two-cycle event.

46. Name the single question this chapter's review turns on. Whose assumption is this, and does the other side know about it?

29. Exercises

1 — Design review · Advanced. Builds: reading a crossing for what it protects. You are shown a 24-bit status bus crossing into a second clock domain with a two-flop synchroniser on every bit. Bounded scope: state what is and is not protected, give the value class the destination can read, and specify the replacement structure with its flop count. Hint: compare the flop counts of the two structures before arguing about correctness.

2 — Debug · Advanced. Builds: localising a defect that belongs to neither block. A link loses exactly one transaction per power-up, never more. Bounded scope: give the hypothesis, the two signals you would capture, and the one-flop telemetry that would have made it a register read. Hint: "never more than one" is the shape of a window, not a rate.

3 — Code review · Intermediate. Builds: checking a shared contract by value rather than by name. Two blocks instantiate the same interface with a WIDTH parameter, set from two different defaults files. Bounded scope: state what each block does at elaboration, what happens at the boundary, and the boundary check you would require. Hint: the reviewer's mistake is reading the identifier.

4 — Design · Advanced. Builds: specifying a handshake so two teams cannot read it differently. Write the transfer rule for a two-wire handshake in one sentence, then specify the telemetry that proves both sides implement it. Bounded scope: the rule, the counters, and the test shape that separates the two readings. Hint: ready tied high makes both readings agree.

5 — Verification · Advanced. Builds: choosing a test pattern as a review decision. A boundary may or may not reverse byte order. Bounded scope: give a pattern that cannot detect a reversal, one that always does, and state the detection probability of each. Hint: the easiest pattern to generate is the useless one.

6 — Design · Intermediate. Builds: making a structural property continuously checkable. Specify the end-of-transfer behaviour of a four-to-one width adapter. Bounded scope: state where the remainder goes, write the conservation equation, and give the transfer lengths that must appear in the regression. Hint: the lengths that hide it are the ones a directed test picks.

7 — Design review · Advanced. Builds: finding the contract nobody wrote. Review a credit-based interface between two teams. Bounded scope: list every question that must be answered before either side is implemented, including what a retrain does to an existing count, and say which failure each unanswered question produces. Hint: one of the two failure modes presents as a dead link rather than as a bug.

8 — Verification · Expert. Builds: coverage that reaches a cell one environment cannot. Define a coverage model that would find the reset-ordering defect. Bounded scope: specify the bins, the cross, identify which cells are unreachable in a single-block environment, and say what that unreachability proves. Hint: the proof is which cells cannot be filled, not which were.

30. Summary

An integration defect is not a bug in either block. It is a contract that was never written down, read differently by two teams who were each internally consistent.

A synchroniser per bit protects every bit and no word. One qualifier costs fewer flops and delivers a value that existed.

The reset release order between two blocks belongs to nobody, which is why it must be given an owner explicitly.

A shared parameter is a contract the language does not enforce. Ask for the value each side elaborated with, as a number.

Two wires admit two readings, and exactly one of them must be written down: a beat transfers when valid and ready are both high on the same edge.

A palindromic test pattern cannot detect a byte-order reversal. The pattern is part of the review.

A width adapter with no flush is what writing nothing produces, and it loses the tail of every transfer whose length is not a multiple of the ratio.

Credits need exactly one named owner, and a second question: what a re-initialisation does to an existing count.

Overlap is a property of the map, and the map belongs to neither decoder.

Idle is a word and the outstanding count is a number. Only the number is a gate condition.

An unconnected input is running a configuration somebody else chose. Publish the effective value, not the intent.

"Backward compatible" describes a specification, not an implementation. A fallback that is not negotiated does not happen.

Six conditions, and "both blocks passed" is one of them. A real review with one finding open is 83 percent. Two correct blocks and nothing else is 16.

Continue learning

Related tutorials

Standards & specifications

Governing standard
CXL Specification (CXL Consortium)(opens CXL Consortium in a new tab)

Defines CXL.io, CXL.cache and CXL.mem, and the coherence and memory-pooling behaviour built on them. System design and deployment topology are not mandated.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the CXL curriculum.