This lesson has one job: pick the cheapest code that meets each memory's failure budget. Parity adds one bit per byte or word and detects any single-bit flip but corrects nothing and misses every even-bit upset. SECDED, single-error correct double-error detect, corrects any one-bit error on the fly and flags two-bit errors as uncorrectable, at the price of extra check bits and a slightly longer read path. Choosing parity where correction matters buys an interrupt storm; choosing SECDED where a parity flag would do buys area and timing for no benefit.
The choice follows the failure model. The ROM is written once and read forever: a parity bit per word catches manufacturing and ageing defects (a mask ROM does not decay from soft errors the way an SRAM does), and there is nothing to correct in flight because the contents never change. A boot-time sweep of all 4,096 words would cost about 384 microseconds at 18 CPU clocks per word, which is not in the 12.581 ms boot ledger and would have to be added to it; the alternative is a signature compare done once at production test. SRAM0 holds the retained image across sleep: a single-bit upset there corrupts the wake path, so SECDED with on-the-fly correction is the natural teaching choice. SRAM1 holds DMA working data that software can regenerate by rerunning the job: parity with an error interrupt, or SECDED if the system cannot tolerate reruns. The generic theory, including encode and syndrome circuits, belongs to ECC and SECDED and Partial Writes and Scrubbing; the integration view here is which memory gets which code and what that costs.
The price table below uses NanoSoC teaching values: a 32-bit data word needs 7 check bits for SECDED and 4 for byte parity, so a 64 KB bank grows by the check-bit ratio. NanoSoC's SRAMs are plain 32-bit macros, so SECDED means 39-bit-wide rows, a different macro or a compiled one. A byte store to a SECDED-protected 32-bit word also becomes a read-modify-write, because the check bits cover the whole word: on a single-port bank that costs an extra cycle per partial write. Correction happens in the read pipeline with no software involvement; detection of an uncorrectable error raises the reporting path of the next lesson.
data_bits = 32
secded_bits = 7
parity_bits = 4
words = 64 * 1024 // 4
for name, check in (("parity", parity_bits), ("SECDED", secded_bits)):
overhead = check / data_bits * 100
extra_kb = words * check / 8 / 1024
print(f"{name}: {check} check bits, {overhead:.1f}% overhead, +{extra_kb:.2f} KB per bank")
assert extra_kb == words * check / 8192
banks_total_kb = 128 + 128 * secded_bits / data_bits
assert abs(banks_total_kb - 156.0) < 0.01, banks_total_kb
assert data_bits + secded_bits == 39, "SECDED rows are 39 bits wide"
assert abs(secded_bits / data_bits * 100 - 21.875) < 1e-9
print("PASS: SECDED costs 21.9% per bank, 156 KB for both banks")Every memory names its code or states why it has none, with the failure model that justifies the choice.
Check-bit storage is budgeted in area and the correction latency is budgeted in the read path.
Partial writes have a defined read-modify-write rule so a byte store never corrupts the check bits of its neighbours.
Inject single-bit and double-bit faults per word in simulation and confirm the contracted behaviour: silent correction of the first, a flagged uncorrectable for the second, and no miscorrection. Cover partial writes explicitly: write one byte of a protected word and read back the whole word plus its check bits. Initialisation coverage belongs to the scrubbing lesson.
There is no write path to corrupt and no in-flight data to rescue: parity flags a defective or aged bit when that word is fetched, and the contents never change, so there is nothing to correct in flight. Correction hardware would sit idle forever. A boot sweep is optional and is not free: it adds time that the boot ledger does not contain.