Choose two numbers and hit Add. The demo simulates both a ripple-carry adder and a carry-lookahead adder on the same inputs, counting how many gate-delay steps each one needs.
<!-- Carry-lookahead adder demo: compare ripple vs lookahead gate delays -->
<div class="controls">
<label>A = <input id="inA" type="number" min="0" max="255" value="178"/></label>
<label>B = <input id="inB" type="number" min="0" max="255" value="93"/></label>
<button id="btnAdd" type="button">Add</button>
<button id="btnReset" type="button" class="ghost">Reset</button>
</div>
<div class="result-row">
<span class="result-label">Result:</span>
<span id="resultDec" class="result-val">—</span>
<span class="result-bin" id="resultBin"></span>
</div>
<div class="section-title">Ripple-Carry Adder</div>
<div id="rippleBits" class="bit-row"></div>
<div class="delay-bar">
<span class="delay-label">Gate delay:</span>
<span id="rippleDelay" class="delay-num">—</span>
<span class="delay-unit">levels</span>
</div>
<div class="section-title">Carry-Lookahead Adder</div>
<div id="claBits" class="bit-row"></div>
<div class="delay-bar">
<span class="delay-label">Gate delay:</span>
<span id="claDelay" class="delay-num">—</span>
<span class="delay-unit">levels</span>
</div>
<div id="status" class="status"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: .5rem; }
.controls { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-bottom: .6rem; }
label { font-size: .88rem; display: flex; align-items: center; gap: .3rem; }
input[type=number] { width: 68px; padding: .25rem .4rem; border: 1px solid #adb1b8; border-radius: 6px; font: inherit; }
button { font: 600 13px system-ui; padding: .35rem .8rem; border: 1px solid #1d3557; background: #1d3557; color: #fff; border-radius: 7px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.result-row { display: flex; align-items: center; gap: .5rem; margin: .3rem 0 .6rem; font-size: .9rem; }
.result-label { color: #555; }
.result-val { font-weight: 700; font-size: 1.05rem; }
.result-bin { font-family: ui-monospace, monospace; font-size: .78rem; color: #666; }
.section-title { font-size: .8rem; font-weight: 600; color: #1d3557; text-transform: uppercase; letter-spacing: .06em; margin: .5rem 0 .2rem; }
.bit-row { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: .3rem; }
.bit { width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; font: 700 11px ui-monospace, monospace; border-radius: 5px; background: #e8eef3; color: #1d3557; border: 1px solid #cdd9e3; transition: background .25s, color .25s; }
.bit.carry { background: #e63946; color: #fff; border-color: #c92f3c; }
.bit.active { background: #f4a261; color: #fff; border-color: #e07b3e; }
.bit.done { background: #2a9d8f; color: #fff; border-color: #1d7a6e; }
.delay-bar { display: flex; align-items: center; gap: .35rem; font-size: .88rem; color: #444; margin-bottom: .3rem; }
.delay-num { font-weight: 700; font-size: 1.1rem; color: #1d3557; }
.delay-unit { font-size: .78rem; color: #888; }
.status { min-height: 1.3em; font-size: .9rem; font-weight: 600; color: #0a7d33; margin-top: .4rem; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Carry-lookahead adder demo: ripple-carry needs O(n) gate levels; CLA needs O(log n).
// Ripple: each carry waits for the carry below — strictly sequential.
// Lookahead: generate/propagate computed in 1 level; carries resolved via prefix tree.
const BITS = 8; // 8-bit operands for clarity; the principle scales to 64 bits
// Clamp inputs to the valid range [0, 255]
function clamp(val) { return Math.max(0, Math.min((1 << BITS) - 1, val)); }
// Simulate ripple-carry: carry chain depth grows by 2 per bit
// Returns { sum, carries[], delays[] } where delays[i] = gate depth at which bit i carry was ready
function rippleAdd(a, b) {
const carries = new Array(BITS + 1).fill(0);
const delays = new Array(BITS + 1).fill(0);
carries[0] = 0; delays[0] = 0;
for (let i = 0; i < BITS; i++) {
const ai = (a >> i) & 1;
const bi = (b >> i) & 1;
// carry out = generate OR (propagate AND carry in)
carries[i + 1] = (ai & bi) | ((ai | bi) & carries[i]);
delays[i + 1] = delays[i] + 2; // each stage adds 2 gate levels (one AND, one OR)
}
const sum = (a + b) & ((1 << BITS) - 1);
return { sum, carries, delays };
}
// Simulate carry-lookahead: precompute G and P, then resolve all carries
// CLA: compute G[i] and P[i] in 1 level, then resolve all carries in O(log n).
function claAdd(a, b) {
const G = new Array(BITS).fill(0);
const P = new Array(BITS).fill(0);
for (let i = 0; i < BITS; i++) {
G[i] = ((a >> i) & 1) & ((b >> i) & 1); // generate: both bits are 1
P[i] = ((a >> i) & 1) | ((b >> i) & 1); // propagate: at least one bit is 1
}
// Resolve carries using the lookahead formula
const carries = new Array(BITS + 1).fill(0);
const delays = new Array(BITS + 1).fill(0);
carries[0] = 0; delays[0] = 0;
const GP_DEPTH = 1; // G and P each take 1 gate level to compute
for (let i = 0; i < BITS; i++) {
// CLA carry: C[i+1] = G[i] OR (P[i] AND C[i])
carries[i + 1] = G[i] | (P[i] & carries[i]);
// Carry i+1 ready at depth GP_DEPTH + ceil(log2(i+2))
delays[i + 1] = GP_DEPTH + Math.ceil(Math.log2(i + 2));
}
const sum = (a + b) & ((1 << BITS) - 1);
return { sum, carries, delays, G, P };
}
// Render bit cells colored by their carry status
function renderBits(containerId, a, b, carries, delayFn) {
const el = document.getElementById(containerId);
el.innerHTML = '';
for (let i = BITS - 1; i >= 0; i--) {
const ai = (a >> i) & 1;
const bi = (b >> i) & 1;
const ci = carries[i];
const co = carries[i + 1];
const d = document.createElement('div');
d.className = 'bit' + (co ? ' carry' : (ci ? ' active' : ' done'));
d.title = 'bit ' + i + ': a=' + ai + ' b=' + bi + ' cin=' + ci + ' cout=' + co + ' @ ' + delayFn(i + 1) + ' gates';
d.textContent = ai ^ bi ^ ci;
el.appendChild(d);
}
}
function run() {
const aRaw = parseInt(document.getElementById('inA').value, 10);
const bRaw = parseInt(document.getElementById('inB').value, 10);
const a = clamp(isNaN(aRaw) ? 0 : aRaw);
const b = clamp(isNaN(bRaw) ? 0 : bRaw);
const rip = rippleAdd(a, b);
const cla = claAdd(a, b);
const resultDec = document.getElementById('resultDec');
const resultBin = document.getElementById('resultBin');
const rippleDelay = document.getElementById('rippleDelay');
const claDelay = document.getElementById('claDelay');
const status = document.getElementById('status');
const sum = rip.sum;
resultDec.textContent = sum;
resultBin.textContent = '(' + sum.toString(2).padStart(BITS, '0') + ')';
const ripMaxDelay = Math.max(...rip.delays);
const claMaxDelay = Math.max(...cla.delays);
rippleDelay.textContent = ripMaxDelay;
claDelay.textContent = claMaxDelay;
renderBits('rippleBits', a, b, rip.carries, (i) => rip.delays[i]);
renderBits('claBits', a, b, cla.carries, (i) => cla.delays[i]);
const speedup = (ripMaxDelay / Math.max(1, claMaxDelay)).toFixed(1);
status.textContent = i18nFmt('Ripple: %ripple% gate levels · Lookahead: %cla% · Speedup: %factor%×', { ripple: ripMaxDelay, cla: claMaxDelay, factor: speedup });
}
document.getElementById('btnAdd').addEventListener('click', run);
document.getElementById('btnReset').addEventListener('click', () => {
document.getElementById('inA').value = 178;
document.getElementById('inB').value = 93;
document.getElementById('resultDec').textContent = '—';
document.getElementById('resultBin').textContent = '';
document.getElementById('rippleDelay').textContent = '—';
document.getElementById('claDelay').textContent = '—';
document.getElementById('rippleBits').innerHTML = '';
document.getElementById('claBits').innerHTML = '';
document.getElementById('status').textContent = '';
});
// Run on load with default values
run();
<!-- Carry-lookahead adder demo: compare ripple vs lookahead gate delays -->
<div class="controls">
<label>A = <input id="inA" type="number" min="0" max="255" value="178"/></label>
<label>B = <input id="inB" type="number" min="0" max="255" value="93"/></label>
<button id="btnAdd" type="button">Add</button>
<button id="btnReset" type="button" class="ghost">Reset</button>
</div>
<div class="result-row">
<span class="result-label">Result:</span>
<span id="resultDec" class="result-val">—</span>
<span class="result-bin" id="resultBin"></span>
</div>
<div class="section-title">Ripple-Carry Adder</div>
<div id="rippleBits" class="bit-row"></div>
<div class="delay-bar">
<span class="delay-label">Gate delay:</span>
<span id="rippleDelay" class="delay-num">—</span>
<span class="delay-unit">levels</span>
</div>
<div class="section-title">Carry-Lookahead Adder</div>
<div id="claBits" class="bit-row"></div>
<div class="delay-bar">
<span class="delay-label">Gate delay:</span>
<span id="claDelay" class="delay-num">—</span>
<span class="delay-unit">levels</span>
</div>
<div id="status" class="status"></div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; padding: .5rem; }
.controls { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin-bottom: .6rem; }
label { font-size: .88rem; display: flex; align-items: center; gap: .3rem; }
input[type=number] { width: 68px; padding: .25rem .4rem; border: 1px solid #adb1b8; border-radius: 6px; font: inherit; }
button { font: 600 13px system-ui; padding: .35rem .8rem; border: 1px solid #1d3557; background: #1d3557; color: #fff; border-radius: 7px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.result-row { display: flex; align-items: center; gap: .5rem; margin: .3rem 0 .6rem; font-size: .9rem; }
.result-label { color: #555; }
.result-val { font-weight: 700; font-size: 1.05rem; }
.result-bin { font-family: ui-monospace, monospace; font-size: .78rem; color: #666; }
.section-title { font-size: .8rem; font-weight: 600; color: #1d3557; text-transform: uppercase; letter-spacing: .06em; margin: .5rem 0 .2rem; }
.bit-row { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: .3rem; }
.bit { width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; font: 700 11px ui-monospace, monospace; border-radius: 5px; background: #e8eef3; color: #1d3557; border: 1px solid #cdd9e3; transition: background .25s, color .25s; }
.bit.carry { background: #e63946; color: #fff; border-color: #c92f3c; }
.bit.active { background: #f4a261; color: #fff; border-color: #e07b3e; }
.bit.done { background: #2a9d8f; color: #fff; border-color: #1d7a6e; }
.delay-bar { display: flex; align-items: center; gap: .35rem; font-size: .88rem; color: #444; margin-bottom: .3rem; }
.delay-num { font-weight: 700; font-size: 1.1rem; color: #1d3557; }
.delay-unit { font-size: .78rem; color: #888; }
.status { min-height: 1.3em; font-size: .9rem; font-weight: 600; color: #0a7d33; margin-top: .4rem; }
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Carry-lookahead adder demo: ripple-carry needs O(n) gate levels; CLA needs O(log n).
// Ripple: each carry waits for the carry below — strictly sequential.
// Lookahead: generate/propagate computed in 1 level; carries resolved via prefix tree.
const BITS = 8; // 8-bit operands for clarity; the principle scales to 64 bits
// Clamp inputs to the valid range [0, 255]
function clamp(val) { return Math.max(0, Math.min((1 << BITS) - 1, val)); }
// Simulate ripple-carry: carry chain depth grows by 2 per bit
// Returns { sum, carries[], delays[] } where delays[i] = gate depth at which bit i carry was ready
function rippleAdd(a, b) {
const carries = new Array(BITS + 1).fill(0);
const delays = new Array(BITS + 1).fill(0);
carries[0] = 0; delays[0] = 0;
for (let i = 0; i < BITS; i++) {
const ai = (a >> i) & 1;
const bi = (b >> i) & 1;
// carry out = generate OR (propagate AND carry in)
carries[i + 1] = (ai & bi) | ((ai | bi) & carries[i]);
delays[i + 1] = delays[i] + 2; // each stage adds 2 gate levels (one AND, one OR)
}
const sum = (a + b) & ((1 << BITS) - 1);
return { sum, carries, delays };
}
// Simulate carry-lookahead: precompute G and P, then resolve all carries
// CLA: compute G[i] and P[i] in 1 level, then resolve all carries in O(log n).
function claAdd(a, b) {
const G = new Array(BITS).fill(0);
const P = new Array(BITS).fill(0);
for (let i = 0; i < BITS; i++) {
G[i] = ((a >> i) & 1) & ((b >> i) & 1); // generate: both bits are 1
P[i] = ((a >> i) & 1) | ((b >> i) & 1); // propagate: at least one bit is 1
}
// Resolve carries using the lookahead formula
const carries = new Array(BITS + 1).fill(0);
const delays = new Array(BITS + 1).fill(0);
carries[0] = 0; delays[0] = 0;
const GP_DEPTH = 1; // G and P each take 1 gate level to compute
for (let i = 0; i < BITS; i++) {
// CLA carry: C[i+1] = G[i] OR (P[i] AND C[i])
carries[i + 1] = G[i] | (P[i] & carries[i]);
// Carry i+1 ready at depth GP_DEPTH + ceil(log2(i+2))
delays[i + 1] = GP_DEPTH + Math.ceil(Math.log2(i + 2));
}
const sum = (a + b) & ((1 << BITS) - 1);
return { sum, carries, delays, G, P };
}
// Render bit cells colored by their carry status
function renderBits(containerId, a, b, carries, delayFn) {
const el = document.getElementById(containerId);
el.innerHTML = '';
for (let i = BITS - 1; i >= 0; i--) {
const ai = (a >> i) & 1;
const bi = (b >> i) & 1;
const ci = carries[i];
const co = carries[i + 1];
const d = document.createElement('div');
d.className = 'bit' + (co ? ' carry' : (ci ? ' active' : ' done'));
d.title = 'bit ' + i + ': a=' + ai + ' b=' + bi + ' cin=' + ci + ' cout=' + co + ' @ ' + delayFn(i + 1) + ' gates';
d.textContent = ai ^ bi ^ ci;
el.appendChild(d);
}
}
function run() {
const aRaw = parseInt(document.getElementById('inA').value, 10);
const bRaw = parseInt(document.getElementById('inB').value, 10);
const a = clamp(isNaN(aRaw) ? 0 : aRaw);
const b = clamp(isNaN(bRaw) ? 0 : bRaw);
const rip = rippleAdd(a, b);
const cla = claAdd(a, b);
const resultDec = document.getElementById('resultDec');
const resultBin = document.getElementById('resultBin');
const rippleDelay = document.getElementById('rippleDelay');
const claDelay = document.getElementById('claDelay');
const status = document.getElementById('status');
const sum = rip.sum;
resultDec.textContent = sum;
resultBin.textContent = '(' + sum.toString(2).padStart(BITS, '0') + ')';
const ripMaxDelay = Math.max(...rip.delays);
const claMaxDelay = Math.max(...cla.delays);
rippleDelay.textContent = ripMaxDelay;
claDelay.textContent = claMaxDelay;
renderBits('rippleBits', a, b, rip.carries, (i) => rip.delays[i]);
renderBits('claBits', a, b, cla.carries, (i) => cla.delays[i]);
const speedup = (ripMaxDelay / Math.max(1, claMaxDelay)).toFixed(1);
status.textContent = i18nFmt('Ripple: %ripple% gate levels · Lookahead: %cla% · Speedup: %factor%×', { ripple: ripMaxDelay, cla: claMaxDelay, factor: speedup });
}
document.getElementById('btnAdd').addEventListener('click', run);
document.getElementById('btnReset').addEventListener('click', () => {
document.getElementById('inA').value = 178;
document.getElementById('inB').value = 93;
document.getElementById('resultDec').textContent = '—';
document.getElementById('resultBin').textContent = '';
document.getElementById('rippleDelay').textContent = '—';
document.getElementById('claDelay').textContent = '—';
document.getElementById('rippleBits').innerHTML = '';
document.getElementById('claBits').innerHTML = '';
document.getElementById('status').textContent = '';
});
// Run on load with default values
run();
Watch the carry chain light up from right to left in the ripple adder — each stage waits for its neighbor. In the lookahead adder all carries are resolved after just two logic levels (generate/propagate, then combine), regardless of how many bits are involved.
Comments
Loading comments...