前言
Wujian100 是阿里平头哥自主研发的 RISC-V SOC,基于玄铁 E902,可供我们学习、开发。源码地址。我们需要对该源码经过简单修改,才能在不同 FPGA 上实现。
Basys3 是最常见的入门 FPGA,但各类资源都较少,不足以实现整个 Wujian100,我们需要对 SoC 进行一定的裁剪。
本文记录了 Wujian100 移植到 Basys3 全流程。移植后的工程文件已分享到了github上:wujian100_basys3
建立 Vivado 工程
新建vivado项目,选择板卡 Basys3。
按照源码 readme 中的移植流程,开始应该用 Synplify 综合。我们用 vivado,应按照 .\fpga\synplify\wujian100_open_200t_3b.prj 导入源码:
- fpga 的顶层文件 “wujian100_open_fpga_top.v”。
- soc 几乎所有文件,除了 “wujian100_open_top.v” ,这是仿真用的。
- soc/sim_lib 的所有源码。
- soc/params 所有。加入后会报错,需要把他们一个一个改为 verilog header 类型。
- 或者在 settings-gerneral-verilog options 加入头文件所在目录。
- 约束文件:fpga/xdc 内,官方自己的约束文件。之后我们需要进行修改。
时钟
Wujian100 的运行频率是 20MHz,Basys3 上的时钟是 100MHz。在 Vivado 中添加一个分频器,分频到 20MHz。例化到顶层文件 wujian100_open_fpga_top.v
中。
wire clk_out1;
clk_wiz_0 u_clk_wiz_0(
.reset (~PAD_MCURST),
.clk_in1 (PIN_EHS),
.clk_out1 (clk_out1),
.locked ()
);
PAD_OSC_IO x_PAD_EHS (
.CLK (ehs_pmu_clk),
.EN (1'b1 ),
.XOSC_IN (clk_out1 ),
.XOSC_OUT (POUT_EHS )
);
修改约束文件
在 https://github.com/Digilent/digilent-xdc 找我们板子的约束文件以对应修改。
时钟 W5
create_clock -period 10.000 -name PIN_EHS [get_ports PIN_EHS]
set_property PACKAGE_PIN W5 [get_ports PIN_EHS]; # 20 MHz
复位信号。这里我映射到了最左边的拨码开关 SW15,拨到上方代表高电平。因此正常运行时,SW15一定要拨到最上面。
set_property PACKAGE_PIN R2 [get_ports PAD_MCURST]
去掉 _c,这是 Synplify 添加的。
#set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets PAD_JTAG_TCLK_c]
set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets PAD_JTAG_TCLK]
调试口,J1–JA1 L2-JA2。
create_clock -period 500.000 -name PAD_JTAG_TCLK [get_ports PAD_JTAG_TCLK]
set_property PACKAGE_PIN J1 [get_ports PAD_JTAG_TCLK]
set_property PACKAGE_PIN L2 [get_ports PAD_JTAG_TMS]
GPIO,映射到了LED上,GPIO0对应LD0。
set_property PACKAGE_PIN U16 [get_ports PAD_GPIO_0]
set_property PACKAGE_PIN E19 [get_ports PAD_GPIO_1]
set_property PACKAGE_PIN U19 [get_ports PAD_GPIO_2]
set_property PACKAGE_PIN V19 [get_ports PAD_GPIO_3]
set_property PACKAGE_PIN W18 [get_ports PAD_GPIO_4]
set_property PACKAGE_PIN U15 [get_ports PAD_GPIO_5]
set_property PACKAGE_PIN U14 [get_ports PAD_GPIO_6]
set_property PACKAGE_PIN V14 [get_ports PAD_GPIO_7]
set_property PACKAGE_PIN V13 [get_ports PAD_GPIO_8]
set_property PACKAGE_PIN V3 [get_ports PAD_GPIO_9]
set_property PACKAGE_PIN W3 [get_ports PAD_GPIO_10]
set_property PACKAGE_PIN U3 [get_ports PAD_GPIO_11]
set_property PACKAGE_PIN P3 [get_ports PAD_GPIO_12]
set_property PACKAGE_PIN N3 [get_ports PAD_GPIO_13]
set_property PACKAGE_PIN P1 [get_ports PAD_GPIO_14]
set_property PACKAGE_PIN L1 [get_ports PAD_GPIO_15]
UART: JB1-RXD JB2-TXD
set_property PACKAGE_PIN A14 [get_ports PAD_USI0_SCLK]; # RXD
set_property PACKAGE_PIN A16 [get_ports PAD_USI0_SD0]; # TXD
裁剪
不经过裁剪的 Wujian100 在 Basys3 上综合所需要的资源:
lut 超了。通过分析资源使用情况并阅读源码,我们可以从两方面入手裁剪:
- SoC 的 RAM 比较大。它有1个 IRAM 块,3个 DRAM 块。每块 64KB,需要消耗 32 个 BRAM。Basys3 只有 100 个 BRAM,所以最后一个 64KB DRAM 块是用 LUT 实现的。其实一般的程序用不到那么大 DRAM,可以去掉一块。
- 总线上挂有较多外设。可以将一些不需要的外设去掉。其中 DMA 消耗资源很多,不需要可以去掉。
Wuijian100 中有很多没有内部逻辑,只有输入输出接口的 dummy 模块。我们可以用类似的方式清空一些无用模块的内部逻辑,使这些模块长成这样:
module empty_module(a,b,c
);
input wire a;
input wire b;
output wire c;
assign c = 1'b0;
endmodule
最后,综合工具会帮我们优化掉它们,这样我们就不用手动清理繁杂的信号线了。我分享的项目裁剪了最后一个 DRAM 块和 DMA。
忽略警告
综合和生成比特流时可能出现报警,如:
- 扇入扇出过多:[Pwropt 34-321] HACOOException: Too many fanin/fanouts in design, exiting pwropt. You can change this limit with the param pwropt.maxFaninFanoutToNetRatio新建 synth_pre.tcl ,使其综合前运行:set_param pwropt.maxFaninFanoutToNetRatio 2000
- 生成比特流时,引脚未配置完全。新建 bit_pre.tcl,写入:set_property SEVERITY {Warning} [get_drc_checks UCIO-1]在左侧的 PROJECT MANAGER->setting->Bitstream->‘tcl.pre’ 添加这个文件即可。
移植到这里就完成了。综合实现并上板,连接 CKLINK,在平头哥的 Debug Server 中应该可以看到 E902 的 CPU 信息。
太感谢大佬的教程指点了!