Validation of a CPU Core for FPGA

I recently bought a bunch of FPGA boards on eBay. All out of date stuff: Virtex II Pro, Virtex 4, Virtex 5, etc. I’m really building a collection of boards from Xilinx’s history here. The reason they’re all Xilinx and not Altera boards is, Xilinx has made license keys for very old versions of their ISE design suite available. That means, there’s essentially no Xilinx made before 2008 that you can’t play around with, without paying a cent for an ISE license. Cool. Altera hasn’t done the same, so there are tons of old Altera FPGA boards out there that are best send directly to the recyclers, as they’re not worth paying $ for a software license to use them. It’s kinda sad.

Anyway, what was this blog post about? Oh yeah, a CPU core. Well, to make use of these old FPGA boards it’ll be interesting to have a CPU core that I can use to do some simple tasks on them – such as utilize the onboard peripherals. Sure, you can communicate over RS232 using a state machine, but it’s more fun to make an actual CPU. And so, I started on that project…

The above image shows my CPU:

  1. Starting up after the reset line goes low.
  2. Reading address 0 (and prefetching addresses 1 & 2).
  3. Identifying it as a NOP.
  4. Delaying for a few clock cycles.
  5. Moving on to address 1 (which shoes that the program counter increments).
  6. Identifying it as a JMP 0x0000 (0x7E 0x00 0x00).
  7. Reading address 0 again (which shows that the program counter was changed to 0x0000).

That’s the extent of the CPU so far. It can only execute JMPs to arbitrary locations, and it can execute NOPs. Not very exciting? I think it is.

Anyway, I wanted to thoroughly test this functionality before adding more instructions to the CPU. I looked at other hobbyist CPUs on github, and most of them seem to run the CPU in the simulator against actual assembly code. That’s a bit too high-level for my taste. When I want to test the CPU against actual assembler, I’ll do Hardware-In-The-Loop simulation (or something like it). I’ll load the CPU into an FPGA and control it via another soft CPU, like MicroBlaze.

What I wanted was to use the kind of low-level verification that everyone learns in typical BSEE courses. I wanted to write SystemVerilog that would instrument my CPU and verify the outputs at points in time.

Here’s my first testcase:


halt = 0;

// TEST 1 – JMP addr16 – Test that we can make an infinite loop



// Pulse the reset line

reset = 1; #30 reset = 0;

// Wait for the first instruction to be read.

wait (CURRENT_CYCLE == 8’h02);

assert (address_bus == 8’h00);

assert (data_bus_register == 8’h01);

// Wait for the second instruction to be read.

wait (CURRENT_CYCLE == 8’h10);

assert (address_bus == 8’h01);

assert (data_bus_register == 8’h7E);

// Test that it loops back around

wait (CURRENT_CYCLE == 8’h1E);

assert (address_bus == 8’h00);

assert (data_bus_register == 8’h01);

wait (CURRENT_CYCLE == 8’h20);

assert (address_bus == 8’h01);

assert (data_bus_register == 8’h7E);


// Done with all tests.


Variables in all-caps are “global variables” that I’ve defined to send signal to helper modules. Variables in lowercase are actually connected to my CPU.

RUNNING starts/stops the simulation. CURRENT_TEST_NAME signals to a virtual ROM block which instructions should be provided to the CPU. CLOCK_RUNNING signals to the free-running clock generator that it should start/stop. CURRENT_CYCLE is incremented by the free-running clock generator. I use it to count cycles and determine when to assert various conditions.

Here’s my virtual ROM block:

if (CURRENT_TEST_NAME == “JMP addr16”) begin

    if (address_bus == 8’h00) begin

        data_bus_register <= 8’h01;


    else if (address_bus == 8’h01) begin

        data_bus_register <= 8’h7E;


    else if (address_bus == 8’h02 || address_bus == 8’h03) begin

        data_bus_register <= 8’h00;




        data_bus_register <= 8’hZ;



It should be simple to expand this for multiple tests. I can even load a virtual ROM from a binary file, for more complicated tests where if/elses will get too unwieldly.

I’ve put the full code here:

Leave a Reply

%d bloggers like this: