基于ICE40的RISC-V软核移植
本项目旨在将基于Murax SoC的RISC-V软核移植到ICE40 FPGA上,并通过实现一个流水灯应用程序来验证软核的正确性。
标签
嵌入式系统
FPGA
ICE40
RISCV
maskmoo
更新2023-03-28
575

项目介绍

本项目旨在将基于Murax SoC的RISC-V软核移植到ICE40 FPGA上,并通过实现一个流水灯应用程序来验证软核的正确性。

VexRiscv 、 Murax SoC和SpinalHDL三者的简介和关系:

VexRiscv是一个基于RISC-V指令集架构的处理器核心,由SpinalHDL社区开发,采用Scala语言进行设计。VexRiscv旨在为FPGA设计提供一个高性能、低功耗、可定制化的处理器核心,可以根据实际需求进行定制化设计。

Murax SoC是基于VexRiscv处理器核心的一个SoC设计,包含了处理器核心、内存控制器、总线接口、外设控制器等组件,可以在FPGA上搭建出符合自己需求的系统。Murax SoC使用了SpinalHDL进行设计,也是SpinalHDL社区的一个开源项目。

SpinalHDL是一个开源的硬件描述语言,类似于Verilog或VHDL,但是具有更强的类型系统和更高的抽象级别,使得硬件设计更加易于管理和维护。SpinalHDL也是SpinalHDL社区的一个开源项目,致力于提供一种更加灵活、高效、可重用的硬件设计方法。

VexRiscv、Murax SoC和SpinalHDL三者之间有着密切的关系。VexRiscv提供了高性能、可定制化的处理器核心,Murax SoC基于VexRiscv设计了一个完整的SoC系统,而SpinalHDL则提供了一种高效、灵活的硬件描述语言,为VexRiscv和Murax SoC的设计提供了强大的支持。

这次选择移植的是Murax SoC,Murax是一个非常轻的SoC并且比较适合于ICE40 FPGA,可以在没有任何外部组件的情况下工作:

移植思路

将基于Murax SoC的RISC-V软核移植到ICE40 FPGA的整体思路如下:

  1. 确定所需的软件和硬件资源:需要使用的软件包括RISC-V软核的代码和SoC的IP核,需要使用的硬件资源包括FPGA开发板和必要的接口和外设。
  2. 配置开发环境:需要下载并安装RISC-V的开发工具链,以及FPGA开发工具,例如Lattice Diamond或iCEcube2等。
  3. 修改SoC代码:使用SoC的IP核,可以根据需要修改Murax SoC的代码来适应目标FPGA。包括更改时钟和重映射I/O端口等。
  4. 生成Bit文件:使用FPGA开发工具,生成适合目标FPGA的位流文件。
  5. 下载Bit文件:将生成的位流文件下载到目标FPGA中。
  6. 测试:在FPGA上测试移植后的SoC代码,以确保其正常工作。

开发环境搭建

因为SpinalHDL是采用Scala语言进行设计的,所以需要安装JDK、sbt的依赖。

# JAVA JDK 8
sudo add-apt-repository -y ppa:openjdk-r/ppa
sudo apt-get update
sudo apt-get install openjdk-8-jdk -y
sudo update-alternatives --config java
sudo update-alternatives --config javac

# Install SBT - https://www.scala-sbt.org/
echo "deb https://dl.bintray.com/sbt/debian /" | sudo tee -a /etc/apt/sources.list.d/sbt.list
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
sudo apt-get update
sudo apt-get install sbt

# Verilator (for sim only, really needs 3.9+, in general apt-get will give you 3.8)
sudo apt-get install git make autoconf g++ flex bison
git clone http://git.veripool.org/git/verilator   # Only first time
unsetenv VERILATOR_ROOT  # For csh; ignore error if on bash
unset VERILATOR_ROOT  # For bash
cd verilator
git pull        # Make sure we're up-to-date
git checkout v3.916
autoconf        # Create ./configure script
./configure
make
sudo make install

BIN2RBT工具实现

由于使用开源FPGA工具链生成的文件是bin格式,但是使用LPC11U35FHI33作为下载工具为ICE40UP5k上传数据时,只支持RBT格式的文件。通过对比分析RBT文件格式实现了一个可以将BIN文件转换为RBT文件的脚本,后来经过Python大佬John Huang的优化解决了初版脚本在Linux平台的兼容性等问题。

375432636/bin2rbt (github.com)

Murax SoC移植

移植部分主要是根据核心板的硬件连接进行I/O端口的重映射。

# For the iCE40-EETREEV2 Board (iCE40UP5K-QFN48)

set_io LED_G 41
set_io LED_R 40
set_io LED_B 39

#set_io SW[0] 37
#set_io SW[1] 38
#set_io SW[2] 42
#set_io SW[3] 43

set_io clk   44

set_io RX    23
set_io TX    21

set_io USB_DP       10
set_io USB_DN       9
set_io USB_PULLUP   11


set_io P4_1     2
set_io P4_2     3
set_io P4_3     4
set_io P4_4     6

set_io io_pina[0] 37
set_io io_pina[1] 38
set_io io_pina[2] 42
set_io io_pina[3] 43
set_io io_pina[4] 36
set_io io_pina[5] 34
set_io io_pina[6] 32
set_io io_pina[7] 31


set_io io_pinb[0] 48
set_io io_pinb[1] 47
set_io io_pinb[2] 46
set_io io_pinb[3] 45
set_io io_pinb[4] 20
set_io io_pinb[5] 19
set_io io_pinb[6] 13
set_io io_pinb[7] 12


#spi
set_io SPI_SS   16
set_io SPI_SCK  15
set_io SPI_MOSI 17
set_io SPI_MISO 14

顶层代码修改:

`timescale 1ns / 1ps

module toplevel(
    input   clk,
    input   P4_2,   /* jtag_tck */
    input   P4_1,   /* jtag_tdi */
    output  P4_3,   /* jtag_tdo */
    input   P4_4,   /* jtag_tms */
    output  TX,
    input   RX,
    output [7:0] io_pina,
    output [7:0] io_pinb,
    output LED_G
  );

  wire [31:0] io_gpioA_read;
  wire [31:0] io_gpioA_write;
  wire [31:0] io_gpioA_writeEnable;
  wire io_mainClk;
  wire io_jtag_tck;

  SB_GB mainClkBuffer (
    .USER_SIGNAL_TO_GLOBAL_BUFFER (clk),
    .GLOBAL_BUFFER_OUTPUT ( io_mainClk)
  );

  SB_GB jtagClkBuffer (
    .USER_SIGNAL_TO_GLOBAL_BUFFER (P4_2),
    .GLOBAL_BUFFER_OUTPUT ( io_jtag_tck)
  );

  assign io_pina = io_gpioA_write[15 : 8];
  assign io_pinb = io_gpioA_write[7 : 0];

  assign LED_G =  0;

  Murax murax ( 
    .io_asyncReset(0),
    .io_mainClk (io_mainClk ),
    .io_jtag_tck(io_jtag_tck),
    .io_jtag_tdi(P4_1),
    .io_jtag_tdo(P4_3),
    .io_jtag_tms(P4_4),
    .io_gpioA_read       (io_gpioA_read),
    .io_gpioA_write      (io_gpioA_write),
    .io_gpioA_writeEnable(io_gpioA_writeEnable),
    .io_uart_txd(TX),
    .io_uart_rxd(RX)
  );        
endmodule

生成 Murax SoC 有两种方式,一种是只生成硬件内容,另一种是会讲一个预编译的程序固件放到RAM里面,下载FPGA文件后会自动运行(生成FPGA文件之前,需要把编译好的程序固件放入到VexRiscv/src/main/ressource/hex目录下命名为muraxDemo.hex)。

# To generate the SoC without any content in the ram
sbt "runMain vexriscv.demo.Murax"

# To generate the SoC with a demo program already in ram
sbt "runMain vexriscv.demo.MuraxWithRamInit"

进入到VexRiscv/scripts/Murax/iCE40_EETREEV2目录执行make compile命令,完成后最终的bin文件生成在bin目录下。

最后通过bin2rbt工具转换后即可烧录测试。

流水灯代码实现

安装RISCV的编译工具链

wget https://static.dev.sifive.com/dev-tools/riscv64-unknown-elf-gcc-20171231-x86_64-linux-centos6.tar.gz
tar -xzvf riscv64-unknown-elf-gcc-20171231-x86_64-linux-centos6.tar.gz
sudo mv riscv64-unknown-elf-gcc-20171231-x86_64-linux-centos6 /opt/riscv64-unknown-elf-gcc-20171231-x86_64-linux-centos6
sudo mv /opt/riscv64-unknown-elf-gcc-20171231-x86_64-linux-centos6 /opt/riscv
echo 'export PATH=/opt/riscv/bin:$PATH' >> ~/.bashrc

示例代码在VexRiscv/src/main/c/murax/目录,基于示例代码实现了ice40_blinky工程

//#include "stddefs.h"
#include <stdint.h>

#include "murax.h"

void print(const char*str){
    while(*str){
        uart_write(UART,*str);
        str++;
    }
}
void println(const char*str){
    print(str);
    uart_write(UART,'\n');
}

void delay(uint32_t loops){
    for(int i=0;i<loops;i++){
        int tmp = GPIO_A->OUTPUT;
    }
}

void main() {
    GPIO_A->OUTPUT_ENABLE = 0x000000FF;
    GPIO_A->OUTPUT = 0x00000000;
    println("Murax for iCE40-EETREE v2 ");
    const int nleds = 4;
    const int nloops = 500000;//500ms
    while(1){
        for(unsigned int i=0;i<nleds-1;i++){
            GPIO_A->OUTPUT = 1<<(i);
            delay(nloops);
        }
        for(unsigned int i=0;i<nleds-1;i++){
            GPIO_A->OUTPUT = (1<<(nleds-1))>>i;
            delay(nloops);
        }
    println("running");
    }
}

void irqCallback(){
}

执行make编译命令后在bin文件下生成程序固件

资源占用

After packing:
IOs          24 / 39
  IO_I3Cs    0 / 2
  IO_ODs     0 / 3
GBs          2 / 8
  GB_IOs     0 / 8
LCs          2746 / 5280
  DFF        1375
  CARRY      128
  CARRY, DFF 82
  DFF PASS   944
  CARRY PASS 33
BRAMs        22 / 30
WARMBOOTs    0 / 1
PLLs         0 / 1
MAC16s       0 / 8
SPRAM256KAs  0 / 4
HFOSCs       0 / 1
LFOSCs       0 / 1
RGBA_DRVs    0 / 1
LEDDA_IPs    0 / 1
I2Cs         0 / 2
SPIs         0 / 2

测试现象

硬件上需要将核心板上连接DapLink的TX/RX与ICE40芯片两个引脚之间的0欧姆电阻进行焊接,相当于可以直接通过Daplink在PC端映射的串口与FPGA进行通讯。

image-20230315215115257

打开串口,待rbt文件烧写完成后,可以在串口助手上观察到软件代码中的打印信息,核心板的4个LED轮流闪烁。

遇到的问题及解决

软核的选择问题

软核的选择上,一开始是打算选择PICORV32等软核,但是了解后发现很多软核的代码启动都是选择基于外部Flash的XIP形式而且,但是在我们的核心板上只能单独烧写RBT文件,并不能直接实现软核代码固件与FPGA固件的合并烧写。后来在网上查找最终找到类似murax这种可以方便的运行程序的软核进行移植。

参考链接

  1. 在Linux上构建iCE40 FPGA工具链
  2. 基于STM32G031 + iCE40UP5K的MCU/FPGA核心模块
  3. VexRiscv: A FPGA friendly 32 bit RISC-V CPU implementation (github.com)
  4. bin2rbt (github.com)
  5. icesugar: iCESugar FPGA Board (base on iCE40UP5k) (github.com)
附件下载
VexRiscv_SRC.zip
源码
top.rbt
rbt文件
团队介绍
坚持就是胜利
团队成员
maskmoo
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号