The demo below shows a 64-bit word protected by 7 SECDED parity bits (a total of 71 bits stored). Click any data bit to flip it, then press Correct to see the hardware repair it. Flip two bits to see double-error detection.
<!-- Interactive SECDED error-correction demo -->
<p class="hint">A 64-bit word is protected by 7 SECDED parity bits. <b>Click any blue bit</b> to flip it (simulating a hardware error), then press <b>Correct</b> — the hardware computes a syndrome and repairs the exact bad bit. Try flipping <b>two</b> bits to see double-error detection.</p>
<div class="word-label">64 data bits</div>
<div id="data-bits" class="bit-row"></div>
<div class="word-label">7 parity bits (SECDED)</div>
<div id="parity-bits" class="bit-row"></div>
<div class="status" id="status">Click a bit to flip it, then press Correct.</div>
<div class="btns">
<button id="btn-correct" type="button">Correct</button>
<button id="btn-reset" type="button" class="ghost">New word</button>
</div>
/* Base styles */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: .5rem; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.word-label { font-size: .78rem; font-weight: 600; letter-spacing: .04em; color: #5a6a7a;
text-transform: uppercase; margin: .5rem 0 .2rem; }
.bit-row { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: .4rem; }
.bit { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
font: 700 13px ui-monospace, monospace; border-radius: 5px; user-select: none;
border: 1.5px solid transparent; transition: background .12s, color .12s; }
/* Blue = data bits; red = flipped (error injected) */
.bit.data { background: #dce8f3; color: #1d3557; border-color: #bad2e8; cursor: pointer; }
.bit.data:hover { background: #c5d9ec; }
.bit.data.flipped { background: #e63946; color: #fff; border-color: #c92f3c; cursor: pointer; }
/* Green = parity bits; yellow = failing check (syndrome bit set) */
.bit.parity { background: #e8f3e8; color: #1a5c1a; border-color: #b2d9b2; cursor: default; }
.bit.parity.wrong { background: #ffd166; color: #7a4e00; border-color: #e6b800; }
.status { font-size: .95rem; font-weight: 600; margin: .6rem 0 .4rem; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.status.warn { color: #7a4e00; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .2rem; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// SECDED = Single-Error Correction, Double-Error Detection.
// 7 parity bits protect 64 data bits. Each parity bit r covers all
// data positions whose index (1-based) has bit r set.
const DATA_BITS = 64;
const PAR_BITS = 7; // 7 is the minimum needed for 64 data bits (ceil(log2(65)) = 7)
// Parity-check matrix H - column col carries the binary encoding of col.
// Each column is the binary representation of the position index.
function buildH() {
const H = [];
for (let col = 1; col <= DATA_BITS; col++) {
const col_vec = [];
for (let r = 0; r < PAR_BITS; r++) col_vec.push((col >> r) & 1);
H.push(col_vec);
}
return H;
}
const H = buildH();
// Application state
let data = new Uint8Array(DATA_BITS); // start with a random 64-bit word
for (let i = 0; i < DATA_BITS; i++) data[i] = Math.random() < 0.5 ? 0 : 1;
let flippedBits = new Set(); // tracks which positions the user has flipped
// Compute 7 parity bits for a given data array.
function computeParity(d) {
const p = new Uint8Array(PAR_BITS);
for (let col = 0; col < DATA_BITS; col++) {
if (!d[col]) continue;
for (let r = 0; r < PAR_BITS; r++) p[r] ^= H[col][r];
}
return p;
}
// XOR recomputed parity with stored parity to get syndrome.
function computeSyndrome(d, storedParity) {
const recalc = computeParity(d);
const syn = new Uint8Array(PAR_BITS);
for (let r = 0; r < PAR_BITS; r++) syn[r] = recalc[r] ^ storedParity[r];
return syn;
}
// Convert syndrome array to integer syndrome value.
function syndromeValue(syn) {
let v = 0;
for (let r = 0; r < PAR_BITS; r++) v |= (syn[r] << r);
return v;
}
let storedParity = computeParity(data); // Encode - store parity for the original data.
function renderBits() {
const dr = document.getElementById('data-bits');
dr.innerHTML = '';
for (let i = 0; i < DATA_BITS; i++) {
const el = document.createElement('div');
el.className = 'bit data' + (flippedBits.has(i) ? ' flipped' : '');
el.textContent = data[i];
el.title = i18nFmt('Data bit %n%', { n: i });
el.addEventListener('click', () => flipDataBit(i));
dr.appendChild(el);
}
const syn = computeSyndrome(data, storedParity);
const pr = document.getElementById('parity-bits');
pr.innerHTML = '';
const recalc = computeParity(data);
for (let r = 0; r < PAR_BITS; r++) {
const el = document.createElement('div');
const isWrong = recalc[r] !== storedParity[r];
el.className = 'bit parity' + (isWrong ? ' wrong' : '');
el.textContent = storedParity[r];
el.title = i18nFmt('Parity bit %n%', { n: r });
pr.appendChild(el);
}
}
function setStatus(msg, cls) {
const el = document.getElementById('status');
el.textContent = msg;
el.className = 'status ' + cls;
}
function flipDataBit(i) {
data[i] ^= 1;
if (flippedBits.has(i)) flippedBits.delete(i);
else flippedBits.add(i);
renderBits();
const count = flippedBits.size;
if (count === 0) {
setStatus('Click a bit to flip it, then press Correct.', '');
} else if (count === 1) {
setStatus(i18nFmt('Bit %n% is flipped — press Correct to repair it.', { n: [...flippedBits][0] }), 'bad');
} else {
setStatus(i18nFmt('%n% bits flipped — double-error detection only, no correction.', { n: count }), 'bad');
}
}
function doCorrect() {
const syn = computeSyndrome(data, storedParity);
const sv = syndromeValue(syn);
if (sv === 0) {
setStatus('No error detected — all parity checks pass.', 'ok');
return;
}
// Syndrome value = 1-based index of the bad bit.
const pos = sv - 1; // convert to 0-based
if (pos < DATA_BITS) {
data[pos] ^= 1;
flippedBits.clear();
renderBits();
setStatus(i18nFmt('Single-bit error at position %n% detected and corrected.', { n: pos }), 'ok');
} else {
setStatus('Double error detected (DED) — cannot correct, only flag.', 'warn');
}
}
function doReset() {
for (let i = 0; i < DATA_BITS; i++) data[i] = Math.random() < 0.5 ? 0 : 1;
storedParity = computeParity(data);
flippedBits.clear();
setStatus('Click a bit to flip it, then press Correct.', '');
renderBits();
}
document.getElementById('btn-correct').addEventListener('click', doCorrect);
document.getElementById('btn-reset').addEventListener('click', doReset);
renderBits();
<!-- Interactive SECDED error-correction demo -->
<p class="hint">A 64-bit word is protected by 7 SECDED parity bits. <b>Click any blue bit</b> to flip it (simulating a hardware error), then press <b>Correct</b> — the hardware computes a syndrome and repairs the exact bad bit. Try flipping <b>two</b> bits to see double-error detection.</p>
<div class="word-label">64 data bits</div>
<div id="data-bits" class="bit-row"></div>
<div class="word-label">7 parity bits (SECDED)</div>
<div id="parity-bits" class="bit-row"></div>
<div class="status" id="status">Click a bit to flip it, then press Correct.</div>
<div class="btns">
<button id="btn-correct" type="button">Correct</button>
<button id="btn-reset" type="button" class="ghost">New word</button>
</div>
/* Base styles */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: .5rem; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .8rem; line-height: 1.5; }
.word-label { font-size: .78rem; font-weight: 600; letter-spacing: .04em; color: #5a6a7a;
text-transform: uppercase; margin: .5rem 0 .2rem; }
.bit-row { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: .4rem; }
.bit { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
font: 700 13px ui-monospace, monospace; border-radius: 5px; user-select: none;
border: 1.5px solid transparent; transition: background .12s, color .12s; }
/* Blue = data bits; red = flipped (error injected) */
.bit.data { background: #dce8f3; color: #1d3557; border-color: #bad2e8; cursor: pointer; }
.bit.data:hover { background: #c5d9ec; }
.bit.data.flipped { background: #e63946; color: #fff; border-color: #c92f3c; cursor: pointer; }
/* Green = parity bits; yellow = failing check (syndrome bit set) */
.bit.parity { background: #e8f3e8; color: #1a5c1a; border-color: #b2d9b2; cursor: default; }
.bit.parity.wrong { background: #ffd166; color: #7a4e00; border-color: #e6b800; }
.status { font-size: .95rem; font-weight: 600; margin: .6rem 0 .4rem; min-height: 1.4em; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.status.warn { color: #7a4e00; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .2rem; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem;
border: 1px solid #1d3557; background: #1d3557; color: #fff;
border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// SECDED = Single-Error Correction, Double-Error Detection.
// 7 parity bits protect 64 data bits. Each parity bit r covers all
// data positions whose index (1-based) has bit r set.
const DATA_BITS = 64;
const PAR_BITS = 7; // 7 is the minimum needed for 64 data bits (ceil(log2(65)) = 7)
// Parity-check matrix H - column col carries the binary encoding of col.
// Each column is the binary representation of the position index.
function buildH() {
const H = [];
for (let col = 1; col <= DATA_BITS; col++) {
const col_vec = [];
for (let r = 0; r < PAR_BITS; r++) col_vec.push((col >> r) & 1);
H.push(col_vec);
}
return H;
}
const H = buildH();
// Application state
let data = new Uint8Array(DATA_BITS); // start with a random 64-bit word
for (let i = 0; i < DATA_BITS; i++) data[i] = Math.random() < 0.5 ? 0 : 1;
let flippedBits = new Set(); // tracks which positions the user has flipped
// Compute 7 parity bits for a given data array.
function computeParity(d) {
const p = new Uint8Array(PAR_BITS);
for (let col = 0; col < DATA_BITS; col++) {
if (!d[col]) continue;
for (let r = 0; r < PAR_BITS; r++) p[r] ^= H[col][r];
}
return p;
}
// XOR recomputed parity with stored parity to get syndrome.
function computeSyndrome(d, storedParity) {
const recalc = computeParity(d);
const syn = new Uint8Array(PAR_BITS);
for (let r = 0; r < PAR_BITS; r++) syn[r] = recalc[r] ^ storedParity[r];
return syn;
}
// Convert syndrome array to integer syndrome value.
function syndromeValue(syn) {
let v = 0;
for (let r = 0; r < PAR_BITS; r++) v |= (syn[r] << r);
return v;
}
let storedParity = computeParity(data); // Encode - store parity for the original data.
function renderBits() {
const dr = document.getElementById('data-bits');
dr.innerHTML = '';
for (let i = 0; i < DATA_BITS; i++) {
const el = document.createElement('div');
el.className = 'bit data' + (flippedBits.has(i) ? ' flipped' : '');
el.textContent = data[i];
el.title = i18nFmt('Data bit %n%', { n: i });
el.addEventListener('click', () => flipDataBit(i));
dr.appendChild(el);
}
const syn = computeSyndrome(data, storedParity);
const pr = document.getElementById('parity-bits');
pr.innerHTML = '';
const recalc = computeParity(data);
for (let r = 0; r < PAR_BITS; r++) {
const el = document.createElement('div');
const isWrong = recalc[r] !== storedParity[r];
el.className = 'bit parity' + (isWrong ? ' wrong' : '');
el.textContent = storedParity[r];
el.title = i18nFmt('Parity bit %n%', { n: r });
pr.appendChild(el);
}
}
function setStatus(msg, cls) {
const el = document.getElementById('status');
el.textContent = msg;
el.className = 'status ' + cls;
}
function flipDataBit(i) {
data[i] ^= 1;
if (flippedBits.has(i)) flippedBits.delete(i);
else flippedBits.add(i);
renderBits();
const count = flippedBits.size;
if (count === 0) {
setStatus('Click a bit to flip it, then press Correct.', '');
} else if (count === 1) {
setStatus(i18nFmt('Bit %n% is flipped — press Correct to repair it.', { n: [...flippedBits][0] }), 'bad');
} else {
setStatus(i18nFmt('%n% bits flipped — double-error detection only, no correction.', { n: count }), 'bad');
}
}
function doCorrect() {
const syn = computeSyndrome(data, storedParity);
const sv = syndromeValue(syn);
if (sv === 0) {
setStatus('No error detected — all parity checks pass.', 'ok');
return;
}
// Syndrome value = 1-based index of the bad bit.
const pos = sv - 1; // convert to 0-based
if (pos < DATA_BITS) {
data[pos] ^= 1;
flippedBits.clear();
renderBits();
setStatus(i18nFmt('Single-bit error at position %n% detected and corrected.', { n: pos }), 'ok');
} else {
setStatus('Double error detected (DED) — cannot correct, only flag.', 'warn');
}
}
function doReset() {
for (let i = 0; i < DATA_BITS; i++) data[i] = Math.random() < 0.5 ? 0 : 1;
storedParity = computeParity(data);
flippedBits.clear();
setStatus('Click a bit to flip it, then press Correct.', '');
renderBits();
}
document.getElementById('btn-correct').addEventListener('click', doCorrect);
document.getElementById('btn-reset').addEventListener('click', doReset);
renderBits();
Notice the asymmetry: correcting a single flipped bit is automatic and silent — the hardware identifies the exact position from the syndrome and inverts it back. Flipping two bits produces a detectable but uncorrectable syndrome; the memory controller raises a flag and lets the operating system decide what to do (usually: kill the offending process and log the event).
Comments
Loading comments...