Back

16-bit CPU in Logisim

Final Project for Computer Architecture course.

What Can It Run?

16 Instructions
8 Registers
9 Control Signals
Harvard Architecture

Demo programs running on the CPU: iterative Fibonacci (left) and prime number sieve (right).
Both use the CPU's stack, I/O, and arithmetic instructions.

Project Overview

  • Goal: design and implement a fully functional single-cycle 16-bit RISC CPU in Logisim
  • Implemented the Duke 250/16 ISA (inspired by MIPS ISA): 16 instructions across R, I, and J formats
  • Wired a full datapath with combinational control logic
  • Built from primitive components (logic gates, multiplexers, D flip-flops)
  • Harvard architecture: separate 16-bit ROM (instruction memory) and RAM (data memory), word-addressed
  • Automated test suite: 9 / 9 test programs passed, including recursion, I/O, shifts, memory, and branch/jump instructions

CPU Architecture

Full CPU Datapath

The CPU follows a classic single-cycle RISC datapath: each instruction completes within one clock cycle, with all datapath decisions made combinationally from the opcode. The design uses a Harvard architecture (instruction and data memory are separate) which maps naturally to Logisim's ROM (instruction fetch) and RAM (load/store) components.

PC Logic Instruction Decode Register File ALU Data Memory Keyboard + TTY
Instruction Set Architecture

Click to expand

The Duke 250/16 implements 16 instructions across three encoding formats. The 4-bit opcode occupies bits [15:12] of every 16-bit instruction word.

Instruction Formats
Format Opcode [15:12] Rs [11:9] Rt [8:6] Rd [5:3] Shamt [2:0]
R-Type 4 bits 3 bits 3 bits 3 bits 3 bits
Format Opcode [15:12] Rs [11:9] Rt [8:6] Immediate [5:0] (6-bit signed)
I-Type 4 bits 3 bits 3 bits 6 bits
Format Opcode [15:12] Address [11:0]
J-Type 4 bits 12 bits
All 16 Instructions
Instruction Opcode Type Usage Operation
addi 0000 I addi $rt, $rs, imm $rt = $rs + imm
add 0001 R add $rd, $rs, $rt $rd = $rs + $rt
sub 0010 R sub $rd, $rs, $rt $rd = $rs − $rt
not 0011 R not $rd, $rs $rd = ~$rs
and 0100 R and $rd, $rs, $rt $rd = $rs & $rt
sll 0101 R sll $rd, $rs, shamt $rd = $rs << shamt
srl 0110 R srl $rd, $rs, shamt $rd = $rs >> shamt (logical)
lw 0111 I lw $rt, D($rs) $rt = Mem[$rs + D]
sw 1000 I sw $rt, D($rs) Mem[$rs + D] = $rt
bgez 1001 I bgez $rs, B if $rs ≥ 0: PC = PC+1+B
beq 1010 I beq $rs, $rt, B if $rs == $rt: PC = PC+1+B
j 1011 J j L PC = L (upper 4 bits zeroed)
jr 1100 R jr $rs PC = $rs
jal 1101 J jal L $r7 = PC+1; PC = L
input 1110 R input $rd $rd = keyboard (non-blocking)
output 1111 R output $rs TTY ← $rs[6:0] (ASCII)

Pseudo-instructions la (load address) and halt are assembled into sequences of real instructions. Immediates are 6-bit signed two's complement; the assembler sign-extends them to 16 bits.

Subcircuits

Click to expand

The CPU is decomposed into reusable subcircuits. Each was designed and tested independently before being wired into the main datapath.

ALU subcircuit
Arithmetic Logic Unit
  • 7 operations: ADD, SUB, NOT, AND, SLL, SRL
  • 3-bit ALUOp select input drives a MUX over operation outputs
  • Zero flag output used by beq to detect equality
  • Sign-bit output used by bgez to detect ≥ 0
Register file subcircuit
Register File
  • 8 × 16-bit registers ($r0–$r7); $r0 hardwired to zero
  • Read ports use tri-state buffers + decoder
  • Write port clocked on rising edge; $r0 write-enable permanently disabled
  • Asynchronous reset clears all registers via DFF clear pins
PC logic subcircuit
PC Logic
  • PC register clocked on falling edge (inverted clock) for timing correctness
  • 2-bit PCSel chooses next PC: sequential (PC+1), branch (PC+1+B), jump (J-addr), or register ($rs)
  • Branch condition (Zero/Sign flag from ALU) gates whether branch target is selected
  • Asynchronous reset forces PC to 0x0000
Control Signals

Click to expand

A central control unit decodes the 4-bit opcode and drives 9 control signals that route data through the datapath each cycle. PCSel selects the next PC source; DSel picks the destination register; RegWE enables register writes; BSel selects ALU input B; ALUOp sets the ALU operation; MemWE enables memory writes; WBSel selects the write-back data; keyRE enables keyboard read; TTYWE enables TTY write.

Control Unit Combinational Logic
Instruction PCSel DSel RegWE BSel ALUOp MemWE WBSel keyRE TTYWE
addi 00rt 1imm add0alu 00
add 00rd 1rt add0alu 00
sub 00rd 1rt sub0alu 00
not 00rd 1rt not0alu 00
and 00rd 1rt and0alu 00
sll 00rd 1shamtsll0alu 00
srl 00rd 1shamtsrl0alu 00
lw 00rt 1imm add0mem 00
sw 00 0imm add1 00
bgez 01 00 add0 00
beq 01 0rt sub0 00
j 10 0 0 00
jr 11 0 0 00
jal 10r7 1 0PC+1 00
input00rd 1 0key 10
output00 0 0 01
Test Suite — 9 / 9 Passed

Click to expand

The automated test harness (hwtest.py) loads each program into the CPU's instruction and data memory via Logisim CLI, clocks the simulation, and compares all register values against expected outputs cycle-by-cycle.

addition
arithmetic
boolean
shift
memory
control
io
recurse
demo_fibonacci

The recurse test verifies the full stack-based calling convention: jal/jr, callee-saved registers ($r3–$r5), and a software stack pointer ($r6) — computing fib(4) = 5 recursively. The io test validates non-blocking keyboard reads and character output to the TTY.

Digital Logic Design
RISC Processor Architecture
ALU & Datapath Design
Control Unit Implementation
Assembly Programming
Harvard Architecture