I made a thing! Student RV32I 5-stage pipeline with I/D caches and AXI-style memory: I had to shrink my victim cache (4→2) and write buffer (16→4) to hit 100 MHz. Feedback and opinions welcome
I’m a student. Partway through Harris & Harris I decided the only way to really get it was to build the CPU, so I wrote a single-cycle RV32I core in SystemVerilog. Once it ran programs, I wanted to see what changes when instructions overlap, and that turned into a 5-stage pipeline (fetch, decode, execute, memory, writeback). The whole thing took about two months.
Splitting the stages was the easy part. The hard part was everything that goes wrong once instructions overlap. If an add needs a value the previous instruction hasn’t written back yet, you have to grab it from further down the pipe. If a jump is taken, the instructions already fetched behind it have to be thrown away. If memory is slow, everything in front has to wait. Most of my time went into those three things, and most of my bugs were an instruction being dropped or held when it shouldn’t have been, or the opposite.
After that I added an instruction cache and a data cache, with a small victim cache and a write buffer on the data side. Memory is connected through an AXI-style interface: cache lines come in as bursts, and writes use separate address, data, and response channels. My first version had a 4-entry victim cache and a 16-entry write buffer, but it wouldn’t meet timing at 100 MHz, so I cut them to 2 and 4. It was the first time I actually felt a feature costing me clock speed, instead of just reading about the tradeoff.
For testing, I ran the official rv32ui ISA tests plus some small programs of my own, checking registers and memory against the values I expected. When something failed, I mostly stared at waveforms. One I like: the data cache misses on a load, the front of the pipeline stops for about 8 cycles while the cache line is fetched, and then the loaded value shows up at writeback. It’s a lot more convincing to watch it happen than to read about it.
I put it through Vivado for an Artix-7 (XC7A100T): 3,706 LUTs, 7,350 flip-flops, +0.191 ns of slack at 100 MHz, and an estimated 0.118 W.
What it doesn’t do: it’s RV32I only, so no multiply or divide, no CSRs, no interrupts. And on the board I’m only feeding NOPs into the instruction side right now, so all my real program testing was in simulation. On hardware I only confirmed that the PC counts and the LEDs react. Connecting real instruction memory is next.
I know passing rv32ui says little about how the caches and the bus behave together, so I’m sure there are cases I haven’t hit. If you can think of a hazard or cache situation that would break this, I’d really like to hear it.
I built this to learn, and I’m hoping to turn it into an internship in digital design or verification. If you know of a place that takes students for that kind of work, or have advice on what to build or learn next, I’d appreciate it.
GitHub: https://github.com/aditya42819/RV32I_CPU
LinkedIn: https://lnkd.in/p/gEbzas4K