【verilog开源赏鉴】PicoRV32:A Size-Optimized RISC-V CPU
项目概述
PicoRV32是一个开源的RISC-V RV32IMC指令集的CPU核心项目,目前github上star量最高的verilog开源项目,2.8K stars。这个项目非常的厉害,厉害到什么程度呢?比如某在售的FPGA卖点介绍时会写明可运行PicoRV:
verilog代码比例为45.5%,纯度很高:
这个项目的主要特点包括:
- 可配置性:PicoRV32可以根据需求配置为不同的核心类型,包括RV32E、RV32I、RV32IC、RV32IM或RV32IMC。它还提供了内置中断控制器的选项,使得它能够适应不同的应用场景。
- 尺寸和性能:PicoRV32设计紧凑,在Xilinx 7系列FPGA架构中占用大约750-2000个逻辑单元(LUTs)。它能够达到很高的时钟频率,在7系列Xilinx FPGAs上为250-450 MHz。
- 接口选项:PicoRV32支持原生内存接口或AXI4-Lite主控,这使得它可以更容易地与现有的系统设计集成。同时,它还提供了中断支持和协处理器接口的选项。
- 易于集成:由于其高时钟频率和紧凑的设计,PicoRV32可以轻松地集成到大多数现有的FPGA和ASIC设计中,而不会引入时钟域的问题。
- 文档和示例:该项目提供了丰富的文档和示例代码,包括RISC-V工具链、用户指南、FPGA脚本等,这为初学者学习Verilog和FPGA设计提供了便利。
- 许可证:PicoRV32使用ISC许可证,这是一种宽松的开源许可证,允许用户自由使用、修改和分发代码。
- 测试固件:项目包含了一个简单的测试固件,用于运行基本的测试和执行中断处理等操作,方便用户验证其设计和功能。
总的来说,PicoRV32是一个适合初学者学习Verilog和FPGA设计的优秀项目,同时也适用于需要小型、高效CPU核心的更高级应用。
CPU核部分的verilog代码量为3049行,为啥如此精确?因为全部module都写在一个模块里了:
其代码风格非常教科书,对齐工整赏心悦目,非常适合初学者阅读:
/***************************************************************
* picorv32_axi_adapter
***************************************************************/
module picorv32_axi_adapter (
input clk, resetn,
// AXI4-lite master memory interface
output mem_axi_awvalid,
input mem_axi_awready,
output [31:0] mem_axi_awaddr,
output [ 2:0] mem_axi_awprot,
output mem_axi_wvalid,
input mem_axi_wready,
output [31:0] mem_axi_wdata,
output [ 3:0] mem_axi_wstrb,
input mem_axi_bvalid,
output mem_axi_bready,
output mem_axi_arvalid,
input mem_axi_arready,
output [31:0] mem_axi_araddr,
output [ 2:0] mem_axi_arprot,
input mem_axi_rvalid,
output mem_axi_rready,
input [31:0] mem_axi_rdata,
// Native PicoRV32 memory interface
input mem_valid,
input mem_instr,
output mem_ready,
input [31:0] mem_addr,
input [31:0] mem_wdata,
input [ 3:0] mem_wstrb,
output [31:0] mem_rdata
);
reg ack_awvalid;
reg ack_arvalid;
reg ack_wvalid;
reg xfer_done;
assign mem_axi_awvalid = mem_valid && |mem_wstrb && !ack_awvalid;
assign mem_axi_awaddr = mem_addr;
assign mem_axi_awprot = 0;
assign mem_axi_arvalid = mem_valid && !mem_wstrb && !ack_arvalid;
assign mem_axi_araddr = mem_addr;
assign mem_axi_arprot = mem_instr ? 3'b100 : 3'b000;
assign mem_axi_wvalid = mem_valid && |mem_wstrb && !ack_wvalid;
assign mem_axi_wdata = mem_wdata;
assign mem_axi_wstrb = mem_wstrb;
assign mem_ready = mem_axi_bvalid || mem_axi_rvalid;
assign mem_axi_bready = mem_valid && |mem_wstrb;
assign mem_axi_rready = mem_valid && !mem_wstrb;
assign mem_rdata = mem_axi_rdata;
always @(posedge clk) begin
if (!resetn) begin
ack_awvalid <= 0;
end else begin
xfer_done <= mem_valid && mem_ready;
if (mem_axi_awready && mem_axi_awvalid)
ack_awvalid <= 1;
if (mem_axi_arready && mem_axi_arvalid)
ack_arvalid <= 1;
if (mem_axi_wready && mem_axi_wvalid)
ack_wvalid <= 1;
if (xfer_done || !mem_valid) begin
ack_awvalid <= 0;
ack_arvalid <= 0;
ack_wvalid <= 0;
end
end
end
endmodule
一个简单的结构实例如下,可以看出确实结构非常的简练规整:
项目介绍
以下为项目README介绍部分。
PicoRV32 是一个实现了 RISC-V RV32IMC 指令集 的 CPU 核心。它可以被配置为 RV32E、RV32I、RV32IC、RV32IM 或 RV32IMC 核心,并可选择性地包含内置中断控制器。
工具(gcc、binutils 等)可以通过 RISC-V 网站 获取。与 PicoRV32 捆绑的示例需要在 /opt/riscv32i[m][c] 中安装各种 RV32 工具链。请参阅下文的 构建纯 RV32I 工具链 了解详细信息。许多 Linux 发行版现在包括了 RISC-V 的工具(例如 Ubuntu 20.04 有 gcc-riscv64-unknown-elf)。使用这些工具编译时,请相应地设置 TOOLCHAIN_PREFIX(例如 make TOOLCHAIN_PREFIX=riscv64-unknown-elf-)。
PicoRV32 是自由且开放的硬件,根据 ISC 许可证(一种与 MIT 许可证或 2-clause BSD 许可证条款相似的许可证)许可。
目录
- 特性和典型应用
- 此存储库中的文件
- Verilog 模块参数
- 每条指令的周期性能
- PicoRV32 本地内存接口
- Pico 协处理器接口 (PCPI)
- ���于 IRQ 处理的自定义指令
- 构建纯 RV32I 工具链
- 为 PicoRV32 链接新立库 (newlib) 的二进制文件
- 评估:在 Xilinx 7 系列 FPGA 上的时间和利用率
特性和典型应用
- 小型(在 7 系列 Xilinx 架构中为 750-2000 LUTs)
- 高 fmax(在 7 系列 Xilinx FPGA 上为 250-450 MHz)
- 可选择本地内存接口或 AXI4-Lite 主控
- 可选择 IRQ 支持(使用简单的自定义 ISA)
- 可选择协处理器接口
这款 CPU 旨在作为 FPGA 设计和 ASIC 中的辅助处理器使用。由于其高 fmax,它可以集成到大多数现有设计中,而不会跨越时钟域。当以较低频率运行时,它将有很多时序余地,因此可以添加到设计中,而不会妨碍时序闭合。
为了更小的尺寸,可以禁用对寄存器 x16..x31 以及 RDCYCLE[H]、RDTIME[H] 和 RDINSTRET[H] 指令的支持,将处理器转变为 RV32E 核心。
此外,可以选择双端口和单端口寄存器文件实现。前者提供更好的性能,而后者则使核心更小。
注意:在实现寄存器文件的专用内存资源中,如许多 FPGA,禁用 16 个上级寄存器和/或禁用双端口寄存器文件可能不会进一步减小核心大小。
核心存在三种变体:picorv32、picorv32_axi 和 picorv32_wb。 第一个提供了一个简单的本地内存接口,可以很容易地在简单环境中使用。picorv32_axi 提供了一个 AXI-4 Lite 主控接口,可以很容易地与已经使用 AXI 标准的现有系统集成。picorv32_wb 提供了一个 Wishbone 主控接口。
提供了一个单独的核心 picorv32_axi_adapter 来在本地内存接口和 AXI4 之间进行桥接。这个核心可以用来创建自定义核心,这些核心包括一个或多个 PicoRV32 核心,以及本地 RAM、ROM 和内存映射的外围设备,它们使用本地接口进行通信,并通过 AXI4 与外部世界通信。
可选的 IRQ 特性可以用来响应外部事件,实现故障处理程序,或者捕获来自更大 ISA 的指令并在软件中模拟它们。
可选的 Pico 协处理器接口 (PCPI) 可以用来在外部协处理器中实现非分支指令。包括在此包中的 PCPI 核心实现 M 标准扩展指令 MUL[H[SU|U]] 和 DIV[U]/REM[U]。
此存储库中的文件
picorv32.v
这个 Verilog 文件包含以下 Verilog 模块:
简单地将此文件复制到您的项目中。
Makefile 和 testbenches
一个基本的测试环境。运行 make test 以运行标准配置的标准测试台 (testbench.v)。还有其他的测试台和配置。请参阅 Makefile 中的 test_* 目标了解详细信息。
运行 make test_ez 以运行 testbench_ez.v,这是一个非常简单的测试台,不需要外部固件 .hex 文件。当 RISC-V 编译器工具链不可用时,这在环境中非常有用。
注意:测试台使用的是 Icarus Verilog。然而,Icarus Verilog 0.9.7(撰写本文时的最新版本)有一些错误,阻止了测试台的运行。升级到 Icarus Verilog 的最新 github master 以运行测试台。
firmware/
一个简单的测试固件。它运行 tests/ 中的基本测试,一些 C 代码,测试 IRQ 处理和乘法 PCPI 核心。
firmware/ 中的所有代码都在公共领域。简单地复制任何您可以使用的代码。
tests/
来自 riscv-tests 的简单的指令级测试。
dhrystone/
另一个简单的测试固件,运行 Dhrystone 基准测试。
picosoc/
一个使用 PicoRV32 的简单示例 SoC,它可以从内存映射的 SPI flash 直接执行代码。
scripts/
针对不同的(综合)工具和硬件架构的各种脚本和示例。
Verilog 模块参数
以下 Verilog 模块参数可用于配置 PicoRV32 核心。
ENABLE_COUNTERS(默认 = 1)
此参数启用对 RDCYCLE[H]、RDTIME[H] 和 RDINSTRET[H] 指令的支持。如果将 ENABLE_COUNTERS 设置为零,则这些指令将像任何其他不支持的指令一样导致硬件陷入。
注意:严格来说 RDCYCLE[H]、RDTIME[H] 和 RDINSTRET[H] 指令对于 RV32I 核心来说不是可选的。但是,一旦应用程序代码经过调试和分析,它们可能不会被错过。这些指令对于 RV32E 核心是可选的。
ENABLE_COUNTERS64(默认 = 1)
此参数启用对 RDCYCLEH、RDTIMEH 和 RDINSTRETH 指令的支持。如果此参数设置为 0,而 ENABLE_COUNTERS 设置为 1,则只有 RDCYCLE、RDTIME 和 RDINSTRET 指令可用。
ENABLE_REGS_16_31(默认 = 1)
此参数启用对 x16..x31 寄存器的支持。RV32E ISA 排除了这些寄存器。然而,RV32E ISA 规范要求当代码尝试访问这些寄存器时,会产生硬件陷阱。这在 PicoRV32 中没有实现。
ENABLE_REGS_DUALPORT(默认 = 1)
寄存器文件可以有两个或一个读端口实现。双端口寄存器文件可以稍微提高性能,但也可能增加核心的大小。
LATCHED_MEM_RDATA(默认 = 0)
如果外部电路在事务之后保持 mem_rdata 稳定,请将其设置为 1。在默认配置中,PicoRV32 核心只期望 mem_rdata 输入在 mem_valid && mem_ready 的周期内有效,并在内部锁存该值。
此参数仅适用于 picorv32 核心。在 picorv32_axi 和 picorv32_wb 核心中,这隐含地设置为 0。
TWO_STAGE_SHIFT(默认 = 1)
默认情况下,移位操作分为两个阶段执行:首先是按 4 位单位移位,然后是按 1 位单位移位。这加快了移位操作的速度,但也增加了额外的硬件。将此参数设置为 0 可以禁用两阶段移位,以进一步减小核心的大小。
BARREL_SHIFTER(默认 = 0)
默认情况下,移位操作是通过连续按小量(见上文的 TWO_STAGE_SHIFT)移位来执行的。启用此选项时,改用桶式移位器。
TWO_CYCLE_COMPARE(默认 = 0)
这通过在条件分支指令中增加一个额外的 FF 阶段来稍微放宽最长数据路径,以增加一个额外的时钟周期延迟。
注意:启用此参数在综合流程中启用重定时(aka "寄存器平衡")时最有效。
TWO_CYCLE_ALU(默认 = 0)
这在 ALU 数据路径中增加了一个额外的 FF 阶段,以改善时序,以牺牲所有使用 ALU 的指令的额外时钟周期为代价。
注意:启用此参数在综合流程中启用重定时(aka "寄存器平衡")时最有效。
COMPRESSED_ISA(默认 = 0)
这启用了对 RISC-V 压缩指令集的支持。
CATCH_MISALIGN(默认 = 1)
将其设置为 0 以禁用捕获未对齐内存访问的电路。
CATCH_ILLINSN(默认 = 1)
将其设置为 0 以禁用捕获非法指令的电路。
将此选项设置为 0 时,核心仍将在 EBREAK 指令上产生陷阱。启用 IRQ 时,EBREAK 通常触发 IRQ 1。将此选项设置为 0 时,EBREAK 将捕获处理器而不触发中断。
ENABLE_PCPI(默认 = 0)
将其设置为 1 以启用外部 Pico 协处理器接口 (PCPI)。外部接口对于内部 PCPI 核心(如 picorv32_pcpi_mul)不是必需的。
ENABLE_MUL(默认 = 0)
此参数在内部启用 PCPI 并实例化 picorv32_pcpi_mul 核心,该核心实现 MUL[H[SU|U]] 指令。只有当 ENABLE_PCPI 也被设置时,外部 PCPI 接口才有效。
ENABLE_FAST_MUL(默认 = 0)
此参数在内部启用 PCPI 并实例化 picorv32_pcpi_fast_mul 核心,该核心实现 MUL[H[SU|U]] 指令。只有当 ENABLE_PCPI 也被设置时,外部 PCPI 接口才有效。
如果同时设置了 ENABLE_MUL 和 ENABLE_FAST_MUL,则将忽略 ENABLE_MUL 设置,并实例化快速乘法器核心。
ENABLE_DIV(默认 = 0)
此参数在内部启用 PCPI 并实例化 picorv32_pcpi_div 核心,该核心实现 DIV[U]/REM[U] 指令。只有当 ENABLE_PCPI 也被设置时,外部 PCPI 接口才有效。
ENABLE_IRQ(默认 = 0)
将其设置为 1 以启用 IRQ。(请参阅下文 "用于 IRQ 处理的自定义指令" 中有关 IRQ 的讨论)
ENABLE_IRQ_QREGS(默认 = 1)
将其设置为 0 以禁用对 getq 和 setq 指令的支持。如果没有 q-寄存器,则中断返回地址将存储在 x3(gp)中,IRQ 位掩码存储在 x4(tp)中,这是 RISC-V ABI 的全局指针和线程指针寄存器。来自普通 C 代码的代码生成不会与这些寄存器交互。
当 ENABLE_IRQ 设置为 0 时,始终禁用对 q-寄存器的支持。
ENABLE_IRQ_TIMER(默认 = 1)
将其设置为 0 以禁用对 timer 指令的支持。
当 ENABLE_IRQ 设置为 0 时,始终禁用对计时器的支持。
ENABLE_TRACE(默认 = 0)
使用 trace_valid 和 trace_data 输出端口生成执行跟踪。要演示此功能的运行,请运行 make test_vcd 创建跟踪文件,然后运行 python3 showtrace.py testbench.trace firmware/firmware.elf 进行解码。
REGS_INIT_ZERO(默认 = 0)
将其设置为 1 以使用 Verilog initial 块将所有寄存器初始化为零。这对于仿真或形式验证非常有用。
MASKED_IRQ(默认 = 32'h 0000_0000)
在此位掩码中,1 位对应于永久禁用的 IRQ。
LATCHED_IRQ(默认 = 32'h ffff_ffff)
在此位掩码中,1 位表示相应的 IRQ 是 "latched",即当 IRQ 线只在一周期内高时,中断将被标记为待处理,并保持待处理状态,直到中断处理程序被调用(也就是 "脉冲中断" 或 "边沿触发中断")。
将此掩码中的位设置为 0 可将中断线转换为 "电平敏感" 中断。
PROGADDR_RESET(默认 = 32'h 0000_0000)
程序的起始地址。
PROGADDR_IRQ(默认 = 32'h 0000_0010)
中断处理程序的起始地址。
STACKADDR(默认 = 32'h ffff_ffff)
当此参数的值不同于 0xffffffff 时,则在重置时将寄存器 x2(堆栈指针)初始化为此值。(所有其他寄存器保持未初始化状态。)请注意,RISC-V 调用约定要求堆栈指针在 16 字节边界(对于 RV32I 软浮点调用约定为 4 字节)上对齐。
每条指令的周期性能
简短提醒:此核心针对大小和 fmax 进行优化,而不是性能。
除非另有说明,以下数字适用于启用 ENABLE_REGS_DUALPORT 并连接到可以在一个时钟周期内满足请求的内存的 PicoRV32。
平均每条指令的周期数(CPI)大约为 4,这取决于代码中指令的混合。个别指令的 CPI 数字可以在下面的表格中找到。"CPI (SP)" 列包含没有 ENABLE_REGS_DUALPORT 的核心的 CPI 数字。
当启用 ENABLE_MUL 时,MUL 指令将在 40 周期内执行,MULH[SU|U] 指令将在 72 周期内执行。
当启用 ENABLE_DIV 时,DIV[U]/REM[U] 指令将在 40 周期内执行。
当启用 BARREL_SHIFTER 时,移位操作将和其他任何 ALU 操作一样长。
以下是启用了 ENABLE_FAST_MUL、ENABLE_DIV 和 BARREL_SHIFTER 选项的核心的 Dhrystone 基准测试结果。
Dhrystone 基准测试结果:0.516 DMIPS/MHz(每秒 908 Dhrystone/MHz)
对于 Dhrystone 基准测试,平均 CPI 为 4.100。
如果不使用前瞻性内存接口(通常需要达到最大时钟速度),这些结果将下降到 0.305 DMIPS/MHz 和 5.232 CPI。
PicoRV32 本地内存接口
PicoRV32 的本地内存接口是一个简单的有效-就绪接口,可以一次运行一个内存传输:
output mem_valid
output mem_instr
input mem_ready
output [31:0] mem_addr
output [31:0] mem_wdata
output [ 3:0] mem_wstrb
input [31:0] mem_rdata
核心通过断言 mem_valid 来启动内存传输。有效信号保持高,直到对等方断言 mem_ready。在 mem_valid 期间,所有核心输出都是稳定的。如果内存传输是指令获取,则核心断言 mem_instr。
读取传输
在读取传输中,mem_wstrb 的值为 0,mem_wdata 未使用。
内存读取地址 mem_addr,并在 mem_ready 高的周期内使读取值在 mem_rdata 上可用。
不需要外部等待周期。内存读取可以异步实现,mem_ready 在与 mem_valid 相同的周期内高,或者 mem_ready 被连接到常数 1。
写入传输
在写入传输中,mem_wstrb 不为 0,mem_rdata 未使用。内存将数据 mem_wdata 写入地址 mem_addr 并通过断言 mem_ready 确认传输。
mem_wstrb 的 4 位是所寻址字中的四个字节的写使能。只有 8 个值是可能的,即不写,写 32 位,写上 16 位,写下 16 位,或分别写一个字节。
不需要外部等待周期。内存可以立即确认写入,mem_ready 在与 mem_valid 相同的周期内高,或者 mem_ready 被连接到常数 1。
前瞻性接口
PicoRV32 核心还提供了一个 "前瞻性内存接口",它比正常接口提前一个时钟周期提供有关下一个内存传输的所有信息。
output mem_la_read
output mem_la_write
output [31:0] mem_la_addr
output [31:0] mem_la_wdata
output [ 3:0] mem_la_wstrb
在 mem_valid 高的前一个时钟周期,此接口将输出 mem_la_read 或 mem_la_write 上的脉冲,以指示下一个时钟周期开始的读写事务。
注意:信号 mem_la_read、mem_la_write 和 mem_la_addr 由 PicoRV32 核心内的组合电路驱动。与上面描述的正常内存接口相比,使用前瞻性接口可能更难实现时序闭合。
Pico 协处理器接口 (PCPI)
Pico 协处理器接口 (PCPI) 可用于在外部核心中实现非分支指令:
output pcpi_valid
output [31:0] pcpi_insn
output [31:0] pcpi_rs1
output [31:0] pcpi_rs2
input pcpi_wr
input [31:0] pcpi_rd
input pcpi_wait
input pcpi_ready
当遇到不支持的指令并激活 PCPI 特性时(见上文 ENABLE_PCPI),则断言 pcpi_valid,指令字本身在 pcpi_insn 上输出,rs1 和 rs2 字段被解码,这些寄存器中的值在 pcpi_rs1 和 pcpi_rs2 上输出。
外部 PCPI 核心可以解码指令,执行它,并在执行指令完成时断言 pcpi_ready。可选地,可以将结果值写入 pcpi_rd 并断言 pcpi_wr。然后 PicoRV32 核心将解码指令的 rd 字段,并将来自 pcpi_rd 的值写入相应的寄存器。
如果在 16 个时钟周期内没有任何外部 PCPI 核心确认指令,那么将引发非法指令异常并调用相应的中断处理程序。需要几个周期来执行指令的 PCPI 核心,应在成功解码指令后尽快断言 pcpi_wait 并保持它断言,直到它断言 pcpi_ready。这将防止 PicoRV32 核心引发非法指令异常。
用于 IRQ 处理的自定义指令
注意:PicoRV32 中的 IRQ 处理功能不遵循 RISC-V 特权 ISA 规范。相反,使用一组非常简单的自定义指令,以最小的硬件开销实现 IRQ 处理。
当通过 ENABLE_IRQ 参数启用 IRQ(见上文)时,仅支持以下自定义指令。
PicoRV32 核心内置了一个具有 32 个中断输入的中断控制器。通过在核心的 irq 输入中断言相应的位,可以触发中断。
当开始中断处理程序时,所处理中断的 eoi(中断结束)信号会变高。当中断处理程序返回时,eoi 信号再次变低。
IRQ 0-2 可以由以下内置中断源内部触发:
IRQ中断源0定时器中断1EBREAK/ECALL 或非法指令2总线错误(未对齐的内存访问)
这些中断也可以由外部源触发,例如通过 PCPI 连接的协处理器。
核心有 4 个额外的 32 位寄存器 q0 .. q3,用于 IRQ 处理。当调用中断处理程序时,寄存器 q0 包含返回地址,q1 包含要处理的所有 IRQ 的位掩码。这意味着一个对中断处理程序的调用需要服务多个 IRQ,当 q1 中设置多个位时。
当启用压缩指令支持时,那么中断指令是压缩指令时,q0 的 LSB 将被设置。这可以用于如果中断处理程序想要解码中断指令。
寄存器 q2 和 q3 未初始化,可以作为临时存储在 IRQ 处理程序中保存/恢复寄存器值时使用。
所有以下指令都编码在 custom0 操作码下。在所有这些指令中,f3 和 rs2 字段都被忽略。
请参阅 firmware/custom_ops.S 以获取 GNU 汇编器宏,该宏为这些指令实现助记符。
请参阅 firmware/start.S 以获取中断处理程序汇编器包装器的示例实现,以及 firmware/irq.c 以获取实际的中断处理程序。
getq rd, qs
该指令从 q-寄存器复制值到通用寄存器。
0000000 ----- 000XX --- XXXXX 0001011
f7 rs2 qs f3 rd opcode
示例:
getq x5, q2
setq qd, rs
该指令从通用寄存器复制值到 q-寄存器。
0000001 ----- XXXXX --- 000XX 0001011
f7 rs2 rs f3 qd opcode
示例:
setq q2, x5
retirq
从中断返回。该指令将 q0 中的值复制到程序计数器并重新启用中断。
0000010 ----- 00000 --- 00000 0001011
f7 rs2 rs f3 rd opcode
示例:
retirq
maskirq
"IRQ 掩码" 寄存器包含已屏蔽(禁用)中断的位掩码。此指令向 irq 掩码寄存器写入新值并读取旧值。
0000011 ----- XXXXX --- XXXXX 0001011
f7 rs2 rs f3 rd opcode
示例:
maskirq x1, x2
处理器一开始就禁用了所有中断。
如果禁用了非法指令或总线错误中断,则非法指令或总线错误将导致处理器停止。
waitirq
暂停执行,直到中断变为待处理。待处理 IRQ 的位掩码被写入 rd。
0000100 ----- 00000 --- XXXXX 0001011
f7 rs2 rs f3 rd opcode
示例:
waitirq x1
timer
重置计时器计数器为新值。计数器倒数时钟周期,并在从 1 变为 0 时触发定时器中断。将计数器设置为零将禁用定时器。旧的计数器值被写入 rd。
0000101 ----- XXXXX --- XXXXX 0001011
f7 rs2 rs f3 rd opcode
示例:
timer x1, x2
构建纯 RV32I 工具链
TL;DR: 运行以下命令构建完整工具链:
make download-tools
make -j$(nproc) build-tools
riscv-tools 构建脚本中的默认设置将构建一个可以针对任何 RISC-V ISA 的编译器、汇编器和链接器,但库是为 RV32G 和 RV64G 目标构建的。按照以下说明构建一个完整的工具链(包括库),目标是纯 RV32I CPU。
运行以下命令将构建 RISC-V GNU 工具链和库,目标是纯 RV32I,并将它们安装在 /opt/riscv32i 中:
# Ubuntu 需要的软件包:
sudo apt-get install autoconf automake autotools-dev curl libmpc-dev \
libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo \
gperf libtool patchutils bc zlib1g-dev git libexpat1-dev
sudo mkdir /opt/riscv32i
sudo chown $USER /opt/riscv32i
git clone https://github.com/riscv/riscv-gnu-toolchain riscv-gnu-toolchain-rv32i
cd riscv-gnu-toolchain-rv32i
git checkout 411d134
git submodule update --init --recursive
mkdir build; cd build
../configure --with-arch=rv32i --prefix=/opt/riscv32i
make -j$(nproc)
这些命令将以 riscv32-unknown-elf- 为前缀构建所有命令,这使得它们可以与常规的 riscv-tools(默认使用 riscv64-unknown-elf- 作为名称前缀)并排安装。
或者,您可以简单地使用 PicoRV32 的 Makefile 中的以下 make 目标之一来构建 RV32I[M][C] 工具链。您仍然需要安装所有先决条件,如上所述。然后在 PicoRV32 源目录中运行以下任何命令:
默认情况下,调用任何这些 make 目标将(重新)下载工具链源代码。提前运行 make download-tools 一次性下载源代码到 /var/cache/distfiles/。
注意:这些说明适用于 git 修订版 411d134 (2018-02-14) 的 riscv-gnu-toolchain。
为 PicoRV32 链接新立库 (newlib) 的二进制文件
工具链(见上一节安装说明)附带了新立库 C 标准库的版本。
使用链接器脚本 firmware/riscv.ld 将二进制文件链接到新立库库。使用此链接器脚本将创建一个入口点在 0x10000 的二进制文件。(默认链接器脚本没有静态入口点,因此需要适当的 ELF 加载器在运行时确定入口点。)
新立库带有一些系统调用存根。您需要提供这些系统调用的自己的实现,并将程序与此实现链接,覆盖新立库的默认存根。请参阅 scripts/cxxdemo/ 中的 syscalls.c 了解如何执行此操作的示例。
评估:在 Xilinx 7 系列 FPGA 上的时间和利用率
以下是使用 Vivado 2017.3 执行的评估。
在 Xilinx 7 系列 FPGA 上的时间
已针对 Xilinx Artix-7T、Kintex-7T、Virtex-7T、Kintex UltraScale 和 Virtex UltraScale 设备的所有速度等级,放置并布线了启用了 TWO_CYCLE_ALU 的 picorv32_axi 模块。使用二分搜索查找设计满足时序的最短时钟周期。
请参阅 scripts/vivado/ 中的 make table.txt。
在 Xilinx 7 系列 FPGA 上的利用率
下表列出了针对以下三个核心的资源利用率,在面积优化的综合中:
- PicoRV32 (small): 没有计数器指令的 picorv32 模块, 没有两阶段移位,外部锁存 mem_rdata,并且没有捕获未对齐的内存访问和非法指令。
- PicoRV32 (regular): 默认配置中的 picorv32 模块。
- PicoRV32 (large): 启用了 PCPI、IRQ、MUL、 DIV、BARREL_SHIFTER 和 COMPRESSED_ISA 功能的 picorv32 模块。
请参阅 scripts/vivado/ 中的 make area。
项目地址
转载自:https://juejin.cn/post/7372245998897774629