Tuesday, October 2, 2018

RISC-V Take I

RISC-V explained in two words: "Frozen ISA" - indeed this is all there is: a RISC Instruction set that is fixed and guarded by an foundation. Products designed to the fixed RISC-V ISA should remain compatible and working as long as they adhere to the ISA, that is the all point there is, nothing more.

There is however no known good implementation of RISC-V, there are many implementations but none of them is the golden reference.

picorv32 is nice and simple implementation written in verilog, with simple adapter to provide AXI bus support. It did take maybe an hour to convert it to Vivado IP Catalog IP core, so it looks in IP Integrator:

Now it takes just a few mouse clicks to create some simplest RISC-V SoC system:


For initial testing I just added AXI BRAM and then used Vivado automation to connect the bus infra for me.

Next is testing right? Now we need some C compiler or assembler at least for RISC-V, this should be simple right? Well no - it is not that easy to find windows executables for RISC-V. Well well, there is F32C project that does include those binaries, well binaries are actually hosted by FPGArduino web.

I have implemented F32C in FPGA before, I have used the IDE and compiled C code for f32c using their compiler. So it must work. It must produce valid and working code? Right? The compiled programs do work, I know they do.

As next I create smallest test program (in assembler) to compile a few instructions for the picorv32 system.This is what f32c/fpgarduino riscv compiler emitted in listing file:

   9              loop1:
  10 0010 63080000 beq zero,zero,loop1
  11 0014 6F000001 j loop1
  12 0018 6F000001 j loop1
  13             
  14              loop2:
  15 001c 630E0000 beq zero,zero,loop2
  16 0020 6F00C001 j loop2
  17 0024 6F000001 j loop1

What!? This can not be! Can it be that risc-v has NO relative jumps or branches !?
Look at the code generated, both jump and branch instruction addressing is absolute.

This can not be! Looking at RISC-V specification. It clearly says that both branches and jumps use relative addressing. I am looking at the listing again.. absolute addresses? But the code generated by this compiler works in FPGA, I know it does. It really does. Well if executed by f32c implementation of RISC-V...

I just cant believe it, did f32c developers really changed the relative addressing to absolute addressing? And then patched the GCC and provided binaries for this patched riscv toolchain for everybody to download? And wonder why nothing works?

I just can describe how I felt when I realized that the f32c/fpgarduino developers indeed are using absolute addressing compiler and broken softcore. I was not happy at all.

Next try, more search.. and found, gnu-mcu-eclipse toolchain binaries for all host OS :) !
It takes some reading to find different commandline switches needed:

riscv-none-embed-gcc.exe -c -march=rv32i -mabi=ilp32 -Wa,-adhln -g start.S >start.lst

OK, opcode for relative branch to own location (forever loop) is 0x63000000 and NOP is 0x13000000 this is all I wanted to know for starters.

Double click on the BRAM and then in COE file editor init values can be entered:

NOP
NOP
L1: beq zero,zero, L1
NOP

Now it is exciting, does picorv32 execute the branch correctly?


And it is working - program counter goes from 0 to 4 then to 8 where the branch instruction is, next location is also fetched, then execution continues again from location 8 the forever loop is working.

It is clearly visible in the trace that there is pretty large latency fetching the instructions from BRAM over AXI bus. Sure this is far away from optimal, but at least it works, and it is now instantly possible to create Vivado design with picorv32 that can use any of the AXI IP cores available for Vivado.
For faster BRAM access it would possible make sense to split the picorv32 bus address space between AXI and LMB and put the BRAM block on LMB bus. This is also not complicated, but right now not of primary importance.

1 comment:

  1. Hi Antti,

    I just bumped into this post, and can't reproduce your results with the f32c's toolchain, which (in riscv case) is a combo of totally unpatched gcc-7.2 and binutils 2.29. Here's the output:

    % riscv32-elf-gcc -march=rv32i -mabi=ilp32 -ffreestanding -c test.S
    % riscv32-elf-objdump -S test.o
    test.o: file format elf32-littleriscv
    Disassembly of section .text:

    00000000 :
    0: 00000063 beqz zero,0
    4: ffdff06f j 0
    8: ff9ff06f j 0

    0000000c :
    c: 00000063 beqz zero,c
    10: ffdff06f j c
    14: fedff06f j 0

    NB f32c uses this toolchain 2017, so I'm puzzled on how did you get the results you posted?

    ReplyDelete