Show description
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.
Control Signals
RegDst
0
ALUSrc
0
MemtoReg
0
RegWrite
0
MemRead
0
MemWrite
0
Branch
0
ALUOp
00
Register File
Reg
Val (Dec)
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>