CASE STUDY / 01 / RTL DESIGN & VERIFICATION
From instruction to signal.
Building a five-stage RISC-V-subset processor in SystemVerilog, with tests for hazards, delayed memory responses, and faults. The central design problem is keeping instructions in order when memory and control flow disagree.
THE PROJECT IN ONE MINUTE
My engineering work
Developing the pipeline, memory handshakes, and self-checking tests. The work connects datapath control with observable register and memory state.
Inspect first
Follow one fault-ordering example, then inspect the testbench and saved simulator output.
Project status
Simulation-tested educational core. Read the supported scope and earlier milestones.
Five stages. Instructions kept in order.
Fetch, decode, execute, memory, and writeback split instruction processing into stages. Forwarding and stalls handle dependencies. Separate instruction and data ports use tagged ready/valid handshakes, allowing their responses to arrive independently.
An invalid speculative fetch cannot stop the core immediately: an older taken branch might make that fetch irrelevant. The design carries a squashable fault token through the pipeline, so instruction age determines whether it is reported or discarded.
Making the behavior explainable.
Keep memory transactions identifiable
Tagged responses let the core match data to requests and drain obsolete instruction replies after a redirect.
Respect older work
A pending store or load must finish before a younger fault stops the core. Testbench checks cover transaction counts and architectural state, rather than only a PASS flag.
Use a separate reference
Earlier memory regressions compare RTL state with a Python sequential instruction interpreter. The latest fetch tests independently assert the expected fault, register, and memory outcomes.
Development approach: AI-assisted development and review supported this project. My portfolio contribution is the evolving design, verification setup, and documented decisions shown here; the linked source makes that work inspectable.
Fetch faults. In instruction order.
72 / 72 scenario runs passed
Twelve scenarios run under six internal/external timing configurations. Each checks 32 registers and 1,024 RAM words: 76,032 repeated final-state comparisons. The suite includes 48 sticky fetch-fault runs and 24 valid/reset runs.
One boundary. Three different correct outcomes.
Examples from the captured passing simulator log and the checking testbench ↗, not illustrative performance numbers.
0xffc redirects inwardInvalid speculative fetch is discarded; no sticky fault; expected marker 41 observed.0xffcSequential fetch at 0x1000 reports cause 1, with fault PC and tval both 0x1000.0xffc targets 0x1000The older link write retires; the younger invalid fetch produces cause 1 at 0x1000.Ordering under load: a separate SW→LW case must finish two older data transactions before the invalid fetch stops the core. The captured external-memory runs report D=2, cause=1 and pc=tval=0x1000. The exact test sequence ↗ and runner output ↗ let a reviewer check the causal claim; these are modeled, not physical bus faults.
Earlier results and implementation history
The earlier fault-stop, external-port, internal-memory, and ALU results remain separate from the latest fetch suite. Their original logs, source revisions, commands, and waveforms are preserved in the development archive.
Take the next step through the toolchain.
The next processor milestone is a synthesizable memory arrangement, followed by synthesis analysis and a small board-level demonstration. I’m first exercising that tool flow on the smaller FIFO block. Processor timing and FPGA results will be published when measured.
For a closer review, follow the current source and one regression before exploring the historical notebook.
Discuss the verification work ↗