Skip to content

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

3-Stage Pipelined CPU with SystemVerilog/UVM Verification

A custom 16-bit pipelined CPU implemented in SystemVerilog and verified with a UVM-style verification environment using Cadence Xcelium and IMC.

The project focuses on pipeline control and design-verification methodology rather than ISA complexity. It includes RAW hazard handling, branch flushing, an architectural reference model, SystemVerilog assertions, constrained-random program generation, regression automation, and functional coverage closure.

Current baseline: stall-based RAW hazard resolution without forwarding


Highlights

  • Custom 16-bit ISA with ADD, SUB, MOVI, LOAD, STORE, BEQ, and HALT
  • 3-stage organization: IF → combined Decode/Execute → EX/WB commit
  • Explicit pipeline-valid tracking
  • RAW hazard detection with PC/IF-ID holds and EX/WB bubble insertion
  • Taken-branch redirect and wrong-path flush
  • Independent architectural reference model in the scoreboard
  • Directed + constrained-random verification
  • SystemVerilog Assertions for cycle-level pipeline behavior
  • 100% architectural functional coverage
  • 100% pipeline / RAW-hazard functional coverage
  • All defined assertions exercised and passing
  • Automated regression and Cadence IMC coverage merge flow

CPU Architecture

CPU pipeline and control architecture

The CPU uses a three-stage organization:

  1. Instruction Fetch
  2. Combined Decode / Execute
  3. EX/WB / Architectural Commit

The main datapath consists of the program counter, instruction memory, IF/ID pipeline state, decode/control logic, register file, ALU, EX/WB pipeline state, and architectural writeback.

The architecture also contains two important control paths:

  • RAW hazard control: holds the PC and IF/ID state while inserting a bubble into EX/WB.
  • Branch control: redirects the PC to the target and invalidates the wrong-path instruction in IF/ID.

Separate instruction and data memories avoid a structural conflict between instruction fetch and LOAD/STORE accesses.


Custom ISA

Instruction Function
ADD Register-register addition
SUB Register-register subtraction
MOVI Write an immediate value to a register
LOAD Load from data memory
STORE Store to data memory
BEQ Branch when two registers are equal
HALT Stop architectural execution

Configuration:

  • Instruction width: 16 bits
  • Register width: 16 bits
  • General-purpose registers: 8
  • PC width: 8 bits
  • Instruction memory depth: 256
  • Data memory depth: 256

OP_NOP is retained internally as a placeholder/control value and is not treated as a required architectural operation in the functional-coverage goal.


Pipeline Valid Control

Each pipeline slot carries an explicit valid bit.

Validity distinguishes a real architectural instruction from an empty slot, a RAW-hazard bubble, or a flushed wrong-path instruction.

Architectural side effects and verification trace generation are gated by validity so stale or invalid pipeline contents cannot commit state.


RAW Hazard Handling

The current baseline does not use forwarding.

A RAW dependency is detected when the instruction in IF/ID reads a register that will be written by the older valid instruction in EX/WB.

On a RAW hazard:

PC       : HOLD
IF/ID    : HOLD
EX/WB    : BUBBLE

The producer is allowed to complete writeback while the consumer remains stalled.

The register file uses synchronous writes and asynchronous reads. After the producer writes on the clock edge, the held consumer can observe the updated register value.

Hazard-source detection separately tracks source A and source B, allowing verification of:

  • source-A-only hazards
  • source-B-only hazards
  • simultaneous source-A/source-B hazards

Dependency detection is kept separate from the final stall decision to keep the hazard logic modular and easier to verify.

RAW Hazard Stall Waveform

RAW hazard stall waveform

RAW hazard stall example. A dependent ADD remains held in IF/ID while an older MOVI producer occupies EX/WB. pipe_stall holds the consumer and the following EX/WB cycle is invalidated to create a bubble before execution continues.

The waveform is interpreted together with pipeline-valid signals. Opcode values in invalid pipeline slots are not treated as architectural instructions.


Branch Handling

BEQ compares two source operands using subtraction and the ALU zero result.

The branch target is calculated as the current instruction PC plus a sign-extended branch offset.

For a taken branch:

  • the PC redirects to the branch target
  • the sequentially fetched wrong-path instruction is invalidated
  • execution resumes from the branch target

Directed tests cover:

  • taken and non-taken branches
  • zero offsets
  • negative / backward offsets

Taken-Branch Flush Waveform

Taken BEQ redirect and flush

Taken-BEQ redirect and flush. The branch at PC 2 redirects execution to PC 4. The sequential ADD at PC 3 is fetched into IF/ID, but pipe_if_id_valid is cleared, preventing the wrong-path instruction from committing.

The important behavior is not that the wrong-path instruction bits disappear. The flush is represented by invalidating the pipeline slot.


HALT Handling

HALT propagates through the pipeline rather than stopping the processor immediately at decode.

The processor enters the halted state only after the valid HALT instruction reaches the commit stage, preventing younger instructions from committing architectural state afterward.


Verification Environment

UVM verification architecture

cpu_base_test builds cpu_env, which contains and connects:

  • cpu_monitor
  • cpu_scoreboard
  • cpu_coverage

The verification data path is:

DUT
 |
cpu_if
 |
cpu_monitor
 |
cpu_trace_item
 |             \
cpu_scoreboard  cpu_coverage

cpu_if --> cpu_assertions

The monitor samples DUT signals through cpu_if, builds cpu_trace_item transactions, and publishes them through its analysis port.

The same transaction stream is consumed by the scoreboard and coverage subscriber.

Assertions observe cpu_if directly because they verify cycle-level timing behavior rather than transaction-level architectural state.


Scoreboard and Reference Model

The scoreboard contains an independent architectural reference model.

It maintains expected:

  • register state
  • data-memory state
  • program-counter state

For each valid committed instruction, the scoreboard:

  1. independently decodes the instruction
  2. predicts the expected architectural behavior
  3. builds the expected commit event
  4. compares expected and actual DUT trace values
  5. updates the reference architectural state

The scoreboard checks:

  • register-write enable
  • destination register
  • writeback data
  • memory-write enable
  • memory address
  • memory write data
  • branch outcome
  • branch target
  • HALT behavior

Architecturally irrelevant datapath values are only compared when their corresponding control enables are active.

The scoreboard is deliberately commit-oriented. Pipeline timing rules such as stalls and flushes are checked separately with assertions.


SystemVerilog Assertions

Assertions verify cycle-level pipeline behavior including:

  • known PC, instruction, and trace-valid values
  • sequential committed-PC progression
  • IF/ID PC hold during stalls
  • IF/ID instruction hold during stalls
  • IF/ID valid hold during stalls
  • EX/WB bubble insertion after a stall
  • branch redirection
  • HALT behavior
  • opcode-specific side-effect rules

All defined assertion properties were exercised and passed in the final verification runs.


Directed and Constrained-Random Verification

Directed tests target scenarios that require precise instruction relationships, including:

  • arithmetic operations
  • LOAD / STORE behavior
  • BEQ taken and not taken
  • backward branch loops
  • zero-offset BEQ
  • individual RAW dependencies
  • store dependencies
  • branch dependencies
  • simultaneous dual-source dependencies
  • producer / consumer hazard combinations
  • HALT behavior

Random verification is split into two layers.

cpu_rand_instr generates legal randomized instruction fields according to opcode-specific constraints.

cpu_rand_program constructs complete programs and introduces relationships across instructions, including:

  • RAW producer / consumer sequences
  • valid memory-access relationships
  • controlled branch sequences

Program generation and CPU execution use separate simulations because instruction memory is initialized with $readmemh during simulation startup.


Coverage

Coverage merge flow

Coverage is collected from three verification groups:

  • Basic directed architectural tests → merged
  • Random seed coverage runs → merged_random
  • Targeted RAW hazard tests → merged_rhazard

The three databases are combined into merged_final and inspected using Cadence IMC.

Functional Coverage Results

Cadence IMC functional coverage closure

Cadence IMC coverage closure showing 100% coverage for both arch_cg and pipeline_cg. The architectural covergroup includes opcode behavior and BEQ outcomes/offset classes, while the pipeline covergroup covers stall sources and RAW producer/consumer combinations.

Final defined functional coverage:

  • Architectural functional coverage: 100%
  • Pipeline / RAW hazard functional coverage: 100%
  • All defined assertions exercised and passing

Architectural coverage includes:

  • required architectural opcodes
  • register-write behavior
  • memory-write behavior
  • HALT behavior
  • BEQ taken / not taken
  • positive / zero / negative BEQ offsets
  • opcode / side-effect crosses

Pipeline coverage includes:

  • stall / non-stall behavior
  • source-A-only dependency
  • source-B-only dependency
  • simultaneous dual-source dependency
  • producer opcode
  • consumer opcode
  • producer × consumer opcode crosses

Coverage Closure Analysis

The aggregate IMC code-coverage percentage is intentionally lower than 100%.

Remaining uncovered code/toggle items were reviewed individually instead of adding tests solely to increase the aggregate percentage.

Reviewed gaps include:

  • upper PC bits not toggling because programs occupy only a small portion of the 8-bit address space
  • upper instruction-memory address bits for the same reason
  • reset toggle metrics
  • unused internal OP_NOP control path
  • instruction-memory fallback initialization branches not used by the normal +PROGRAM regression flow
  • upper bits of saved branch-target values not toggling in the tested address range

Core block and expression behavior was reviewed separately from incidental toggle coverage.


Regression and Coverage Flow

Run one directed test:

make run TEST=smoke

Generate and execute one random program:

make rand SEED=10

Run the random regression:

make regress

Generate, merge, and view the complete coverage suite:

make coverage_complete NUM_SEEDS=20

Clean generated simulation artifacts:

make clean

Project Structure

rtl/
    cpu_top.sv
    cpu_pkg.sv
    control.sv
    decoder.sv
    alu.sv
    reg_file.sv
    instr_mem.sv
    data_mem.sv
    pc_reg.sv

tb/
    cpu_if.sv
    tb_cpu_uvm_top.sv

    assert/
        cpu_assertions.sv

    uvm/
        cpu_uvm_pkg.sv
        cpu_trace_item.sv
        cpu_monitor.sv
        cpu_scoreboard.sv
        cpu_coverage.sv
        cpu_env.sv
        cpu_base_test.sv
        cpu_rand_instr.sv
        cpu_rand_program.sv
        cpu_rand_test.sv

tests/
    directed and generated CPU program hex files

sim/
    Makefile
    run.sh
    run_random.sh
    run_regression.sh
    run_random_coverage.sh
    run_raw_hazard.sh
    coverage merge TCL scripts

docs/
    images/
        cpu_architecture.png
        uvm_verification_architecture.png
        coverage_flow.png
        raw_hazard_stall.png
        beq_flush.png
        imc_functional_coverage.png

Tools

  • SystemVerilog
  • UVM
  • SystemVerilog Assertions
  • Cadence Xcelium
  • Cadence IMC
  • Bash
  • Make
  • Git

Current Status

Completed:

  • 3-stage pipelined CPU baseline
  • pipeline-valid control
  • RAW dependency detection
  • stall-based RAW hazard resolution
  • branch redirect and flush
  • HALT handling
  • architectural scoreboard / reference model
  • directed verification
  • constrained-random program generation
  • random regression
  • SystemVerilog assertions
  • functional coverage
  • coverage closure and uncovered-item review

About

Custom 16-bit 3-stage pipelined CPU in SystemVerilog with UVM verification, RAW hazard handling, assertions, constrained-random testing, and functional coverage.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages