4 minute read

RISC-V Fibonacci

If you designed your own RISC-V CPU and want to open-source it, one of the best first blog topics is:

“How to run C code on your custom RISC-V processor, end-to-end.”

In this post, we use the TRIX-V SoC and run a Fibonacci C program all the way from compilation to simulation and FPGA output.

Table of Contents

Run C Code: Calculate the Fibonacci Number

In this section, we run a Fibonacci program on the TRIX-V SoC.

Fibonacci Definition

The Fibonacci sequence is defined as:

  • F(0) = 0
  • F(1) = 1
  • F(n) = F(n-1) + F(n-2) for n > 1

Our program calculates the nth Fibonacci number iteratively and writes intermediate values to address 0x400.

0x400 is the UART TX register in this system. Writing to this address pushes data into the UART FIFO, then the UART transmits it.

C Program

#include <stdint.h>

int main() {
    volatile uint32_t *ptr = (volatile uint32_t *)0x400;

    int n = 40; // Change this value to test

    int a = 0;
    int b = 1;
    int temp;

    for (int i = 0; i < n; i++) {
        temp = a + b;
        a = b;
        b = temp;
        *ptr = a;
    }

    while (1);
    // a: nth Fibonacci number
}

Expected behavior: after execution, the Fibonacci result sequence is written through UART, and for n = 40, the expected final value is 102334155.

Step 1: Compile C to RISC-V Assembly (Godbolt)

Use Godbolt Compiler Explorer to generate RISC-V assembly from the C code. Godbolt is an online tool that compiles code and shows the resulting assembly instructions.

  1. Paste the Fibonacci C code into Godbolt.
  2. Select a RISC-V 32-bit GCC target.
  3. Compile and inspect the assembly output.

In this flow, we use RISC-V (32-bits) gcc 14.2.0.

Godbolt Fibonacci Assembly
Figure 1: Fibonacci C code compiled to RISC-V assembly using Godbolt

Step 2: Convert Assembly to Machine Code

Use the online assembler: riscvasm.lucasteske.dev

  1. Copy the generated assembly from Godbolt.
  2. Paste it into the assembler input.
  3. Add stack pointer initialization:
    li sp, 1020
    

    This sets sp to the last data memory address (255 * 4 = 1020). In this case, data memory is 256 words (1024 bytes), so the stack starts at the end of this region.

  4. Click Build.
  5. Copy the generated machine code (hex instructions).
RISC-V Assembler Input
Figure 2: RISC-V assembly converted to machine code using an online assembler
RISC-V Assembler Output
Figure 3: Machine code output from the assembler

Step 3: Load Program into TRIX-V Instruction Memory

Copy the generated machine code into your instruction memory initialization file, for example:

tb/trixv.imem

This file is used to preload program instructions before simulation starts.

Step 4: Run Simulation

You can run the Fibonacci test in two common ways. First, change to the scripts directory:

cd scripts

If you run with a Makefile, run the following command in the terminal:

make simulate

If you run with Vivado, create a Vivado prjoct: Run the following command in the terminal:

vivado -mode batch -source src/create_project.tcl

Or open Vivado GUI, from Tools menu select run Tcl script... and select the provided script create_project.tcl. Then Run simulation:

  • Click Restart simulation.
  • Click Run all.

Reference Terminal Output:

# run 1000ns
Starting Fibonacci test...
INFO: [USF-XSim-96] XSim completed. Design snapshot 'test_trixv_mc_fibo_behav' loaded.
INFO: [USF-XSim-97] XSim simulation ran for 1000ns
launch_simulation: Time (s): cpu = 00:00:09 ; elapsed = 00:00:10 . Memory (MB): peak = 7988.883 ; gain = 20.613 ; free physical = 11296 ; free virtual = 29221
restart
INFO: [Wavedata 42-604] Simulation restarted
run all
Starting Fibonacci test...
Test Passed: dmem[250] = 102334155 (Expected: 102334155)

[1;32m
*************************************************
*   _______     _       ______    ______   _    *
*  |_   __ \   / \    .' ____ \ .' ____ \ | |   *
*    | |__) | / _ \   | (___ \_|| (___ \_|| |   *
*    |  ___/ / ___ \   _.____`.  _.____`. | |   *
*   _| |_  _/ /   \ \_| \____) || \____) ||_|   *
*  |_____||____| |____|\______.' \______.'(_)   *
*                                               *
*************************************************
[0m
All tests passed!
$finish called at time : 1060020 ns : File "tb/test_trixv_mc_fibo.sv" Line 76

Step 5: Run Fibonacci on FPGA

The constraints file is set for Digilent Nexys Video (Artix-7 XC7A200T):

constraints/nexys_video.xdc

If you use a different board, update constraints accordingly.

Instruction memory is initialized in src/top_trixv_mc.sv using:

parameter IMEM_INIT_FILE = "trixv.imem"

You need a USB-to-TTL Serial Cable to connect the FPGA UART pins to your computer. Connect the adapter’s RX pin to the FPGA’s UART TX pin (defined in constraints) and GND to GND.

USB-to-TTL
Figure 4: USB-to-TTL Serial Cable for FPGA UART Connection

After programming the FPGA:

  1. Open a serial terminal (PuTTY, CuteCom, etc.).
  2. Set the correct COM port and baud rate (115200).
  3. Observe Fibonacci output bytes from UART.

You can also use scripts/read_fibo.py python script. This Python script reads raw Fibonacci bytes from TRIX-V UART and prints annotated decimal values.

FPGA UART Output
Figure 5: FPGA UART output when running the Fibonacci program

This confirms the TRIX-V SoC computes Fibonacci correctly and that CPU, memory, and UART all operate correctly on real hardware.

FPGA Waveform (Improved the image qualiy by Nano Bnana)
Figure 6: FPGA Waveform

References