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

/ 2评 / 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的?

发表回复

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