Software writes the same configuration twice. The shadow comparator sees a match and commits it. If both writes carry the same wrong value, the protocol still succeeds. We distinguish update consistency from permission to choose that value.
The first write is staged, not active
An equipment cabinet requires two matching entries to change its rule. Active code starts at 9. A first entry of 6 is staged while the cabinet still uses 9. A second 6 accepts the update, after which active becomes 6: active, staged, pending, and csr_commit. A second 7 leaves 9 in use. The story checks an update protocol, not two independent policy approvals.
A control/status register, CSR, exposes configuration or status to software. A shadow update first stages a value without changing the committed value used by hardware. A second matching write completes the update. Our four-bit RW example starts at restrictive FAIL=9.
At edge 1, write 6 enters staged storage and pending becomes true after the edge. The active CSR is still 9. At edge 2, another 6 compares with staged=6. The update commit occurs on that edge; active storage changes afterward. This CSR commit means accepted configuration update, not instruction retirement.
If the second value is 7, the pair fails. The committed value remains 9 and an update error is recorded. That is a detected inconsistency in the write protocol. It says nothing about a persistent storage error after a successful update; that target needs a separate checker and test.
OpenTitan documents paired shadow writes, update/storage error distinctions and reset concerns. Our compact protocol omits its complemented storage, read-to-reset-phase behavior and specific bus semantics. It must not be presented as a replica of prim_subreg_shadow. Register generator documentation
Separate sampling from the two write updates
Before edge 1 the cabinet uses 9 and has no pending entry. After the first 6, staged is 6 and pending is 1. At edge 2, equality with the second entry determines commit before active updates. This follows the table's pre-edge and post-edge ordering. Two matching sixes still violate a policy permitting only 9. Matching handwriting does not authorize a rule change.
CSR means Control and Status Register. Active is the effective value; staged is awaiting confirmation; pending records the first beat. Assume unlocked, no reset and pair policy. Both writes contain 6 (0110); active initially holds 9 (1001).
| edge | Before: active / pending / staged | Action and result after the edge |
|---|---|---|
| 1 | 9 / 0 / 9 | First 6 is staged: active=9, pending=1, staged=6; commit=0 |
| 2 | 9 / 1 / 6 | Second 6 matches: commit=1; active becomes 6 and pending=0 |
| 3 | 6 / 0 / 6 | No write; active remains 6 |
Change the second beat to 7. Equality at edge 2 becomes false, active stays 9, and update error is recorded. Two writes of 6 have no disagreement, but remain unauthorized if trusted policy permits only 9. Pair checking cannot substitute for write authorization.
Matching writes can still be unauthorized
A policy allows only code 9, but two entries of 6 match perfectly. The pair checker says yes; the independent policy ledger says no. Policy-bound adds the permitted-value check. Once locked, neither entry may be accepted. One forced low lockSeen spanning both writes is a separate lock scenario. Repeated wrong data and one lock force have different interpretations; agreement cannot establish full access authority.
The expected configuration in this scenario is 9. Two writes of 6 match each other but not the independent policy reference. Pair-only hardware accepts them. The monitor records an unauthorized update even though no mismatch occurred. Repeating data is not a second policy decision.
A policy-bound variant requires each accepted write to equal an independently justified expected value. The lab gets that value from a trusted harness. A product may instead enforce privilege, lifecycle and permitted field values. Whatever policy is used must protect its source and the final write enable.
Lock has a different job: after configuration is frozen, no writes may enter either phase. Our locked control refuses both beats. For the separate lock fault, one transient force clears lockSeen for the two-beat window. Correct matching data can then change a locked register.
Reset the protocol as well as the value
A reset between confirmations is like a shift change that invalidates the old pending slip. Restoring only active to 9 while keeping pending and staged makes the new shift's single 9 complete an old pair. That is partial-reset: correct data, wrong epoch. Coherent-reset also clears pending, so the new 9 is only a first entry. Shift changes illustrate protocol epochs, not simulated electrical reset skew or every software interruption.
A split reset can reset the active CSR while preserving staged data and pending. After edge 1, our deliberately broken reset target restores only value=9. At edge 2, the preserved pending phase treats the next write as the second beat. With first=second=expected=9, it commits even though reset canceled the first beat in the independent reference. This is a reset-epoch violation with correct data.
This scenario declares the reset target and retained fields explicitly. It is not a single-bit flip or an OpenTitan behavior claim. Reset clears the independent referencePending. Coherent reset also clears DUT pending: edge 2 has pending=false and no commit; afterward that write stages a new first beat. Edge 3 has pending=true and value=9, still the reset value, not a completed update. A product must specify value, phase, lock and error-history reset together.
Interleaved writers create another protocol concern. If interrupt code writes between the pair, the second beat might belong to a different operation. Atomic software access or an explicit transaction protocol can prevent accidental mixing; neither automatically blocks a faulted lock or common wrong data.
Correct data can still belong to the wrong update epoch
Set both entries and expected to 9. The old shift stages the first 9; a partial reset restores active but preserves pending; the next 9 incorrectly commits. The independent ledger has cancelled the old first entry, so referencePending=0. A final display of 9 does not erase an unauthorized update event. Inspect commit and epoch rather than only the final value.
For the partial-reset witness, first=second=expected=9. The fault does not change the data to a wrong value. Edge 1 stages 9. Reset then restores active to 9 but leaves pending=1 and staged=9. The trusted protocol cancels the pre-reset first beat, making referencePending=0. At edge 2, the DUT treats the second 9 as confirmation and sets commit=1. The reference has no complete pair in the new epoch, so this is unauthorized.
With coherent-reset, the same reset also clears pending. The 9 at edge 2 becomes the new epoch's first beat and is only staged. Thus checking “final value equals expected” misses the protocol error. Set all three values to 9 in the laboratory and switch reset targets. Compare pending, referencePending and commit at edge 2, not just active.
Count an update once and inspect the retained value
Try 6 then 7 for mismatch; change only the second to 6 for matching but forbidden data; then use 9 and 9 for lock and reset comparisons. Start the cabinet protocol afresh for each case to separate data, lock, and epoch causes. The 256 pairs enumerate sixteen four-bit values, not 256 physical attacks. Policy-bound alone cannot repair retained pending state.
Choose first=6, second=7, expected=9 and pair policy. Inspect edge 3: value remains 9 and bad is true. Change only second to 6. There is one commit at edge 2, bad stays false, and reference is false. Export this equal-wrong pair before trying policy-bound.
The Node campaign enumerated all 256 four-bit write pairs. Sixteen matched. Fifteen matching values differed from expected=9 and were unauthorized under pair-only policy. Policy-bound rejected those cases. A matching authorized pair committed once; mismatches never changed the active value.
Set both writes and expected to 9 before the lock experiment. Set locked true and target none: no commit. Change only target to lock: correct data now bypasses the trusted locked policy. Then clear locked and compare none, partial-reset and coherent-reset: they commit once, commit outside the reset epoch, and stage a new first beat without committing, respectively. Policy-bound cannot repair retained phase.
Proposed RTL / SVA
The cabinet code may stay unchanged even when a confirmation is accepted, so RTL records csr_commit as an event rather than comparing old and new active. A second signature for 9 still needs an authorized pair from the current epoch. These are reference_pair_authorized and reference_locked. The fragment omits full bus and reset priorities; two signatures on paper do not establish a validated CSR implementation.
Read this lesson's property: Update events and write policy
At the accepting second-entry edge, the auditor checks independent lock and pair records. Copying the cabinet's pending flag would legitimize the cross-shift old slip; reference must invalidate the old epoch independently. Cover uses legitimate 9,9 to reach one update. This preserves a positive path without guaranteeing concurrent entries, readback behavior, or reset safety.
SVA means SystemVerilog Assertions. An assertion checks a rule; it is not the permission gate. At each rising edge outside reset, if accepted_commit (csr_commit in Lesson 9) is one, |-> requires the right-hand condition on that same edge. With no acceptance, this implication raises no authorization failure; positive controls must still demonstrate useful work. disable iff (!rst_n) excludes active reset, without proving reset safety.
Cover seeks one matching path, rather than proving every transaction correct. Reference independently records the expected result in the testbench. The harness supplies inputs, fault budgets and this oracle; DUT means design under test. A two-state model uses only 0/1 logic, excluding X and intra-cycle delay; it does not mean a two-state FSM. Revisit Lesson 1 section 5 for syntax and wiring comparisons. These snippets have not been compiled, so listing a property does not establish a proof.
// An accepted update counts even if wdata equals the old active value.
assign csr_commit = write && !locked_q && policy_ok_i && pending_q
&& (wdata == staged_q);
// Illustrative write rule; a complete RTL module must define priorities.
if (write && !locked_q && policy_ok_i) begin
if (!pending_q) begin staged_q <= wdata; pending_q <= 1; end
else begin
if (wdata == staged_q) committed_q <= wdata;
else update_error_q <= 1;
pending_q <= 0;
end
end
assert property (@(posedge clk) disable iff (!rst_n)
csr_commit |-> !reference_locked && reference_pair_authorized);
cover property (@(posedge clk) disable iff (!rst_n)
reference_pair_authorized && csr_commit);The fragment is uncompiled. It lacks complete reset, bus response, byte-enable and access-type logic. A product must test lock polarity, readback behavior, storage checking, phase integrity and reset skew. The next lesson measures when an error signal actually prevents the accepted operation.
Check your reasoning
For the questions, ask whether the first entry changes the cabinet rule, why two nines can still cross an invalid epoch, and whether locking must reject the first entry too. These concern staging, epoch, and lock; equality alone answers none completely. If a read occurs between entries, its effect on pending needs a separate rule. The analogy supplies no silent default.
1. When does the first write change the active CSR?
It does not; only a matching second beat commits.
2. How many matching write values are wrong when expected is 9?
Fifteen of sixteen.
3. Does lock checking only on the second beat fully specify the protocol?
No; define whether the first beat may stage while locked too.
4. What persists in partial-reset?
Staged data and pending phase.
5. Are update mismatch and storage mismatch the same target?
No; they occur at different phases and need distinct observations.
Engineering wrap-up
Hand over the two-confirmation slip with active and the reset point preserved. The wrap-up continues to separate agreement, authority, and epoch. A screenshot of matching final values is insufficient, and other CSR types do not enter the scope.
- Threat and fault model
- Four-bit RW CSR, two writes at edges 1/2, one update acceptance at edge 2. Common wrong pair, lock force and committed-only reset are separate protocol cases.
Entries occur at edges 1 and 2; forced-low lock and active-only reset are separate runs. Shared wrong data is an input scenario, not necessarily one stored-bit flip.
- Root cause
- Equality cannot establish policy authorization. A vulnerable lock or retained phase can admit an otherwise forbidden update.
Two sixes agree but violate permission for nine only; cross-epoch nines can lack a current pair. Equality establishes neither authority nor epoch.
- Defense
- Protect policy, lock, stage/commit protocol and coherent reset. Keep the old active value on mismatch.
A mismatch keeps old active nine; reset should also cancel the pending slip. Data, lock, and phase need their own integrity; two confirmations do not cover them all.
- Validation
- Node ran all 256 write pairs, lock/reset witnesses and an exactly-once authorized pair. No complete CSR RTL or bus test ran.
Record sixteen-by-sixteen pairs, legitimate 9/9, and an old cross-epoch slip separately. Protocol enumeration is not a full CSR bus run.
- Limits
- This simplified protocol excludes complemented storage, byte masks, read semantics, concurrent writers and physical reset behavior.
The two-entry slip defines no read-cancellation, byte masks, or concurrent signing. Specify those protocols separately rather than guessing hardware behavior from paper.
- Transfer exercise
- Allow a read between the writes. Specify whether it cancels pending, then predict the next write behavior.
Insert a rule read between entries. If the policy cancels pending, the next write is a new first entry; otherwise derive the alternative. The original model has no such read.