The demo below shows a two-instruction sequence with a classic RAW data hazard. Instruction 1 computes a value; Instruction 2 immediately reads that register.
<!-- Pipeline hazard visualizer — two instructions with a RAW data hazard -->
<div class="controls">
<label class="toggle-label">
<input type="checkbox" id="fwd-toggle">
<span class="toggle-track"><span class="toggle-thumb"></span></span>
<span class="toggle-text">Forwarding ON</span>
</label>
<button id="btn-step" type="button">Step →</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
<div id="pipeline-grid" class="pipeline-grid"></div>
<div id="info-box" class="info-box">Press "Step" to advance the pipeline one cycle at a time.</div>
/* Pipeline grid styles — stage colours match textbook conventions */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.controls { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .8rem; }
.toggle-label { display: flex; align-items: center; gap: .45rem; cursor: pointer; user-select: none; font-size: .9rem; }
.toggle-track { position: relative; width: 36px; height: 20px; background: #cdd; border-radius: 10px; transition: background .2s; flex-shrink: 0; }
.toggle-thumb { position: absolute; top: 3px; left: 3px; width: 14px; height: 14px; background: #fff; border-radius: 50%; transition: left .2s; }
#fwd-toggle:checked + .toggle-track { background: #1d3557; }
#fwd-toggle:checked + .toggle-track .toggle-thumb { left: 19px; }
#fwd-toggle { display: none; }
button { font: 600 13px system-ui; padding: .4rem .85rem; border: 1px solid #1d3557; background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.pipeline-grid { display: grid; grid-template-columns: 100px repeat(8, 1fr); gap: 3px; }
.gh { /* grid header cell */ font-size: .72rem; font-weight: 700; color: #555; text-align: center; padding: 3px 0; }
.row-label { font-size: .8rem; font-weight: 600; color: #333; display: flex; align-items: center; padding: 0 4px; }
.cell { height: 36px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: .75rem; font-weight: 700; color: #fff; text-align: center; }
.cell-if { background: #4361ee; }
.cell-id { background: #3a86ff; }
.cell-ex { background: #2a9d8f; }
.cell-mem { background: #e9c46a; color: #333; }
.cell-wb { background: #f4a261; color: #333; }
.cell-bub { background: #c0c5ce; color: #555; }
.cell-empty { background: transparent; }
.info-box { margin-top: .7rem; font-size: .9rem; line-height: 1.5; padding: .5rem .7rem; background: #f0f4f8; border-left: 3px solid #1d3557; border-radius: 4px; min-height: 2.5em; }
.info-box.ok { border-color: #2a9d8f; background: #e8f8f5; }
.info-box.bad { border-color: #e76f51; background: #fdf0ec; }
.info-box.done { border-color: #4361ee; background: #eef0fd; }
.legend { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .6rem; font-size: .72rem; }
.leg-item { display: flex; align-items: center; gap: .25rem; }
.leg-swatch { width: 12px; height: 12px; border-radius: 3px; flex-shrink: 0; }
// Pipeline hazard demo — RAW (Read-After-Write) dependency between I1 and I2
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Stage names and CSS classes in pipeline order
const STAGE_LABELS = ['IF', 'ID', 'EX', 'MEM', 'WB'];
const STAGE_CLASS = ['cell-if', 'cell-id', 'cell-ex', 'cell-mem', 'cell-wb'];
const NUM_CYCLES = 8; // total columns in the grid
// instruction display labels (read from injected spans)
const instrLabel = () => [document.getElementById('lbl-i1').textContent, document.getElementById('lbl-i2').textContent];
// Compute per-instruction stage assignments for each cycle (0-indexed stage)
function buildSchedule(forwarding) {
// I1 starts at cycle 1, I2 at cycle 2 — standard 2-instruction overlap
// I1: ADD r1, r2, r3 -> IF ID EX MEM WB
// I2: SUB r4, r1, r5 -> IF ID [stall x2] EX MEM WB (no forwarding)
// IF ID EX MEM WB (with forwarding — no stall)
if (forwarding) {
return [
[0,1,2,3,4,null,null,null], // I1 flows normally in both cases
[null,0,1,2,3,4,null,null] // I2 has no stall — forwarding routes EX result of I1 directly to EX of I2
];
} else {
return [
[0,1,2,3,4,null,null,null], // I1 flows normally
[null,0,1,null,null,2,3,4] // I2 stalls 2 cycles (bubbles at cols 2 and 3) then continues
];
}
}
let cycle = 0;
let forwarding = false;
function getMsg(cyc, fwd) {
if (cyc === 0) return { text: 'Press "Step" to advance the pipeline one cycle at a time.', cls: '' };
// pick an explanatory message for the current cycle and forwarding state
const hasStall = !fwd;
if (!fwd && (cyc === 4 || cyc === 5)) {
return { text: 'Bubble! The pipeline is stalled — I2 cannot read r1 until I1 finishes EX.', cls: 'bad' };
}
if (!fwd && cyc === 6) {
return { text: 'I2 finally reaches EX and reads r1 from the register file (written in the previous cycle).', cls: 'bad' };
}
if (fwd && cyc === 4) {
return { text: 'Forwarding active — I2 reads r1 directly from I1’s EX output. No stall!', cls: 'ok' };
}
if (cyc >= NUM_CYCLES) {
if (fwd) return { text: i18nFmt('Done. With forwarding: %cycles% active cycles, 0 stalls.', { cycles: 6 }), cls: 'done' };
return { text: i18nFmt('Done. Without forwarding: %cycles% active cycles (including 2 stall bubbles).', { cycles: 8 }), cls: 'done' };
}
return { text: i18nFmt('Cycle %n% — advancing…', { n: cyc }), cls: '' };
}
function render() {
const labels = [
document.getElementById('lbl-i1') ? document.getElementById('lbl-i1').textContent : 'I1: ADD r1, r2, r3',
document.getElementById('lbl-i2') ? document.getElementById('lbl-i2').textContent : 'I2: SUB r4, r1, r5'
];
const sched = buildSchedule(forwarding);
const grid = document.getElementById('pipeline-grid');
grid.innerHTML = '';
// column headers (Cycle 1 … Cycle N)
grid.appendChild(Object.assign(document.createElement('div'), { className: 'gh' }));
for (let c = 1; c <= NUM_CYCLES; c++) {
const h = document.createElement('div');
h.className = 'gh';
h.textContent = i18nFmt('C%n%', { n: c });
grid.appendChild(h);
}
// one row per instruction
for (let i = 0; i < 2; i++) {
const rowLbl = document.createElement('div');
rowLbl.className = 'row-label';
rowLbl.textContent = labels[i] || (i === 0 ? 'I1: ADD r1, r2, r3' : 'I2: SUB r4, r1, r5');
grid.appendChild(rowLbl);
for (let c = 0; c < NUM_CYCLES; c++) {
const cell = document.createElement('div');
const stageIdx = sched[i][c];
if (stageIdx === null) {
// mark slot as bubble if it falls in the stall window
const isBubble = (i === 1 && !forwarding && (c === 3 || c === 4));
cell.className = 'cell ' + (isBubble ? 'cell-bub' : 'cell-empty');
cell.textContent = isBubble ? 'BUBBLE' : '';
} else {
cell.className = 'cell ' + STAGE_CLASS[stageIdx];
cell.textContent = STAGE_LABELS[stageIdx];
}
// outline the current cycle column
if (c + 1 === cycle) cell.style.outline = '2px solid #333';
grid.appendChild(cell);
}
}
// write the status message below the grid
const { text, cls } = getMsg(cycle, forwarding);
const box = document.getElementById('info-box');
box.textContent = text;
box.className = 'info-box ' + cls;
document.getElementById('btn-step').disabled = (cycle >= NUM_CYCLES);
}
function buildLegend() {
// append a colour-keyed legend below the grid
const items = [
{ cls: 'cell-if', lbl: 'IF – Fetch' },
{ cls: 'cell-id', lbl: 'ID – Decode' },
{ cls: 'cell-ex', lbl: 'EX – Execute' },
{ cls: 'cell-mem', lbl: 'MEM – Memory' },
{ cls: 'cell-wb', lbl: 'WB – Write-back' },
{ cls: 'cell-bub', lbl: 'BUBBLE – Stall' }
];
const leg = document.createElement('div');
leg.className = 'legend';
items.forEach(({ cls, lbl }) => {
const item = document.createElement('span');
item.className = 'leg-item';
item.innerHTML = '<span class="leg-swatch ' + cls + '"></span>' + lbl;
leg.appendChild(item);
});
document.body.appendChild(leg);
}
// inject instruction label line between controls and grid
function injectLabels() {
const ctrl = document.querySelector('.controls');
const p = document.createElement('p');
p.style.cssText = 'font-size:.8rem;color:#555;margin:.2rem 0 .4rem;width:100%';
p.innerHTML = '<span id="lbl-i1">I1: ADD r1, r2, r3</span> | <span id="lbl-i2">I2: SUB r4, r1, r5</span>';
ctrl.after(p);
}
document.getElementById('fwd-toggle').addEventListener('change', function() {
forwarding = this.checked;
cycle = 0;
render();
});
document.getElementById('btn-step').addEventListener('click', function() {
if (cycle < NUM_CYCLES) { cycle++; render(); }
});
document.getElementById('btn-reset').addEventListener('click', function() {
cycle = 0; render();
});
injectLabels();
buildLegend();
render();
<!-- Pipeline hazard visualizer — two instructions with a RAW data hazard -->
<div class="controls">
<label class="toggle-label">
<input type="checkbox" id="fwd-toggle">
<span class="toggle-track"><span class="toggle-thumb"></span></span>
<span class="toggle-text">Forwarding ON</span>
</label>
<button id="btn-step" type="button">Step →</button>
<button id="btn-reset" type="button" class="ghost">Reset</button>
</div>
<div id="pipeline-grid" class="pipeline-grid"></div>
<div id="info-box" class="info-box">Press "Step" to advance the pipeline one cycle at a time.</div>
/* Pipeline grid styles — stage colours match textbook conventions */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.controls { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .8rem; }
.toggle-label { display: flex; align-items: center; gap: .45rem; cursor: pointer; user-select: none; font-size: .9rem; }
.toggle-track { position: relative; width: 36px; height: 20px; background: #cdd; border-radius: 10px; transition: background .2s; flex-shrink: 0; }
.toggle-thumb { position: absolute; top: 3px; left: 3px; width: 14px; height: 14px; background: #fff; border-radius: 50%; transition: left .2s; }
#fwd-toggle:checked + .toggle-track { background: #1d3557; }
#fwd-toggle:checked + .toggle-track .toggle-thumb { left: 19px; }
#fwd-toggle { display: none; }
button { font: 600 13px system-ui; padding: .4rem .85rem; border: 1px solid #1d3557; background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
.pipeline-grid { display: grid; grid-template-columns: 100px repeat(8, 1fr); gap: 3px; }
.gh { /* grid header cell */ font-size: .72rem; font-weight: 700; color: #555; text-align: center; padding: 3px 0; }
.row-label { font-size: .8rem; font-weight: 600; color: #333; display: flex; align-items: center; padding: 0 4px; }
.cell { height: 36px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: .75rem; font-weight: 700; color: #fff; text-align: center; }
.cell-if { background: #4361ee; }
.cell-id { background: #3a86ff; }
.cell-ex { background: #2a9d8f; }
.cell-mem { background: #e9c46a; color: #333; }
.cell-wb { background: #f4a261; color: #333; }
.cell-bub { background: #c0c5ce; color: #555; }
.cell-empty { background: transparent; }
.info-box { margin-top: .7rem; font-size: .9rem; line-height: 1.5; padding: .5rem .7rem; background: #f0f4f8; border-left: 3px solid #1d3557; border-radius: 4px; min-height: 2.5em; }
.info-box.ok { border-color: #2a9d8f; background: #e8f8f5; }
.info-box.bad { border-color: #e76f51; background: #fdf0ec; }
.info-box.done { border-color: #4361ee; background: #eef0fd; }
.legend { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .6rem; font-size: .72rem; }
.leg-item { display: flex; align-items: center; gap: .25rem; }
.leg-swatch { width: 12px; height: 12px; border-radius: 3px; flex-shrink: 0; }
// Pipeline hazard demo — RAW (Read-After-Write) dependency between I1 and I2
const i18nFmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Stage names and CSS classes in pipeline order
const STAGE_LABELS = ['IF', 'ID', 'EX', 'MEM', 'WB'];
const STAGE_CLASS = ['cell-if', 'cell-id', 'cell-ex', 'cell-mem', 'cell-wb'];
const NUM_CYCLES = 8; // total columns in the grid
// instruction display labels (read from injected spans)
const instrLabel = () => [document.getElementById('lbl-i1').textContent, document.getElementById('lbl-i2').textContent];
// Compute per-instruction stage assignments for each cycle (0-indexed stage)
function buildSchedule(forwarding) {
// I1 starts at cycle 1, I2 at cycle 2 — standard 2-instruction overlap
// I1: ADD r1, r2, r3 -> IF ID EX MEM WB
// I2: SUB r4, r1, r5 -> IF ID [stall x2] EX MEM WB (no forwarding)
// IF ID EX MEM WB (with forwarding — no stall)
if (forwarding) {
return [
[0,1,2,3,4,null,null,null], // I1 flows normally in both cases
[null,0,1,2,3,4,null,null] // I2 has no stall — forwarding routes EX result of I1 directly to EX of I2
];
} else {
return [
[0,1,2,3,4,null,null,null], // I1 flows normally
[null,0,1,null,null,2,3,4] // I2 stalls 2 cycles (bubbles at cols 2 and 3) then continues
];
}
}
let cycle = 0;
let forwarding = false;
function getMsg(cyc, fwd) {
if (cyc === 0) return { text: 'Press "Step" to advance the pipeline one cycle at a time.', cls: '' };
// pick an explanatory message for the current cycle and forwarding state
const hasStall = !fwd;
if (!fwd && (cyc === 4 || cyc === 5)) {
return { text: 'Bubble! The pipeline is stalled — I2 cannot read r1 until I1 finishes EX.', cls: 'bad' };
}
if (!fwd && cyc === 6) {
return { text: 'I2 finally reaches EX and reads r1 from the register file (written in the previous cycle).', cls: 'bad' };
}
if (fwd && cyc === 4) {
return { text: 'Forwarding active — I2 reads r1 directly from I1’s EX output. No stall!', cls: 'ok' };
}
if (cyc >= NUM_CYCLES) {
if (fwd) return { text: i18nFmt('Done. With forwarding: %cycles% active cycles, 0 stalls.', { cycles: 6 }), cls: 'done' };
return { text: i18nFmt('Done. Without forwarding: %cycles% active cycles (including 2 stall bubbles).', { cycles: 8 }), cls: 'done' };
}
return { text: i18nFmt('Cycle %n% — advancing…', { n: cyc }), cls: '' };
}
function render() {
const labels = [
document.getElementById('lbl-i1') ? document.getElementById('lbl-i1').textContent : 'I1: ADD r1, r2, r3',
document.getElementById('lbl-i2') ? document.getElementById('lbl-i2').textContent : 'I2: SUB r4, r1, r5'
];
const sched = buildSchedule(forwarding);
const grid = document.getElementById('pipeline-grid');
grid.innerHTML = '';
// column headers (Cycle 1 … Cycle N)
grid.appendChild(Object.assign(document.createElement('div'), { className: 'gh' }));
for (let c = 1; c <= NUM_CYCLES; c++) {
const h = document.createElement('div');
h.className = 'gh';
h.textContent = i18nFmt('C%n%', { n: c });
grid.appendChild(h);
}
// one row per instruction
for (let i = 0; i < 2; i++) {
const rowLbl = document.createElement('div');
rowLbl.className = 'row-label';
rowLbl.textContent = labels[i] || (i === 0 ? 'I1: ADD r1, r2, r3' : 'I2: SUB r4, r1, r5');
grid.appendChild(rowLbl);
for (let c = 0; c < NUM_CYCLES; c++) {
const cell = document.createElement('div');
const stageIdx = sched[i][c];
if (stageIdx === null) {
// mark slot as bubble if it falls in the stall window
const isBubble = (i === 1 && !forwarding && (c === 3 || c === 4));
cell.className = 'cell ' + (isBubble ? 'cell-bub' : 'cell-empty');
cell.textContent = isBubble ? 'BUBBLE' : '';
} else {
cell.className = 'cell ' + STAGE_CLASS[stageIdx];
cell.textContent = STAGE_LABELS[stageIdx];
}
// outline the current cycle column
if (c + 1 === cycle) cell.style.outline = '2px solid #333';
grid.appendChild(cell);
}
}
// write the status message below the grid
const { text, cls } = getMsg(cycle, forwarding);
const box = document.getElementById('info-box');
box.textContent = text;
box.className = 'info-box ' + cls;
document.getElementById('btn-step').disabled = (cycle >= NUM_CYCLES);
}
function buildLegend() {
// append a colour-keyed legend below the grid
const items = [
{ cls: 'cell-if', lbl: 'IF – Fetch' },
{ cls: 'cell-id', lbl: 'ID – Decode' },
{ cls: 'cell-ex', lbl: 'EX – Execute' },
{ cls: 'cell-mem', lbl: 'MEM – Memory' },
{ cls: 'cell-wb', lbl: 'WB – Write-back' },
{ cls: 'cell-bub', lbl: 'BUBBLE – Stall' }
];
const leg = document.createElement('div');
leg.className = 'legend';
items.forEach(({ cls, lbl }) => {
const item = document.createElement('span');
item.className = 'leg-item';
item.innerHTML = '<span class="leg-swatch ' + cls + '"></span>' + lbl;
leg.appendChild(item);
});
document.body.appendChild(leg);
}
// inject instruction label line between controls and grid
function injectLabels() {
const ctrl = document.querySelector('.controls');
const p = document.createElement('p');
p.style.cssText = 'font-size:.8rem;color:#555;margin:.2rem 0 .4rem;width:100%';
p.innerHTML = '<span id="lbl-i1">I1: ADD r1, r2, r3</span> | <span id="lbl-i2">I2: SUB r4, r1, r5</span>';
ctrl.after(p);
}
document.getElementById('fwd-toggle').addEventListener('change', function() {
forwarding = this.checked;
cycle = 0;
render();
});
document.getElementById('btn-step').addEventListener('click', function() {
if (cycle < NUM_CYCLES) { cycle++; render(); }
});
document.getElementById('btn-reset').addEventListener('click', function() {
cycle = 0; render();
});
injectLabels();
buildLegend();
render();
Comments
Loading comments...