Quick Start
- 下载并安装 Vivado 2024.2(或更高版本,以大赛指定版本为准),确保包含 Vitis 和 Xilinx SDK。
- 从大赛官方仓库克隆 RISC-V 软核源码(推荐使用 VexRiscv 或 SERV 等成熟核)。
- 创建一个新的 Vivado 项目,目标器件选择大赛指定 FPGA(如 Xilinx Artix-7 XC7A100T)。
- 在项目中添加 RISC-V 软核 RTL 源码(包括 CPU 核、总线、外设 IP)。
- 编写顶层模块,实例化 RISC-V 核、AXI4-Lite 总线、UART、GPIO 等外设。
- 添加约束文件(XDC),分配时钟(如 50 MHz)、复位、UART 和 GPIO 引脚。
- 运行综合(Synthesis)与实现(Implementation),检查时序是否满足(WNS ≥ 0)。
- 生成比特流并下载到 FPGA 开发板;通过串口终端(如 Putty)验证 RISC-V 软核输出“Hello World”信息。
验收点:串口打印出预期的字符串,且无时序违例。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| FPGA 开发板 | Xilinx Artix-7 XC7A100T(大赛指定) | 主选平台 | XC7A35T(资源受限)或 Kintex-7(资源更充裕) |
| EDA 版本 | Vivado 2024.2 | 大赛指定版本 | Vivado 2023.2(需确认兼容性) |
| 仿真器 | Vivado Simulator 或 ModelSim/Questa | 推荐使用 Vivado 内置仿真器 | Verilator(仅限仿真) |
| 时钟/复位 | 外部晶振 50 MHz,异步低电平复位 | 基础时钟源 | 可改用 PLL 生成其他频率 |
| 接口依赖 | UART(115200 baud)、GPIO(LED/按键) | 标准外设接口 | SPI、I2C(需额外 IP) |
| 约束文件 | XDC 文件,包含时钟周期、I/O 引脚、时序例外 | 时序约束基础 | SDC 格式(Vivado 兼容) |
| RISC-V 软核 | VexRiscv(RV32IM,五级流水线) | 性能与资源平衡 | SERV(位串行,面积小)、PicoRV32(简单) |
目标与验收标准
- 功能点:RISC-V 软核能正确执行编译后的 C 程序(如打印字符串、控制 GPIO 闪烁)。
- 性能指标:系统时钟频率 ≥ 50 MHz(以大赛要求为准);Dhrystone 分数不低于参考值(例如 1.2 DMIPS/MHz)。
- 资源占用:LUT ≤ 6000,FF ≤ 4000,BRAM ≤ 16(示例值,以实际设计为准)。
- 验收方式:通过串口输出指定测试向量;Vivado 时序报告显示 WNS ≥ 0;资源利用率报告符合预期。
实施步骤
工程结构与关键模块
将项目划分为以下目录:rtl/(RTL 源码)、sim/(仿真文件)、constraints/(XDC)、sw/(C 程序)。RTL 中必须包含:CPU 核、总线桥(AXI4-Lite 到 APB 或自定义)、外设(UART、GPIO、Timer)。
// 顶层模块:soc_top.v
module soc_top (
input wire clk_50m, // 外部 50 MHz 时钟
input wire rst_n, // 异步低电平复位
output wire uart_tx, // UART 发送
input wire uart_rx, // UART 接收
output wire [3:0] led // 4 位 LED
);
// 内部信号
wire clk_cpu; // CPU 时钟(经 PLL 或直接连接)
wire rst_cpu_n; // 同步复位
wire [31:0] instr_addr; // 指令地址
wire [31:0] instr_data; // 指令数据
wire [31:0] data_addr; // 数据地址
wire [31:0] data_wdata; // 写数据
wire [3:0] data_wstrb; // 写选通
wire [31:0] data_rdata; // 读数据
wire data_we; // 写使能
// 实例化 RISC-V 核(VexRiscv)
vexriscv_core u_cpu (
.clk (clk_cpu),
.reset_n (rst_cpu_n),
.i_addr (instr_addr),
.i_data (instr_data),
.d_addr (data_addr),
.d_wdata (data_wdata),
.d_wstrb (data_wstrb),
.d_rdata (data_rdata),
.d_we (data_we)
);
// 实例化总线桥(AXI4-Lite to APB)
axi2apb_bridge u_bridge (
.clk (clk_cpu),
.rst_n (rst_cpu_n),
.axi_addr (data_addr),
.axi_wdata(data_wdata),
.axi_wstrb(data_wstrb),
.axi_rdata(data_rdata),
.axi_we (data_we),
.apb_addr (apb_addr),
.apb_wdata(apb_wdata),
.apb_rdata(apb_rdata),
.apb_we (apb_we)
);
// 实例化 UART 外设(地址 0x10000000)
uart_periph u_uart (
.clk (clk_cpu),
.rst_n (rst_cpu_n),
.apb_addr (apb_addr),
.apb_wdata(apb_wdata),
.apb_rdata(apb_rdata),
.apb_we (apb_we),
.uart_tx (uart_tx),
.uart_rx (uart_rx)
);
// 实例化 GPIO 外设(地址 0x10000010)
gpio_periph u_gpio (
.clk (clk_cpu),
.rst_n (rst_cpu_n),
.apb_addr (apb_addr),
.apb_wdata(apb_wdata),
.apb_rdata(apb_rdata),
.apb_we (apb_we),
.gpio_out (led)
);
// 时钟与复位同步
assign clk_cpu = clk_50m;
// 同步复位(防止异步复位引起时序问题)
reg [1:0] rst_sync;
always @(posedge clk_cpu or negedge rst_n) begin
if (!rst_n) begin
rst_sync <= 2'b00;
end else begin
rst_sync <= {rst_sync[0], 1'b1};
end
end
assign rst_cpu_n = rst_sync[1];
endmodule逐行说明
- 第 1 行:模块声明,端口包括时钟、复位、UART 和 LED。
- 第 2–3 行:输入时钟和复位,均为外部引脚。
- 第 4–5 行:UART 发送和接收信号。
- 第 6 行:4 位 LED 输出。
- 第 9–17 行:内部信号声明,用于连接 CPU 核与总线。
- 第 20–28 行:实例化 VexRiscv CPU 核,注意端口映射与命名约定。
- 第 31–42 行:实例化 AXI4-Lite 到 APB 总线桥,将 CPU 数据总线转换为 APB 协议。
- 第 45–53 行:实例化 UART 外设,地址映射由译码逻辑决定(此处简化)。
- 第 56–63 行:实例化 GPIO 外设,控制 LED。
- 第 66–74 行:时钟直接连接;复位同步器,防止异步复位释放时产生亚稳态。
时序与 CDC 处理
RISC-V 核通常工作于单一时钟域,但外设可能跨时钟域(如 UART 使用独立波特率时钟)。必须使用双级同步器或异步 FIFO 处理 CDC 路径。在约束中声明伪路径(false path)或异步时钟组。
# 约束文件:soc_top.xdc
# 主时钟定义
create_clock -period 20.000 [get_ports clk_50m]
# 异步复位(伪路径)
set_false_path -from [get_ports rst_n] -to [all_registers]
# UART 时钟域(假设由内部 PLL 生成)
# 如果 UART 使用独立时钟,需声明异步时钟组
# set_clock_groups -asynchronous -group [get_clocks -of_objects [get_pins u_uart/clk_uart]] -group [get_clocks -of_objects [get_pins clk_cpu]]逐行说明
- 第 1 行:创建主时钟,周期 20 ns(50 MHz)。
- 第 2 行:将复位端口设为伪路径,避免时序分析误报。
- 第 3–4 行:若 UART 有独立时钟,用 set_clock_groups 声明异步,防止跨时钟域路径被分析。
验证与仿真
编写 Testbench 加载固件(hex 文件),检查 CPU 执行结果。使用 Vivado 仿真运行 100 μs,观察 UART 输出和 GPIO 翻转。
// 仿真顶层:tb_soc.v
`timescale 1ns / 1ps
module tb_soc;
reg clk;
reg rst_n;
wire uart_tx;
wire [3:0] led;
// 实例化 DUT
soc_top u_dut (
.clk_50m (clk),
.rst_n (rst_n),
.uart_tx (uart_tx),
.uart_rx (1'b0), // 未使用
.led (led)
);
// 时钟生成
always #10 clk = ~clk; // 50 MHz
initial begin
clk = 0;
rst_n = 0;
#100 rst_n = 1;
#1000_000 $finish;
end
// 监测 UART 输出
initial begin
$monitor("Time=%0t, led=%b, uart_tx=%b", $time, led, uart_tx);
end
endmodule逐行说明
- 第 1 行:时间尺度定义。
- 第 2–6 行:Testbench 信号声明。
- 第 9–15 行:实例化顶层模块,uart_rx 接地。
- 第 18 行:每 10 ns 翻转时钟,生成 50 MHz。
- 第 20–24 行:初始化,复位 100 ns 后释放,仿真 1 ms 后结束。
- 第 27–29 行:打印 led 和 uart_tx 变化。
常见坑与排查
- 坑 1:复位不同步导致 CPU 启动失败。检查点:复位同步器输出是否在仿真中正确变为高电平。
- 坑 2:总线地址映射错误,外设无响应。检查点:在仿真中观察 apb_addr 是否命中外设基地址。
- 坑 3:UART 波特率不匹配。检查点:计算分频系数,确保与终端设置一致。
- 坑 4:时序违例导致功能随机失败。检查点:运行时序报告,关注 setup/hold 违例路径。
原理与设计说明
为什么选择 VexRiscv?它采用 RV32IM 指令集,支持五级流水线,性能优于位串行核(SERV),且资源消耗适中(约 3000 LUT)。与 PicoRV32 相比,VexRiscv 拥有更成熟的 AXI 总线接口,便于集成高速外设。
关键 trade-off:
- 资源 vs Fmax:流水线级数越多,Fmax 越高但 LUT 增加。五级流水线是平衡点。
- 吞吐 vs 延迟:使用 AXI4-Lite 总线可提高吞吐,但增加桥接逻辑延迟。对于大赛应用,延迟可接受。
- 易用性 vs 可移植性:VexRiscv 提供预配置脚本,但依赖 Scala 生成 RTL。若需纯 Verilog,可选 PicoRV32。
CDC 处理:UART 通常使用独立时钟(如 1.8432 MHz),必须使用双级触发器同步控制信号,或使用异步 FIFO 同步数据。忽略 CDC 会导致偶发错误,在仿真中难以复现。
验证与结果
| 指标 | 测量值(示例) | 测量条件 |
|---|---|---|
| Fmax | 85 MHz | Vivado 2024.2,Artix-7,最差工艺角 |
| LUT 占用 | 4200 | 启用 DSP 块,优化面积 |
| FF 占用 | 3100 | 同上 |
| BRAM 占用 | 12 | 指令/数据缓存各 4 KB |
| Dhrystone 分数 | 1.15 DMIPS/MHz | GCC -O2 编译 |
| UART 输出 | “Hello World” 无乱码 | 115200 baud,8N1 |
注意:以上数值为示例,以实际大赛环境和器件为准。
故障排查(Troubleshooting)
- 现象:仿真中 CPU 无指令执行。原因:复位未释放或指令存储器未初始化。检查点:查看 rst_cpu_n 波形;确认 BRAM 已加载 hex 文件。修复建议:延长复位时间;检查 $readmemh 路径。
- 现象:综合后时序违例(WNS < 0)。原因:关键路径在 CPU 数据通路或总线桥。检查点:查看时序报告中的最差路径。修复建议:插入流水线寄存器;降低时钟频率;启用综合优化(retiming)。
- 现象:上板后 UART 输出乱码。原因:波特率分频错误或时钟频率不匹配。检查点:计算分频系数;用示波器测量 TX 引脚。修复建议:调整分频寄存器值;确保终端设置一致。
- 现象:LED 不亮。原因:GPIO 地址映射错误或软件未配置。检查点:仿真中观察 apb_addr 和 apb_we;检查 C 代码中 GPIO 基地址。修复建议:核对地址译码逻辑;修改 linker script。
- 现象:资源占用超标。原因:CPU 配置过大或未优化。检查点:查看资源报告,找出高占用模块。修复建议:禁用 CPU 的乘除法器;减小缓存大小;使用面积优化综合策略。
- 现象:仿真中数据总线出现 X 态。原因:未初始化的寄存器或总线冲突。检查点:检查驱动源是否多重赋值。修复建议:确保所有输出有默认值;使用 always_ff 避免 latch。
- 现象:Vivado 报错“Unsupported clock interaction”。原因:跨时钟域路径未声明。检查点:检查 set_clock_groups 或 set_false_path 是否遗漏。修复建议:添加异步时钟组约束。
- 现象:软件编译后无法链接。原因:链接脚本未正确映射内存和外设地址。检查点:检查 .ld 文件中的 RAM 和 UART 地址。修复建议:修改链接脚本,与硬件地址一致。
扩展与下一步
- 参数化设计:使用 Verilog 参数(如 CLK_FREQ, BAUD_RATE)使外设可配置。
- 带宽提升:将 AXI4-Lite 升级为 AXI4-Full,支持突发传输,提高 DDR 访问效率。
- 跨平台移植:将 RISC-V 核移植到 Lattice 或 Intel FPGA,使用平台无关的 RTL。
- 加入断言与覆盖:在关键总线添加 SystemVerilog 断言(SVA),在仿真中自动检查协议。
- 形式验证:使用 Symbiyosys 对总线桥进行形式验证,确保无死锁。
- 增加外设:集成 SPI Flash 控制器、I2C 传感器接口,丰富 SoC 功能。
参考与信息来源
- VexRiscv 官方仓库:https://github.com/SpinalHDL/VexRiscv
- RISC-V 指令集手册(RV32I):https://riscv.org/technical/specifications/
- Xilinx Vivado 用户指南(UG901, UG903)
- 大赛官方文档与论坛(以 2026 年大赛官网为准)
技术附录
术语表
- RISC-V:开源指令集架构(ISA),基于精简指令集原则。
- AXI4-Lite:AMBA 总线的一个子集,用于简单外设通信。
- CDC:跨时钟域(Clock Domain Crossing),处理不同时钟域间的信号同步。
- WNS:最差负时序裕量(Worst Negative Slack),必须 ≥ 0。



