Show description
CSE 230: Datapath Architect
CSE 230: Datapath Architect
Datapath Architect CSE 230
Single-Cycle MIPS Simulator
Instruction Type
R-Type (add, sub, and, or)
Load Word (lw)
Store Word (sw)
Branch Equal (beq)
Operation / Function
Registers (rs, rt, rd)
Immediate / Offset (Hex)
add $t0, $t1, $t2
EXECUTE CYCLE
RESET MACHINE
Theory: In a single-cycle datapath, the entire instruction (Fetch, Decode, Execute, Memory, Writeback) completes in one clock tick. The Control Unit sets the multiplexers based on the Opcode.
PC
IM
RegFile
Control
ALU Ctrl
ALU
DM
Control Signals
RegDst0
ALUSrc0
MemtoReg0
RegWrite0
MemRead0
MemWrite0
Branch0
ALUOp00
Register File
Reg
Val (Dec)
CSE 230: Datapath Architect
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSE 230: Datapath Architect</title>
<style>
:root {
--bg-dark: #0f172a;
--bg-panel: rgba(30, 41, 59, 0.7);
--text-main: #e2e8f0;
--text-muted: #94a3b8;
--accent-cyan: #06b6d4;
--accent-purple: #a855f7;
--accent-green: #22c55e;
--accent-red: #ef4444;
--wire-inactive: #334155;
--font-ui: 'Inter', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;
}
* { box-sizing: border-box; }
body {
margin: 0;
padding: 0;
background-color: var(--bg-dark);
color: var(--text-main);
font-family: var(--font-ui);
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
}
/* --- Header --- */
header {
padding: 1rem 2rem;
border-bottom: 1px solid rgba(255,255,255,0.1);
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(15, 23, 42, 0.9);
backdrop-filter: blur(10px);
z-index: 10;
}
h1 { margin: 0; font-size: 1.25rem; letter-spacing: 1px; color: var(--accent-cyan); text-transform: uppercase; }
.badge { font-size: 0.75rem; background: var(--accent-purple); color: white; padding: 2px 8px; border-radius: 4px; margin-left: 10px; }
/* --- Main Layout --- */
.dashboard {
display: grid;
grid-template-columns: 300px 1fr 300px;
flex: 1;
overflow: hidden;
}
/* --- Sidebar: Controls --- */
.sidebar-left {
padding: 1.5rem;
background: var(--bg-panel);
border-right: 1px solid rgba(255,255,255,0.05);
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.control-group { display: flex; flex-direction: column; gap: 0.5rem; }
label { font-size: 0.85rem; color: var(--text-muted); font-weight: 600; text-transform: uppercase; }
select, button, input {
background: rgba(0,0,0,0.3);
border: 1px solid rgba(255,255,255,0.1);
color: var(--text-main);
padding: 0.75rem;
border-radius: 6px;
font-family: var(--font-mono);
font-size: 0.9rem;
cursor: pointer;
transition: all 0.2s;
}
select:focus, input:focus { outline: none; border-color: var(--accent-cyan); }
button.primary {
background: linear-gradient(45deg, var(--accent-cyan), #3b82f6);
border: none;
color: black;
font-weight: bold;
box-shadow: 0 0 15px rgba(6, 182, 212, 0.3);
}
button.primary:hover { box-shadow: 0 0 25px rgba(6, 182, 212, 0.5); transform: translateY(-1px); }
.instruction-preview {
font-family: var(--font-mono);
background: black;
padding: 1rem;
border-radius: 6px;
border-left: 3px solid var(--accent-green);
font-size: 0.9rem;
}
/* --- Center: Visualization --- */
.viz-stage {
position: relative;
background-image: radial-gradient(circle at 50% 50%, #1e293b 1px, transparent 1px);
background-size: 20px 20px;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden; /* For pan/zoom if added later */
}
/* SVG Styles */
svg {
width: 95%;
height: 95%;
max-width: 1000px;
}
.component { fill: rgba(30, 41, 59, 0.9); stroke: var(--text-muted); stroke-width: 2; }
.component-text { fill: var(--text-main); font-family: var(--font-ui); font-size: 12px; font-weight: bold; text-anchor: middle; dominant-baseline: middle; pointer-events: none; }
.wire { fill: none; stroke: var(--wire-inactive); stroke-width: 2; transition: stroke 0.3s, stroke-width 0.3s; }
.wire.active { stroke: var(--accent-cyan); stroke-width: 3; filter: drop-shadow(0 0 5px var(--accent-cyan)); }
.wire.control { stroke: var(--wire-inactive); stroke-dasharray: 4; }
.wire.control.active { stroke: var(--accent-purple); filter: drop-shadow(0 0 5px var(--accent-purple)); animation: flow 1s linear infinite; }
.mux { fill: #334155; stroke: var(--text-muted); }
@keyframes flow {
to { stroke-dashoffset: -20; }
}
/* --- Sidebar: Registers & Signals --- */
.sidebar-right {
padding: 1.5rem;
background: var(--bg-panel);
border-left: 1px solid rgba(255,255,255,0.05);
overflow-y: auto;
font-family: var(--font-mono);
}
.data-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; margin-bottom: 2rem; }
.data-table th { text-align: left; color: var(--accent-cyan); padding-bottom: 0.5rem; border-bottom: 1px solid rgba(255,255,255,0.1); }
.data-table td { padding: 0.4rem 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
.data-table .val-changed { color: var(--accent-green); animation: flash 1s; }
.signal-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
.signal-box { background: rgba(0,0,0,0.3); padding: 0.5rem; border-radius: 4px; text-align: center; border: 1px solid transparent; }
.signal-box.active { border-color: var(--accent-purple); color: var(--accent-purple); box-shadow: 0 0 10px rgba(168, 85, 247, 0.2); }
.signal-label { display: block; font-size: 0.7rem; color: var(--text-muted); }
.signal-val { font-weight: bold; font-size: 0.9rem; }
@keyframes flash {
0% { color: white; text-shadow: 0 0 10px white; }
100% { color: var(--accent-green); }
}
/* Tooltip */
.tooltip {
position: absolute;
background: rgba(0,0,0,0.9);
border: 1px solid var(--accent-cyan);
padding: 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s;
z-index: 100;
}
</style>
</head>
<body>
<header>
<div>
<h1>Datapath Architect <span class="badge">CSE 230</span></h1>
</div>
<div style="font-size: 0.8rem; color: var(--text-muted);">
Single-Cycle MIPS Simulator
</div>
</header>
<div class="dashboard">
<div class="sidebar-left">
<div class="control-group">
<label>Instruction Type</label>
<select id="instrType">
<option value="R-Type">R-Type (add, sub, and, or)</option>
<option value="LW">Load Word (lw)</option>
<option value="SW">Store Word (sw)</option>
<option value="BEQ">Branch Equal (beq)</option>
</select>
</div>
<div class="control-group">
<label>Operation / Function</label>
<select id="opcodeSelect">
</select>
</div>
<div class="control-group">
<label>Registers (rs, rt, rd)</label>
<div style="display: flex; gap: 5px;">
<select id="rs" class="reg-select"></select>
<select id="rt" class="reg-select"></select>
<select id="rd" class="reg-select"></select>
</div>
</div>
<div class="control-group" id="immGroup" style="display:none;">
<label>Immediate / Offset (Hex)</label>
<input type="text" id="immediate" value="0x0004" maxlength="6">
</div>
<div class="instruction-preview" id="asmPreview">
add $t0, $t1, $t2
</div>
<button class="primary" onclick="executeCycle()">EXECUTE CYCLE</button>
<button onclick="resetMachine()">RESET MACHINE</button>
<div style="margin-top: auto; font-size: 0.8rem; color: #64748b; line-height: 1.4;">
<strong style="color:white;">Theory:</strong> In a single-cycle datapath, the entire instruction (Fetch, Decode, Execute, Memory, Writeback) completes in one clock tick. The <strong>Control Unit</strong> sets the multiplexers based on the Opcode.
</div>
</div>
<div class="viz-stage">
<div id="tooltip" class="tooltip"></div>
<svg viewBox="0 0 800 500" id="datapathSvg">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#334155" />
</marker>
<marker id="arrow-active" markerWidth="10" markerHeight="10" refX="7" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#06b6d4" />
</marker>
</defs>
<rect x="50" y="200" width="40" height="60" class="component" />
<text x="70" y="230" class="component-text">PC</text>
<rect x="150" y="150" width="80" height="120" class="component" />
<text x="190" y="210" class="component-text">IM</text>
<rect x="350" y="150" width="80" height="120" class="component" />
<text x="390" y="210" class="component-text">RegFile</text>
<ellipse cx="250" cy="80" rx="40" ry="30" class="component" style="stroke: var(--accent-purple);" />
<text x="250" y="80" class="component-text" style="fill: var(--accent-purple);">Control</text>
<ellipse cx="480" cy="350" rx="30" ry="20" class="component" style="stroke: var(--accent-purple);" />
<text x="480" y="350" class="component-text" style="font-size: 10px;">ALU Ctrl</text>
<path d="M480,180 L480,260 L540,240 L540,200 Z" class="component" />
<text x="510" y="220" class="component-text">ALU</text>
<rect x="600" y="180" width="80" height="100" class="component" />
<text x="640" y="230" class="component-text">DM</text>
<path d="M310,230 L310,270 L330,270 L330,230 Z" class="mux" id="mux-regdst" />
<path d="M450,230 L450,270 L470,270 L470,230 Z" class="mux" id="mux-alusrc" />
<path d="M720,200 L720,240 L740,240 L740,200 Z" class="mux" id="mux-memtoreg" />
<path d="M90,230 L150,230" class="wire" id="w-pc-im" />
<path d="M230,190 L350,190" class="wire" id="w-im-reg1" />
<path d="M230,210 L350,210" class="wire" id="w-im-reg2" />
<path d="M230,170 L250,170 L250,110" class="wire control" id="w-im-control" />
<path d="M230,230 L310,240" class="wire" id="w-im-muxreg-0" />
<path d="M230,250 L310,260" class="wire" id="w-im-muxreg-1" />
<path d="M230,270 L400,270 L400,260 L450,260" class="wire" id="w-im-signext" />
<path d="M430,190 L480,190" class="wire" id="w-reg-alu1" />
<path d="M430,230 L450,240" class="wire" id="w-reg-muxalu" />
<path d="M470,250 L480,250" class="wire" id="w-muxalu-alu" />
<path d="M540,220 L600,220" class="wire" id="w-alu-dm-addr" />
<path d="M540,220 L560,220 L560,160 L710,160 L710,210 L720,210" class="wire" id="w-alu-muxmem-0" />
<path d="M430,230 L440,230 L440,300 L600,300 L600,270" class="wire" id="w-reg-dm-data" />
<path d="M680,230 L720,230" class="wire" id="w-dm-muxmem-1" />
<path d="M740,220 L760,220 L760,380 L300,380 L300,250 L350,250" class="wire" id="w-wb-reg" />
<path d="M290,80 L320,80 L320,220" class="wire control" id="c-regdst" /> <path d="M270,110 L270,350 L460,350" class="wire control" id="c-aluop" /> <path d="M250,50 L460,50 L460,220" class="wire control" id="c-alusrc" /> <path d="M230,110 L230,420 L620,420 L620,280" class="wire control" id="c-memwrite" /> <path d="M210,90 L200,90 L200,440 L660,440 L660,280" class="wire control" id="c-memread" /> <path d="M290,70 L730,70 L730,190" class="wire control" id="c-memtoreg" /> <path d="M290,90 L370,90 L370,140" class="wire control" id="c-regwrite" /> </svg>
</div>
<div class="sidebar-right">
<h3>Control Signals</h3>
<div class="signal-grid" id="signalGrid">
<div class="signal-box" id="sig-regdst"><span class="signal-label">RegDst</span><div class="signal-val">0</div></div>
<div class="signal-box" id="sig-alusrc"><span class="signal-label">ALUSrc</span><div class="signal-val">0</div></div>
<div class="signal-box" id="sig-memtoreg"><span class="signal-label">MemtoReg</span><div class="signal-val">0</div></div>
<div class="signal-box" id="sig-regwrite"><span class="signal-label">RegWrite</span><div class="signal-val">0</div></div>
<div class="signal-box" id="sig-memread"><span class="signal-label">MemRead</span><div class="signal-val">0</div></div>
<div class="signal-box" id="sig-memwrite"><span class="signal-label">MemWrite</span><div class="signal-val">0</div></div>
<div class="signal-box" id="sig-branch"><span class="signal-label">Branch</span><div class="signal-val">0</div></div>
<div class="signal-box" id="sig-aluop"><span class="signal-label">ALUOp</span><div class="signal-val">00</div></div>
</div>
<h3 style="margin-top: 2rem;">Register File</h3>
<table class="data-table">
<thead><tr><th>Reg</th><th>Val (Dec)</th></tr></thead>
<tbody id="regTableBody">
</tbody>
</table>
</div>
</div>
<script>
// --- System State ---
const registers = Array(10).fill(0); // $zero to $t9 simplified
const regNames = ['$zero', '$at', '$v0', '$v1', '$a0', '$a1', '$a2', '$a3', '$t0', '$t1'];
// Initial values for demo
registers[8] = 10; // $t0
registers[9] = 20; // $t1
// --- DOM Elements ---
const els = {
instrType: document.getElementById('instrType'),
opcodeSelect: document.getElementById('opcodeSelect'),
rs: document.getElementById('rs'),
rt: document.getElementById('rt'),
rd: document.getElementById('rd'),
immGroup: document.getElementById('immGroup'),
immediate: document.getElementById('immediate'),
asmPreview: document.getElementById('asmPreview'),
regTable: document.getElementById('regTableBody'),
signals: {
regdst: document.getElementById('sig-regdst'),
alusrc: document.getElementById('sig-alusrc'),
memtoreg: document.getElementById('sig-memtoreg'),
regwrite: document.getElementById('sig-regwrite'),
memread: document.getElementById('sig-memread'),
memwrite: document.getElementById('sig-memwrite'),
branch: document.getElementById('sig-branch'),
aluop: document.getElementById('sig-aluop'),
}
};
// --- Initialization ---
function init() {
// Populate Registers Dropdowns
const createOpt = (name, idx) => `<option value="${idx}">${name}</option>`;
const options = regNames.map((n, i) => createOpt(n, i)).join('');
els.rs.innerHTML = options;
els.rt.innerHTML = options;
els.rd.innerHTML = options;
// Set default selections
els.rs.value = 8; // $t0
els.rt.value = 9; // $t1
els.rd.value = 8; // $t0
updateOpcodeOptions();
renderRegisters();
updatePreview();
}
// --- Event Listeners ---
els.instrType.addEventListener('change', () => {
updateOpcodeOptions();
updateUIForType();
updatePreview();
});
[els.opcodeSelect, els.rs, els.rt, els.rd, els.immediate].forEach(el => {
el.addEventListener('input', updatePreview);
});
// --- Logic ---
function updateOpcodeOptions() {
const type = els.instrType.value;
let ops = [];
if (type === 'R-Type') ops = ['add', 'sub', 'and', 'or', 'slt'];
else if (type === 'LW') ops = ['lw'];
else if (type === 'SW') ops = ['sw'];
else if (type === 'BEQ') ops = ['beq'];
els.opcodeSelect.innerHTML = ops.map(op => `<option value="${op}">${op}</option>`).join('');
}
function updateUIForType() {
const type = els.instrType.value;
// R-Type uses rd, others use immediate
if (type === 'R-Type') {
els.rd.style.display = 'inline-block';
els.immGroup.style.display = 'none';
} else {
els.rd.style.display = 'none';
els.immGroup.style.display = 'block';
}
}
function updatePreview() {
const op = els.opcodeSelect.value;
const rsName = regNames[els.rs.value];
const rtName = regNames[els.rt.value];
const rdName = regNames[els.rd.value];
const imm = els.immediate.value;
let asm = "";
if (els.instrType.value === 'R-Type') {
asm = `${op} ${rdName}, ${rsName}, ${rtName}`;
} else if (els.instrType.value === 'LW' || els.instrType.value === 'SW') {
asm = `${op} ${rtName}, ${imm}(${rsName})`;
} else if (els.instrType.value === 'BEQ') {
asm = `${op} ${rsName}, ${rtName}, ${imm}`;
}
els.asmPreview.innerText = asm;
}
function renderRegisters() {
els.regTable.innerHTML = registers.map((val, i) => `
<tr>
<td style="color:var(--accent-cyan)">${regNames[i]}</td>
<td id="reg-val-${i}">${val}</td>
</tr>
`).join('');
}
function resetMachine() {
resetWires();
resetSignals();
registers.fill(0);
registers[8] = 10;
registers[9] = 20;
renderRegisters();
}
function resetWires() {
document.querySelectorAll('.wire').forEach(w => {
w.classList.remove('active');
});
document.querySelectorAll('.wire.control').forEach(w => {
w.classList.remove('active');
});
}
function resetSignals() {
Object.values(els.signals).forEach(el => {
el.classList.remove('active');
el.querySelector('.signal-val').innerText = '0';
});
}
// --- Core Simulation ---
function executeCycle() {
resetWires();
const type = els.instrType.value;
const op = els.opcodeSelect.value;
const rsIdx = parseInt(els.rs.value);
const rtIdx = parseInt(els.rt.value);
const rdIdx = parseInt(els.rd.value);
// 1. Fetch Stage (Always Active)
highlight(['w-pc-im', 'w-im-reg1', 'w-im-reg2', 'w-im-control']);
// 2. Decode / Control Unit
const signals = getControlSignals(type);
updateControlDisplay(signals);
// 3. Execution Flow based on Type
if (type === 'R-Type') {
// Read Registers
highlight(['w-reg-alu1', 'w-reg-muxalu', 'w-muxalu-alu']);
// ALU Op
const res = aluCalc(op, registers[rsIdx], registers[rtIdx]);
// Write Back
highlight(['w-alu-muxmem-0', 'w-wb-reg']);
if (rdIdx !== 0) { // Can't write to $zero
registers[rdIdx] = res;
flashRegister(rdIdx);
}
} else if (type === 'LW') {
// Read Base Addr
highlight(['w-reg-alu1', 'w-im-signext', 'w-muxalu-alu']);
// Mem Access
highlight(['w-alu-dm-addr', 'w-dm-muxmem-1', 'w-wb-reg']);
// Fake Memory Load (random or static for demo)
const memVal = 42;
if (rtIdx !== 0) {
registers[rtIdx] = memVal;
flashRegister(rtIdx);
}
} else if (type === 'SW') {
// Calc Addr & Read Data
highlight(['w-reg-alu1', 'w-im-signext', 'w-muxalu-alu', 'w-reg-dm-data']);
// Write to Mem (End of path)
highlight(['w-alu-dm-addr']);
// Logic: registers[rtIdx] written to Mem[rsIdx + imm]
} else if (type === 'BEQ') {
// Compare
highlight(['w-reg-alu1', 'w-reg-muxalu', 'w-muxalu-alu']);
// Branch Logic (Simplified visual)
if (registers[rsIdx] === registers[rtIdx]) {
// Branch taken
}
}
renderRegisters();
}
function getControlSignals(type) {
// Simplified MIPS Control Truth Table
if (type === 'R-Type') return { regdst: 1, alusrc: 0, memtoreg: 0, regwrite: 1, memread: 0, memwrite: 0, branch: 0, aluop: '10' };
if (type === 'LW') return { regdst: 0, alusrc: 1, memtoreg: 1, regwrite: 1, memread: 1, memwrite: 0, branch: 0, aluop: '00' };
if (type === 'SW') return { regdst: 'X', alusrc: 1, memtoreg: 'X', regwrite: 0, memread: 0, memwrite: 1, branch: 0, aluop: '00' };
if (type === 'BEQ') return { regdst: 'X', alusrc: 0, memtoreg: 'X', regwrite: 0, memread: 0, memwrite: 0, branch: 1, aluop: '01' };
}
function updateControlDisplay(sigs) {
for (const [key, val] of Object.entries(sigs)) {
const el = els.signals[key];
el.querySelector('.signal-val').innerText = val;
// Highlight wire in SVG if signal is 1
if (val === 1 || val === '10' || val === '01') {
el.classList.add('active');
const wire = document.getElementById(`c-${key}`);
if(wire) wire.classList.add('active');
} else {
el.classList.remove('active');
}
}
}
function highlight(ids) {
ids.forEach(id => {
const el = document.getElementById(id);
if (el) el.classList.add('active');
});
}
function aluCalc(op, v1, v2) {
if (op === 'add') return v1 + v2;
if (op === 'sub') return v1 - v2;
if (op === 'and') return v1 & v2;
if (op === 'or') return v1 | v2;
if (op === 'slt') return (v1 < v2) ? 1 : 0;
return 0;
}
function flashRegister(idx) {
const cell = document.getElementById(`reg-val-${idx}`);
if (cell) {
cell.classList.remove('val-changed');
void cell.offsetWidth; // trigger reflow
cell.classList.add('val-changed');
}
}
// Run
init();
</script>
</body>
</html>