16'bit RISC-V
PROCESSOR
RISC ARCHITECTURE
[TOTAL INST = 13]
Instruction set
Memory Access Data Processing Control Flow
Instructions Instructions Instructions
1. LW → Load Word 1. ADD → Addition 1. BEQ → Branch on Equal
2. SW → Store Word 2. SUB → Subtraction 2. BNE → Branch on Not Equal
3. INV → Invert(1' complement) 3. JMP → Jump
4. LSL → Logical Shift Left
5. LSR → Logical Shift Right
6. AND → Bitwise AND
7. OR → Bitwise OR
8. SLT → Set on Less Than
Instruction Format
CONTROL SIGNALS
ALU CONTROL MAPPING
ALUOp = 10 → “xxxx” / 000 / ADD / LW, SW
ALUOp = 01 → “xxxx” / 001 / SUB / BEQ, BNE
ALUOp = 00 / 0002 → 000 / ADD / D-type: ADD
ALUOp = 00 / 0003 → 001 / SUB / D-type: SUB
ALUOp = 00 / 0004 → 010 / INV / D-type: INV
ALUOp = 00 / 0005 → 011 / LSL / D-type: LSL
ALUOp = 00 / 0006 → 100 / LSR / D-type: LSR
ALUOp = 00 / 0007 → 101 / AND / D-type: AND
ALUOp = 00 / 0008 → 110 / OR / D-type: OR
ALUOp = 00 / 0009 → 111 / SLT / D-type: SLT
Instruction Memory
Purpose / Role:
1. Fetch Stage
Takes pc[15:0] as input, uses bits [4:1] to form a 4-bit “ROM”
address.
Outputs a 16-bit instruction word.
2. Initialization
On reset or simulation start, $readmemb("./test/[Link]",…)
loads the binary instructions from an external file.
3. Fixed-Size
Parameter `row_i defines how many instructions are stored.
Keeps instruction memory small (e.g. 15 instructions) so
simulation is fast.
Register File
Purpose / Role:
1. Asynchronous Reads
At any time, “read ports” output the contents of reg_array[reg_read_addr_1] and
reg_array[reg_read_addr_2].
This supports the ALU’s need for two source operands in the same cycle.
2. Synchronous Write
On the rising edge of clk, if reg_write_en == 1, the chosen register (reg_write_dest) is
overwritten by reg_write_data.
Guarantees data stability until the next clock edge.
3. Reset / Initialization
At module initialization, all eight registers are set to 0. This ensures deterministic
simulation behavior.
4. Multiplexing Destination
Controlled by RegDst: if RegDst=1, we write to the “D-type” field (instr[5:3]);
otherwise (e.g. loads) we pick the “I-type” field (instr[8:6]).
Instruction Memory
Mem Write WBsrc
Purpose / Role:
1. Addressing
The ALU result (ALU_out) goes into mem_access_addr. The lower 3 bits ([2:0]) index
one of the 8 words.
2. Synchronous Writes
When mem_write_en == 1, on the next rising clock edge, memory[ram_addr] is
updated to mem_write_data.
3. Asynchronous (Combinational) Read
If mem_read == 1, mem_read_data = memory[ram_addr] immediately (no extra clock).
Otherwise, output is 0.
This lets the CPU fetch load results without additional gating.
4. Initialization
On start, $readmemb("./test/[Link]", memory) preloads the eight words from a
file.
A $fmonitor logs every memory location to an output file whenever any write
happens, for post‐simulation analysis.
ALU
Bsrc Zero flag
Purpose / Role:
1. Operation Selection
alu_control codes (000..111) select one of eight operations:
000 → a + b (ADD)
001 → a - b (SUB)
010 → ~a (INV, 1’s complement)
011 → a << b (LSL, logical shift left by b bits)
100 → a >> b (LSR, logical shift right by b bits)
101 → a & b (AND)
110 → a | b (OR)
111 → 1 if a < b else 0 (SLT)
2. Zero Flag
If result == 0, the zero output goes high. This is used for BEQ/BNE.
3. Combinational Behavior
The always @(*) block recomputes result every time a, b, or alu_control changes,
ensuring single-cycle operation.
ALU Control
Purpose / Role:
1. Two-Level Decoding
If ALUOp == 2’b10, generate ALU_Cnt=000 (ADD) for all loads/stores.
If ALUOp == 2’b01, generate ALU_Cnt=001 (SUB) for both BEQ and BNE.
If ALUOp == 2’b00 (D-type), examine the 4-bit Opcode (instr[15:12]) to pick exactly
which 3-bit ALU_Cnt to output (e.g. 010 for INV, 011 for LSL, …).
2. Flexibility / Expansion
Adding a new D-type operation only requires assigning a new 4-bit opcode and
expanding the casex(…) table.
3. One “Casex” Block
Packs all eight D-type cases + two high-level (load/store and branch) into a single 6-
bit key = {ALUOp, Opcode} for a compact implementation.
Data Path
Purpose / Role / Sub‐Blocks:
1. Program Counter (PC) & Next‐PC Logic
Holds pc_current (16 bits). On every rising clock, pc_current ← pc_next.
pc2 = pc_current + 2 (since each instruction is 2 bytes).
Branch Target: PC_beq = pc2 + (sign-extended offset << 1).
Jump Target: PC_j = {pc2[15:13], instr[11:0], 1’b0}.
Mux decides pc_next based on jump, bne, beq signals.
2. Instruction Fetch
Instantiates Instruction_Memory with pc_current → outputs 16-bit instr.
Separates the logic that updates PC from the logic that decodes the instruction.
3. Register File Interface
Extracts reg_read_addr_1 = instr[11:9], reg_read_addr_2 = instr[8:6].
reg_write_dest = (reg_dst) ? instr[5:3] : instr[8:6].
Connects to GPRs module to read reg_read_data_1, reg_read_data_2 and write back
reg_write_data.
4. Immediate Extension
Takes the 6-bit offset (instr[5:0]), sign‐extends to 16 bits:
ext_im = { {10{instr[5]}}, instr[5:0] }.
Serves both branch offset (shifted by one) and I-type immediate for loads/stores.
5. ALU & ALU MUX
read_data2 = (alu_src) ? ext_im : reg_read_data_2.
Instantiates ALU( a = reg_read_data_1, b = read_data2, control = ALU_Control ) →
ALU_out, zero_flag.
ALU’s result used for either arithmetic logic or effective address (for load/store).
6. Data Memory Access
Uses ALU_out as mem_access_addr.
If mem_write = 1 → write reg_read_data_2 into data memory.
If mem_read = 1 → read mem_read_data from data memory.
7. Write‐Back MUX
reg_write_data = (mem_to_reg) ? mem_read_data : ALU_out.
Ensures either the load value or the ALU result is written back to the register file.
8. Opcode Output
Finally, opcode = instr[15:12] is driven out to the Control Unit each cycle.
Control Unit Purpose / Role / Signal Definitions:
1. opcode In
Top 4 bits of the fetched instruction. Determines the instruction type (e.g. 0000 →
LW).
2. Generate Control Signals
RegDst:
1 for D-type (ADD, SUB, …) so destination = instr[5:3].
0 for I-type (LW, SW) so destination = instr[8:6].
ALUSrc:
1 for instructions using immediate (LW, SW).
CU 0 for register–register (ADD, SUB, etc.).
MemtoReg:
1 for loads (so that write-back comes from data memory).
0 otherwise (ALU result goes back).
RegWrite:
1 if instruction writes to a register (D-type or LW).
0 for SW, BEQ, BNE, JMP.
MemRead / MemWrite:
Only LW sets MemRead=1.
Only SW sets MemWrite=1.
Branch (shared for BEQ/BNE) & bne:
BEQ sets Branch=1, ALUOp=01.
BNE sets bne=1, ALUOp=01.
ALUOp:
10 for load/store, 01 for BEQ/BNE, 00 for D-type.
Jump (JMP)
1 only for opcode 1101 (JMP).
3. Default / Fallback
Unrecognized opcode behaves like a D-type (RegWrite=1, ALUOp=00).
Top-Level RISC Module
What it is:
The top-level module that ties Datapath Unit and Control Unit together under a single
clock.
Why we need it:
To create a complete 16-bit RISC CPU with one instantiation of each major block.
Provides a single entity (Risc_16_bit) that can be instantiated by a testbench or higher-
level system.
Purpose / Role / Connections:
1. Clock Input Only
Single clk drives all stateful components (PC register, register file, data memory
writing).
2. Datapath Instantiation
Connect clk + all control signals (jump, beq, bne, mem_read, mem_write, alu_src,
reg_dst, mem_to_reg, reg_write) + alu_op + opcode.
Produces opcode (from fetched instruction) back to the Control Unit.
3. Control Unit Instantiation
Takes opcode from the Datapath.
Outputs all control signals to the Datapath.
4. No Additional Logic
The top‐level does not add or transform signals. It simply wires Control ↔ Datapath
together.
Parameter
Parameter.v:
Centralizes all sizes and file paths (instruction memory size, data memory size, waveform
dump file) in one place. If you want a larger instruction ROM or data RAM, you can
modify row_i or row_d without editing multiple modules.
Testbench:
Provides a controlled simulation environment: generates the clock, waits for a
predetermined time (#160 ns), then ends. Allows viewing waveforms (PC changes,
register writes, memory writes) and verifying correctness.
Purpose / Role in Verification:
1. Automated Simulation
No external stimuli needed: instructions come from [Link], data comes from
[Link].
Clock automatically toggles.
2. Logging / Monitoring
$fmonitor in Data Memory writes a timestamped dump of all eight memory words
whenever there’s a write.
Post-simulation, you can open the .o file to confirm load/store behavior.
3. Parameter Flexibility
Changing Parameter.v (e.g. row_i=32, row_d=16) automatically adjusts the sizes of
Instruction Memory and Data Memory across all modules.
Summary
Instruction Memory: Holds the program, provides each fetched instruction to the
CPU.
Register File: Stores and supplies up to two source operands per instruction, and
writes back results.
Data Memory: Implements load/store operations, backed by synchronized writes and
combinational reads.
ALU Unit: Performs arithmetic and logic operations on register/immediate inputs,
asserts zero flag.
ALU Control: Decodes high-level ALUOp & 4-bit opcode to a 3-bit ALU function code.
Datapath Unit: Wires together PC, instruction fetch, register file, immediate
extension, ALU, data memory, and write-back.
Control Unit: Decodes the instruction’s opcode into all necessary control signals for
the datapath.
Top-Level Module: Instantiates and interconnects datapath + control under a single
clock.
Testbench & Parameter File: Provide a self-contained simulation environment and
configurable constants.
THANK YOU