项目介绍
本项目旨在将基于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的整体思路如下:
- 确定所需的软件和硬件资源:需要使用的软件包括RISC-V软核的代码和SoC的IP核,需要使用的硬件资源包括FPGA开发板和必要的接口和外设。
- 配置开发环境:需要下载并安装RISC-V的开发工具链,以及FPGA开发工具,例如Lattice Diamond或iCEcube2等。
- 修改SoC代码:使用SoC的IP核,可以根据需要修改Murax SoC的代码来适应目标FPGA。包括更改时钟和重映射I/O端口等。
- 生成Bit文件:使用FPGA开发工具,生成适合目标FPGA的位流文件。
- 下载Bit文件:将生成的位流文件下载到目标FPGA中。
- 测试:在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进行通讯。
打开串口,待rbt文件烧写完成后,可以在串口助手上观察到软件代码中的打印信息,核心板的4个LED轮流闪烁。
遇到的问题及解决
软核的选择问题
软核的选择上,一开始是打算选择PICORV32等软核,但是了解后发现很多软核的代码启动都是选择基于外部Flash的XIP形式而且,但是在我们的核心板上只能单独烧写RBT文件,并不能直接实现软核代码固件与FPGA固件的合并烧写。后来在网上查找最终找到类似murax这种可以方便的运行程序的软核进行移植。
参考链接