picorv32是一个由YosysHQ所收录的、单一文件的RISC-V CPU Core,包含了不带firmware/software的testebench_ez和一个带有firmware/software的testbench,对入门很友好。
当前访问频次受限,请登录后继续访问
ChipCamp 关于 PicoRV32 的博客(自 20251216 起)
-
git clone https://gitcode.com/ChipCamp/picorv32
-
make test_ez。//不依赖 xxx.hex(进一步依赖 riscv64-unknown-elf-gcc 等)。
- 操作日志:comments/01_make_test_ez.log
- make test。//与直接执行 make 命令效果相同,因为“test”是 Makefile 中的第一个目标。详见 make_test.log
- 操作日志:comments/02_make.log
PicoRV32 - 一款尺寸优化的 RISC-V CPU
PicoRV32 是一款实现了 RISC-V RV32IMC 指令集 的 CPU 核。 它可配置为 RV32E、RV32I、RV32IC、RV32IM 或 RV32IMC 核,并可选择包含一个内置中断控制器。
工具(如 gcc、binutils 等)可通过 RISC-V 官方网站 获取。
PicoRV32 附带的示例期望各种 RV32 工具链安装在 /opt/riscv32i[m][c] 目录下。有关详细信息,请参见下文的 构建说明。
许多 Linux 发行版现已包含 RISC-V 工具(例如 Ubuntu 20.04 提供 gcc-riscv64-unknown-elf)。若要使用这些工具进行编译,请相应地设置 TOOLCHAIN_PREFIX(例如 make TOOLCHAIN_PREFIX=riscv64-unknown-elf-)。
PicoRV32 是自由开源硬件,采用 ISC 许可证(一种条款类似于 MIT 许可证或二条款 BSD 许可证的许可协议)。
目录
- 特性与典型应用
- 本仓库中的文件
- Verilog 模块参数
- 每指令周期性能
- PicoRV32 原生内存接口
- Pico 协处理器接口 (PCPI)
- 用于 IRQ 处理的自定义指令
- 构建纯 RV32I 工具链
- 使用 newlib 为 PicoRV32 链接二进制文件
- 评估:在 Xilinx 7 系列 FPGA 上的时序与资源占用
特性与典型应用
- 体积小巧(在Xilinx 7系列架构中占用750-2000个LUT)
- 高fmax(在Xilinx 7系列FPGA上可达250-450 MHz)
- 可选择原生内存接口或AXI4-Lite主接口
- 可选中断支持(使用简单的自定义指令集)
- 可选协处理器接口
该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与外部世界通信。
可选的中断功能可用于响应外部事件、实现故障处理程序,或捕获来自更大指令集的指令并在软件中对其进行仿真。
可选的Pico协处理器接口(PCPI)可用于在外部协处理器中实现非分支指令。本软件包中包含了实现M标准扩展指令MUL[H[SU|U]]和DIV[U]/REM[U]的PCPI内核实现。
此仓库中的文件
README.md
您当前正在阅读的文件。
picorv32.v
此 Verilog 文件包含以下 Verilog 模块:
| 模块 | 描述 |
|---|---|
picorv32 |
PicoRV32 CPU |
picorv32_axi |
带有 AXI4-Lite 接口的 CPU 版本 |
picorv32_axi_adapter |
从 PicoRV32 内存接口到 AXI4-Lite 的适配器 |
picorv32_wb |
带有 Wishbone 主接口的 CPU 版本 |
picorv32_pcpi_mul |
实现 MUL[H[SU|U]] 指令的 PCPI 内核 |
picorv32_pcpi_fast_mul |
picorv32_pcpi_fast_mul 的一个版本,使用单周期乘法器 |
picorv32_pcpi_div |
实现 DIV[U]/REM[U] 指令的 PCPI 内核 |
只需将此文件复制到您的项目中即可。
Makefile 和测试平台
一个基础的测试环境。运行 make test 可在标准配置下运行标准测试平台(testbench.v)。此外还有其他测试平台和配置。有关详细信息,请参见 Makefile 中的 test_* make 目标。
运行 make test_ez 可运行 testbench_ez.v,这是一个非常简单的测试平台,不需要外部固件 .hex 文件。在无法获取 RISC-V 编译器工具链的环境中,这可能会很有用。
注意:测试平台使用 Icarus Verilog。然而,Icarus Verilog 0.9.7(撰写本文时的最新版本)存在一些 bug,会导致测试平台无法运行。请升级到 Icarus Verilog 的最新 github 主版本以运行测试平台。
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 设置为 0,这些指令将导致硬件陷阱(与任何其他不支持的指令一样)。
注:严格来说,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)阶段,稍微放宽最长数据路径,但代价是条件分支指令增加一个额外的时钟周期延迟。
注:在综合流程中启用重定时(也称为“寄存器平衡”)时,启用此参数效果最佳。
TWO_CYCLE_ALU(默认值 = 0)
这会在 ALU 数据路径中增加一个额外的触发器(FF)阶段,以改善时序,但代价是所有使用 ALU 的指令都增加一个时钟周期。
注:在综合流程中启用重定时(也称为“寄存器平衡”)时,启用此参数效果最佳。
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 寄存器,irq 返回地址将存储在 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 是“锁存的”,即当 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数值。
| 指令类型 | CPI | CPI (SP) |
|---|---|---|
| 直接跳转(jal) | 3 | 3 |
| ALU 寄存器 + 立即数 | 3 | 3 |
| ALU 寄存器 + 寄存器 | 3 | 4 |
| 分支(未命中) | 3 | 4 |
| 存储器加载 | 5 | 5 |
| 存储器存储 | 5 | 6 |
| 分支(命中) | 5 | 6 |
| 间接跳转(jalr) | 6 | 6 |
| 移位操作 | 4-14 | 4-15 |
当激活ENABLE_MUL时,MUL指令将执行40个周期,MULH[SU|U]指令将执行72个周期。
当激活ENABLE_DIV时,DIV[U]/REM[U]指令将执行40个周期。
当激活BARREL_SHIFTER时,移位操作的执行时间与其他任何ALU操作相同。
以下Dhrystone基准测试结果针对启用了ENABLE_FAST_MUL、ENABLE_DIV和BARREL_SHIFTER选项的内核。
Dhrystone基准测试结果:0.516 DMIPS/MHz(908 Dhrystones/秒/MHz)
在Dhrystone基准测试中,平均CPI为4.100。
如果不使用预读存储器接口(通常是实现最高时钟速度所必需的),测试结果将降至0.305 DMIPS/MHz和5.232 CPI。
PicoRV32 原生存储器接口
PicoRV32的原生存储器接口是一个简单的valid-ready接口,一次可执行一个存储器传输:
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来启动存储器传输。该valid信号保持高电平,直到对等方断言mem_ready。在mem_valid期间,所有内核输出均保持稳定。如果存储器传输是指令取指,内核会断言mem_instr。
读传输
在读传输中,mem_wstrb的值为0,mem_wdata未使用。
存储器读取地址mem_addr,并在mem_ready为高电平的周期内将读取值置于mem_rdata上。
无需外部等待周期。存储器读取可以异步实现,mem_ready在mem_valid的同一个周期内变高,或者mem_ready直接接高电平。
写传输
在写传输中,mem_wstrb不为0,mem_rdata未使用。存储器将mem_wdata的数据写入地址mem_addr,并通过断言mem_ready来确认传输完成。
mem_wstrb的4个比特是所寻址字中四个字节的写使能。仅可能出现8种取值:0000、1111、1100、0011、1000、0100、0010和0001,分别表示:无写入、写入32位、写入高16位、写入低16位或写入单个字节。
无需外部等待周期。存储器可以通过在mem_valid的同一个周期内使mem_ready变高来立即确认写入,或者mem_ready直接接高电平。
预读接口
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 中的值写入相应的寄存器。
如果外部 PCPI 核心在 16 个时钟周期内未确认该指令,则会引发非法指令异常,并调用相应的中断处理程序。如果某个 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 | 定时器中断 |
| 1 | EBREAK/ECALL 或非法指令 |
| 2 | 总线错误(未对齐内存访问) |
这些中断也可以由外部源触发,例如通过 PCPI 连接的协处理器。
该核心有 4 个额外的 32 位寄存器 q0 .. q3,用于 IRQ 处理。当调用 IRQ 处理程序时,寄存器 q0 包含返回地址,q1 包含所有待处理 IRQ 的位掩码。这意味着,当 q1 中设置了多个位时,一次中断处理程序调用需要处理多个 IRQ。
如果启用了对压缩指令的支持,那么当被中断的指令是压缩指令时,q0 的最低位(LSB)会被置位。如果 IRQ 处理程序需要对被中断的指令进行解码,这会很有用。
寄存器 q2 和 q3 未初始化,可在 IRQ 处理程序中保存/恢复寄存器值时用作临时存储。
以下所有指令都在 custom0 操作码下编码。所有这些指令中,f3 和 rs2 字段都被忽略。
有关实现这些指令助记符的 GNU 汇编器宏,请参见 firmware/custom_ops.S。
有关中断处理程序汇编包装器的示例实现,请参见 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 工具链
简而言之:运行以下命令以构建完整的工具链:
make download-tools make -j$(nproc) build-tools
riscv-tools 构建脚本中的默认设置将构建一个能够面向任何 RISC-V ISA 的编译器、汇编器和链接器,但库是针对 RV32G 和 RV64G 目标构建的。按照以下说明构建一个完整的工具链(包括库),以面向纯 RV32I CPU。
以下命令将为纯 RV32I 目标构建 RISC-V GNU 工具链和库,并将其安装在 /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 源代码目录中运行以下任一命令:
| 命令 | 安装目录 | ISA |
|---|---|---|
make -j$(nproc) build-riscv32i-tools |
/opt/riscv32i/ |
RV32I |
make -j$(nproc) build-riscv32ic-tools |
/opt/riscv32ic/ |
RV32IC |
make -j$(nproc) build-riscv32im-tools |
/opt/riscv32im/ |
RV32IM |
make -j$(nproc) build-riscv32imc-tools |
/opt/riscv32imc/ |
RV32IMC |
或者,只需运行 make -j$(nproc) build-tools 来构建并安装所有四个工具链。
默认情况下,调用这些 make 目标中的任何一个都会(重新)下载工具链源代码。运行 make download-tools 可以提前将源代码下载到 /var/cache/distfiles/ 目录一次。
注意:这些说明适用于 riscv-gnu-toolchain 的 git 修订版本 411d134(2018-02-14)。
使用 newlib 为 PicoRV32 链接二进制文件
工具链(安装说明参见最后一节)附带了 newlib C 标准库的一个版本。
链接针对 newlib 库的二进制文件时,请使用链接脚本 firmware/riscv.ld。使用此链接脚本将创建一个入口点位于 0x10000 的二进制文件。(默认链接脚本没有静态入口点,因此需要一个能够在加载程序时运行时确定入口点的适当 ELF 加载器。)
Newlib 附带了一些系统调用存根。您需要提供这些系统调用的自己的实现,并将您的程序与此实现链接,以覆盖 newlib 中的默认存根。有关如何执行此操作的示例,请参见 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 Kintex-7T | xc7k70t-fbg676-2 | -2 | 2.4 ns (416 MHz) |
| Xilinx Kintex-7T | xc7k70t-fbg676-3 | -3 | 2.2 ns (454 MHz) |
| Xilinx Virtex-7T | xc7v585t-ffg1761-2 | -2 | 2.3 ns (434 MHz) |
| Xilinx Virtex-7T | xc7v585t-ffg1761-3 | -3 | 2.2 ns (454 MHz) |
| Xilinx Kintex UltraScale | xcku035-fbva676-2-e | -2 | 2.0 ns (500 MHz) |
| Xilinx Kintex UltraScale | xcku035-fbva676-3-e | -3 | 1.8 ns (555 MHz) |
| Xilinx Virtex UltraScale | xcvu065-ffvc1517-2-e | -2 | 2.1 ns (476 MHz) |
| Xilinx Virtex UltraScale | xcvu065-ffvc1517-3-e | -3 | 2.0 ns (500 MHz) |
| Xilinx Kintex UltraScale+ | xcku3p-ffva676-2-e | -2 | 1.4 ns (714 MHz) |
| Xilinx Kintex UltraScale+ | xcku3p-ffva676-3-e | -3 | 1.3 ns (769 MHz) |
| Xilinx Virtex UltraScale+ | xcvu3p-ffvc1517-2-e | -2 | 1.5 ns (666 MHz) |
| Xilinx Virtex UltraScale+ | xcvu3p-ffvc1517-3-e | -3 | 1.4 ns (714 MHz) |
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。
| 内核变体 | Slice LUTs | LUTs 用作内存 | Slice Registers |
|---|---|---|---|
| PicoRV32 (small) | 761 | 48 | 442 |
| PicoRV32 (regular) | 917 | 48 | 583 |
| PicoRV32 (large) | 2019 | 88 | 1085 |