CSCE 611 Fall 2026 Lecture 9: RISC-V Microarchitecture 2
Watch on YouTube →
Overview
Jason D. Bakos explains how to build the lab 3 RISC-V CPU in SystemVerilog, connecting instruction and register memories, a 4-bit-opcode ALU, instruction decoding, and a three-stage pipeline. He works through control-signal choices for the 22 supported instructions, then outlines simulation tests and a fixed-point multiply-high method for converting switch inputs to decimal without divide or modulo instructions.
Key takeaways
- The lab's three-stage CPU sustains one instruction per cycle after pipeline fill, but its three-cycle instruction latency requires careful cycle alignment of destination addresses and writeback controls.
- Memory behavior determines FPGA implementation: synchronous instruction reads can map to dedicated RAM blocks, while the 32-entry asynchronous, multiport register file may synthesize into flip-flops and multiplexers.
- The writeback mux must distinguish GPIO input, U-type immediate data, and ALU results; the GPIO output register additionally needs an enable to preserve display values between CSRRW writes.
- CSRRW handling is customized for the lab: one control-register address routes switches into the register file, while another updates the hex displays through GPIO write enable.
- A multiply by approximately 429,496,730 represents multiplication by 0.1 in a 32-bit fractional scale, allowing multiply-high and the low product to replace division and modulo by 10.
- SystemVerilog testbenches can inspect internal register-file memory hierarchically, but checks must account for the pipeline's delay before an instruction updates its destination.
Chapters
0:00
RAM Depth, Width, and Read Timing for the CPU
- RAM depth counts entries, so a 1,024-entry memory needs 10 address bits; the 32-entry register file needs 5.
- The lab uses 32-bit-wide instruction and register memories, with one word at each address.
- Instruction RAM uses synchronous reads, while the small three-port register file uses asynchronous reads.
4:39
FPGA RAM Mapping and the ALU Opcode Table
- A RAM with synchronous reads and no more than two ports can map to an FPGA memory block such as Intel/Altera M20K.
- The register file's asynchronous reads and multiple ports may instead require flip-flops, decoders, and read multiplexers.
- The ALU has a 4-bit opcode for up to 16 values, but repeated encodings leave 13 distinct operations; its table is a control-unit reference.
8:54
RISC-V Instruction Fields and Immediate Data Paths
- Lab 3 implements 22 RISC-V instructions across R-, I-, and U-type encodings, with instruction fields extracted from instruction_ex.
- RS1 and RS2 feed register-file read addresses; RD must be delayed to align with the later writeback stage.
- Opcode, funct3, and funct7 identify instructions through nested decoding; U-type instructions can be identified by opcode alone.
- The 12-bit immediate feeds an ALU-input mux, while the 20-bit U-type immediate needs a separate path to register writeback.
15:00
SystemVerilog Instruction ROM and Word-Addressed PC
- The instruction memory is declared as a SystemVerilog array and initialized with $readmemh from a program file such as program.rom.
- A clocked read places the addressed instruction into instruction_ex, and the program counter advances by one because the RAM is word-addressed.
- The synthesis tool infers a read-only memory from the described behavior and can map it to FPGA RAM resources.
19:00
Why the Lab CPU Uses a Three-Stage Pipeline
- A single-cycle CPU completes fetch, decode, execute, and writeback in one long cycle, limiting clock speed through its critical path.
- The lab pipeline groups execution into fetch, execute, and writeback stages so those stages process different instructions in parallel.
- The design targets one instruction per cycle after pipeline fill, while each instruction has a three-cycle latency.
- In lab 3, the fetch stage is represented mainly by the PC; writeback centers on the register-file write port.
23:00
Fetch/Decode Wiring and Pipeline Registers
- The instruction memory and PC can be behavioral code rather than separate instantiated modules in the block diagram.
- Instruction fields are routed to the control unit, register-file ports, or clocked pipeline registers according to their later use.
- RD bits 11:7 are stored in a pipeline register so the destination address reaches writeback one cycle later.
- Pipeline registers may be grouped in one always_ff block or split across several blocks; either organization is acceptable.
29:00
Readable Instruction-Field Names and Microcode
- Optional aliases such as opcode_ex and funct3_ex make instruction-field wiring easier to interpret than raw bit slices.
- The control unit translates machine-code fields into mux selections and enables—the CPU's control signals, sometimes described as microcode.
- Historical CPUs sometimes allowed microcode tables to be reprogrammed to add or alter instruction behavior.
31:30
Exporting RARS Machine Code to the Instruction ROM
- RARS can assemble source code and export the text segment as hexadecimal machine-code words through File → Dump Memory.
- The exported hex file is loaded into the instruction ROM with $readmemh; its filename must match the initialization code.
- Simulation must run from the directory containing the file, unless the initialization statement uses an absolute path.
37:00
Register Writeback Mux and Delayed Control Signals
- reg_write_ex is generated from the execute-stage instruction and delayed to become the write-enable control for the writeback stage.
- The register-file write port combines a delayed destination address, write enable, and selected write data.
- A three-input writeback mux selects GPIO input, the U-type immediate concatenated with 12 zeros, or the delayed ALU result.
- Although most lab 3 instructions write a general-purpose register, the GPIO-output CSRRW case does not.
43:00
ALU Operand Selection and Persistent GPIO Output
- The ALU's first operand comes from the register file; ALU source control selects a register value or an immediate for the second operand.
- Selecting an immediate for an instruction without a valid immediate field would feed irrelevant bits to the ALU.
- Hex-display output needs an enabled state-holding register; GPIO write enable should assert only for the CSRRW that updates the displays.
- Without that enable, the display value may last only one 50 MHz cycle and appear to be missing during a hardware demonstration.
44:30
Control-Unit Inputs and R-Type Instruction Examples
- The suggested monolithic control unit decodes funct7, funct3, opcode, and—in this lab's CSRRW handling—the immediate field.
- For R-type arithmetic and logic operations, ALU source selects the second register operand, writeback selects the ALU result, and register write is asserted.
- The ALU opcode must match the operation: examples include add, subtract, multiply-low, and signed set-less-than.
- GPIO write enable remains low for ordinary R-type instructions so the display-holding register retains its previous value.
52:00
Immediate Operations, LUI, and CSRRW Control Choices
- An immediate operation such as ANDI uses the immediate ALU input; its R-type counterpart uses the register input, while both select the same ALU function.
- LUI bypasses the ALU and writes the 20-bit immediate shifted into the upper bits; ALU controls are don't-cares when the writeback mux does not select the ALU.
- CSRRW with the switch-input control address writes GPIO input into the register file; the hex-display address instead asserts GPIO write enable.
- In SystemVerilog, a designer-assigned X can indicate a don't-care value, unlike an unresolved X caused by unknown or conflicting logic.
1:02:30
Instruction Tests and Direct Register-File Inspection
- The provided test program executes each supported instruction at least once, initializes registers with ADDI, and checks expected values in comments.
- Its early instructions deliberately test register overwrite and a simultaneous writeback/read of the same register.
- A SystemVerilog testbench can inspect internal register-file memory hierarchically, such as through a DUT register-file instance's mem array.
- Because the CPU is pipelined, testbench checks must allow instructions to reach writeback; a second meaningful workload converts switch values to decimal.
1:08:30
Binary-to-Decimal Conversion with Fixed-Point Multiply-High
- The decimal-display workload converts switch input into base-10 digits, unlike lab 2's direct hexadecimal display of switch nibbles.
- Since the ALU lacks divide and modulo, division by 10 is approximated by multiplying by the fixed-point reciprocal 0.1.
- Representing 0.1 in a 32-bit fractional scale uses approximately 429,496,730; multiply-high produces the integer quotient.
- The low product supplies the fractional remainder: multiplying that fraction by 10 and taking the high part recovers the decimal digit, as in 12,345 → quotient 1,234 and remainder 5.
1:15:00
Waveform Debugging Tips for ModelSim and SystemVerilog
- An enumerated instruction label in the control unit can identify the execute-stage instruction in simulation without affecting synthesized hardware.
- Delaying that label by one cycle can also identify the instruction in writeback.
- The ModelSim command $wlfdumpvars(0, tb) records non-RAM signals even if they are not yet added to the waveform viewer.
- Register-file and instruction memories are not included by that dump command, so they must be inspected separately.
Summary, takeaways, and chapters were generated by AI from the video's transcript and may contain errors. The video belongs to its creator, Jason D. Bakos.