Assignment Chef icon Assignment Chef
All English tutorials

Programming lesson

Building a Single-Cycle RISC-V CPU in Verilog: A Step-by-Step Lab Guide

Master the implementation of a single-cycle RISC-V CPU in Verilog HDL with this comprehensive lab tutorial. Learn to create instruction memory, integrate ALU and control units, and simulate your design step by step.

single-cycle CPU RISC-V Verilog instruction memory Verilog CDA 4205L lab 8 Verilog CPU implementation single-cycle processor tutorial RISC-V simulation Verilog memory initialization $readmemb Verilog computer architecture lab single-cycle vs pipelining Verilog testbench CPU RISC-V ALU control unit FPGA design tutorial Vivado simulation CPU educational CPU design

Introduction to Single-Cycle CPU Implementation

Welcome to CDA 4205L Lab #8! In this tutorial, you will learn how to implement a single-cycle RISC-V CPU using Verilog HDL. This lab builds on the ALU and Control Unit design from Lab #7 and brings together all components to form a working processor. By the end, you'll be able to run machine code generated by your assembler from Labs #3 and #4. We'll focus on the critical missing piece: the instruction memory module. Let's dive in!

What is a Single-Cycle CPU?

A single-cycle CPU executes each instruction in one clock cycle. While modern processors use pipelining for higher performance, single-cycle designs are simpler and ideal for educational purposes. In this lab, you'll implement a RISC-V processor that fetches, decodes, executes, and writes back results within a single cycle. Think of it like a fast-food drive-through: every order (instruction) is processed completely before the next car moves. This analogy helps understand the simplicity and timing constraints.

Prerequisites and Lab Setup

Before starting, ensure you have:

  • Vivado project files from Canvas (including instr_mem.v template)
  • Completed Lab #7 (ALU and Control Unit)
  • Machine code file from your assembler (binary or hex)
Download the files and open instr_mem.v. This file contains a module with pre-defined parameters: DATA_WIDTH, ADDRESS_WIDTH, and NUM_MEM_CELLS. These defaults can be overridden when instantiating the module in the top-level design.

Step 1: Declaring Instruction Memory

In Verilog, memory is declared as a 2D register array. The syntax is: reg [DATA_WIDTH-1:0] inst_mem [0:NUM_MEM_CELLS-1]. This creates a memory with NUM_MEM_CELLS locations, each DATA_WIDTH bits wide. For example, with default DATA_WIDTH=32 and NUM_MEM_CELLS=256, we get 256 words of 32 bits each. This is enough for small test programs. Override these parameters in the top-level if you need more space, similar to how AI models like GPT-4 use large memory contexts to process prompts.

Step 2: Initializing Memory with Machine Code

To load your assembled program into memory, use the $readmemb() system task inside an initial block. The syntax is: $readmemb("filename.bin", inst_mem); If your instructions are in hex, use $readmemh(). This is only for simulation; real FPGAs would need a different initialization method. Place the initial block after the memory declaration. For example:

reg [31:0] inst_mem [0:255];
initial begin
    $readmemb("program.bin", inst_mem);
end

Make sure the file path is correct relative to the simulation directory. This step is crucial because without initialized memory, the CPU will fetch unknown values.

Step 3: Continuous Read for Single-Cycle Operation

Unlike synchronous memories that use a clock, a single-cycle CPU reads instructions continuously. Use an assign statement to output the instruction at the current address: assign data = inst_mem[address]; This ensures that the instruction is available immediately when the address changes. The address comes from the Program Counter (PC), which is updated each cycle. This combinational read is like a Google search: you type a query (address) and instantly get results (instruction).

Step 4: Integrating with Existing Components

Your single-cycle CPU includes the ALU, Control Unit, register file, and data memory from previous labs. The instruction memory feeds the instruction to the control unit and the register file. Ensure all modules are connected correctly in the top-level. For instance, the instruction's opcode and funct fields drive the control unit, while the immediate and register addresses are extracted. Use the provided testbench to verify functionality. If you encounter issues, check that $readmemb loaded the file correctly by printing the first few instructions.

Step 5: Simulation and Debugging

Run simulation in Vivado. Observe the waveform to see if the CPU executes instructions as expected. Common bugs include: wrong memory file path, incorrect address width, or mismatched data widths. Also, ensure that the address input to instruction memory is word-aligned (byte offset = 0). Debugging a single-cycle CPU is like debugging a viral app: you trace each step from fetch to write-back, just as developers track user interactions. Use breakpoints and watch variables to isolate issues.

Conclusion and Next Steps

Congratulations! You've implemented a single-cycle RISC-V CPU in Verilog. This lab reinforces your understanding of computer architecture and Verilog design. Extend your design by adding more instructions, implementing pipelining, or optimizing the ALU. The skills you've learned are directly applicable to modern processor design, from smartphone SoCs to AI accelerators. Keep experimenting!

Additional Resources

  • RISC-V Specification Volume 1: Unprivileged ISA
  • Verilog HDL tutorial from previous labs
  • Vivado simulation guide
Tip: If you're stuck, review the prelab material and ensure your ALU and Control Unit from Lab #7 are correct. Many issues stem from earlier components.