PicoRV32 简单移植 – 基于Tang Nano 20K

/ 7评 / 0

说实话,国产FPGA的开发环境真的很简陋,但是应该有的都有吧,毕竟用iverilog和OpenFPGALoader的大神有的是,有简陋IDE已经很了不起了,不是吗?

PicoRV32内核项目地址.

https://github.com/YosysHQ/picorv32

其实整个CPU核心就只有一个文件.

这个处理器核心可以支持AXI-Lite接口或者原生内存接口,也支持中断(软件上稍微做得要多一些),或者作为协处理器(PCPI)存在.我们这次实现最最最简单的移植.即原生接口,没有外设,并对内核做一些定制(比如开启乘法,除法,压缩指令)

首先把picorv32.v塞到工程里,再写一个非常非常简单的顶层文件,注释已经在文件内.

`timescale 1 ns / 1 ps

module system (
	input            clk, // 输入时钟
	input            resetn, // 输入复位(Tang Nano 20K按键按下后为高,松开为低,所以实际逻辑会取反!)
	output           trap, // 陷阱(执行了不被支持的东西,处理器故障!)
	output reg [7:0] out_byte, // 数据输出
	output reg       out_byte_en // 当前out_byte有效
);

// 4096 32bit words = 16kB 内存
parameter MEM_SIZE = 4096;

wire mem_valid;
wire mem_instr;
reg mem_ready;
wire [31:0] mem_addr;
wire [31:0] mem_wdata;
wire [3:0] mem_wstrb;
reg [31:0] mem_rdata;

picorv32 #(
.ENABLE_MUL(1), // 乘法指令
.ENABLE_DIV(1), // 除法指令
.COMPRESSED_ISA(1) // 压缩指令
) picorv32_core (
    .clk         (clk         ),
    .resetn      (!resetn      ),
    .trap        (trap        ),
    .mem_valid   (mem_valid   ),
    .mem_instr   (mem_instr   ),
    .mem_ready   (mem_ready   ),
    .mem_addr    (mem_addr    ),
    .mem_wdata   (mem_wdata   ),
    .mem_wstrb   (mem_wstrb   ),
    .mem_rdata   (mem_rdata   )
);

reg [31:0] memory [0:MEM_SIZE-1];
initial $readmemh("firmware.hex", memory); // 固件,等下编译!

reg [31:0] m_read_data;
reg m_read_en;

// 下面代码会推断出BSRAM
always @(posedge clk) begin
    m_read_en <= 0;
    mem_ready <= mem_valid && !mem_ready && m_read_en;

    m_read_data <= memory[mem_addr >> 2];
    mem_rdata <= m_read_data;

    out_byte_en <= 0;

    (* parallel_case *)
    case (1)
        mem_valid && !mem_ready && !mem_wstrb && (mem_addr >> 2) < MEM_SIZE: begin
            m_read_en <= 1;
        end
        mem_valid && !mem_ready && |mem_wstrb && (mem_addr >> 2) < MEM_SIZE: begin
            if (mem_wstrb[0]) memory[mem_addr >> 2][ 7: 0] <= mem_wdata[ 7: 0];
            if (mem_wstrb[1]) memory[mem_addr >> 2][15: 8] <= mem_wdata[15: 8];
            if (mem_wstrb[2]) memory[mem_addr >> 2][23:16] <= mem_wdata[23:16];
            if (mem_wstrb[3]) memory[mem_addr >> 2][31:24] <= mem_wdata[31:24];
            mem_ready <= 1;
        end
        mem_valid && !mem_ready && |mem_wstrb && mem_addr == 32'h1000_0000: begin
            // 访问地址在0x10000000,则输出到IO上!
            out_byte_en <= 1;
            out_byte <= mem_wdata;
            mem_ready <= 1;
        end
    endcase
end

endmodule

综合没什么问题,然后开始做软件部分.

因为内核特别精简,默认的编译器是为RV32IG(G=MAFD)构建的,我们根本最多也只能启用M扩展,所以得自己先编译一个编译器.

git clone https://github.com/riscv/riscv-gnu-toolchain
cd riscv-gnu-toolchain
git submodule update --init --recursive
cd riscv32i
mkdir build; cd build
../configure --with-arch=rv32i
make -j$(nproc)

之后编译程序需要3个文件,这里PicoRV32项目也给出来了.

我们随便进入一个,比如Vivado例子里得,找到这三个文件,他就是我们要的固件.

编译过程

riscv32-unknown-elf-gcc -Os -ffreestanding -nostdlib -o firmware.elf firmware.S firmware.c --std=gnu99 -Wl,-Bstatic,-T,firmware.lds,-Map,firmware.map,--strip-debug -lgcc
riscv32-unknown-elf-objcopy -O binary firmware.elf firmware.bin
python3 makehex.py firmware.bin 4096 > firmware.hex

给Gowin新建一个GAO文件.

当out_byte_en上升沿时,触发.

绑定条件.

设置其他采样信息.

引脚约束.

布局布线下载就不用教了吧,打开GAO准备查看成果.

应该能顺利看到.

the PicoRV32 CPU seems to be working just fine.

至此,最简单的移植,完事,看看资源占用,这么看,官方说9K就适合验证软核,也没错嘛,最乞丐的版本.

关闭所有扩展的资源消耗.

  1. markman说道:

    不用仿真器,能用串口刷c编译的bin文件到rom里面运行么? 另外 c编译的bin文件怎么固化到fpga的?

  2. markman说道:

    如果编译软件的工具链做成windows下面的

    把uart等中断实现了, 这个项目应用会更广,因为很多人还在用windows下面的。这个核类似mcu的定位挺好的

  3. xiaguangbo说道:

    关于编译器,rust没有直接提供ch32v003使用的rv32ec的编译目标,在ch32rs项目里则使用指令集描述文档来支持,也就是说直接向LLVM后端传递编译目标

    riscv32ec-unknown-none-elf.json:
    “`json
    {
    “arch”: “riscv32”,
    “atomic-cas”: false,
    “cpu”: “generic-rv32”,
    “crt-objects-fallback”: “false”,
    “data-layout”: “e-m:e-p:32:32-i64:64-n32-S32”,
    “eh-frame-header”: false,
    “emit-debug-gdb-scripts”: false,
    “features”: “+e,+c,+forced-atomics”,
    “linker”: “rust-lld”,
    “linker-flavor”: “gnu-lld”,
    “llvm-target”: “riscv32”,
    “llvm-abiname”: “ilp32e”,
    “max-atomic-width”: 32,
    “panic-strategy”: “abort”,
    “relocation-model”: “static”,
    “target-pointer-width”: “32”
    }
    “`

    具体代表什么可以问deepseek,非常简单易懂。picorv32也有rust支持

  4. xiaguangbo说道:

    固件的烧录是将firmware.hex文件放到高云项目的src下就能一起编译到fpga里,还是需要创建个什么ip核再把程序放进进去

  5. xiaguangbo说道:

    启动时要按一下resetn对应的开发板上的按钮

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注