1.1 基于Icarus Verilog、ModelSim和Vivado对蜂鸟E203处理器进行仿真
目录
一、仿真准备
1. 相关知识
2. Icarus Verilog安装
3. ModelSim与Vivado的安装
二、使用Icarus Verilog仿真
三、使用ModelSim仿真
四、使用Vivado进行仿真
1. 创建工程
2. 添加IP核
3. 开始仿真
五、使用Nuclei Studio生成测试用例
六、总结
一、仿真准备
1. 相关知识
本文将介绍蜂鸟E203处理器SoC的仿真步骤,以及中间涉及到的概念和原理。正式开始前,我们需要下载Hummingbirdv2 E203 Core and SoC,这是托管在github上的蜂鸟E203 RISC-V处理器内核和SoC项目,也是我们的仿真对象。另外,需要下载软件Nuclei Studio IDE,这是一个功能强大的RISC-V集成开发环境,主要包括工程创建和管理、代码编辑、SDK管理和程序下载等功能,更多关于Nuclei Studio的介绍参考Nuclei Studio IDE使用说明。当然,对于仿真来说,Nuclei Studio主要用来将C语言源码转化为二进制机器码文件,用于蜂鸟E203处理器的仿真。
对硬件单元的仿真包括功能仿真、门级仿真和时序仿真。功能仿真在RTL级别验证逻辑正确性,不考虑时序延迟,常用工具如Icarus Verilog、ModelSim/QuestaSim等。门级仿真针对综合后的门级网表,验证功能并估算单元延迟,是综合后的关键步骤。时序仿真基于布局布线后的网表,加入实际的门延迟和线延迟,验证设计在真实时序下的行为。在FPGA中,Vivado和Quartus Prime提供完整的仿真流程;在ASIC中,门级和时序仿真通常依赖VCS、NC-Verilog等工具,结合PrimeTime进行静态时序分析,而HSPICE/Spectre用于后端电路级验证。
对于蜂鸟E203处理器,我们采用Icarus Verilog和ModelSim进行功能仿真,用Vivado进行时序仿真。Icarus Verilog适合快速仿真,而ModelSim更适合深度调试。我们采用的开发板是DDR200T,上面搭载了一块Artix-7系列FPGA——XC7A200T,XC7A200T是Xilinx 公司的一款高性能、低功耗FPGA器件,我们需要采用Xilinx 公司的官方仿真软件Vivado Simulator进行门级仿真和时序仿真,Vivado Simulator集成于Vivado开发套件,支持RTL级和门级仿真,可直接读取XDC约束文件,并且可以与Vivado 综合、实现流程无缝衔接。
2. Icarus Verilog安装
Icarus Verilog适合快速功能仿真,但调试功能较基础,需依赖外部工具如GTKWave分析波形。在Linux下,可通过包管理器一键安装(如sudo apt install iverilog gtkwave)。Linux下蜂鸟E203的具体仿真流程可参考其官方文档:How to run simulation,这里不再赘述。在windows环境下,我们手动安装Icarus Verilog和运行Makefile脚本的Make命令工具。Makefile 是用于自动化构建过程的脚本文件,可以自动调用iverilog编译代码,运行仿真并生成波形。
Icarus Verilog的下载地址为:Icarus Verilog for Windows,我们直接下载最新的版本,最新版本已经自带了GTKWave波形分析工具。下载后双击启动,按引导安装即可,注意勾上将路径添加到环境变量的选项。
Make命令工具的下载地址为:Make for Windows,直接下载完整的包,如下面红圈所示,双击启动后按引导安装即可。安装后将安装路径添加到用户或系统的环境变量Path中,默认安装路径为C:\Program Files (x86)\GnuWin32\bin。
最终,环境变量Path中应该有以下三个路径:
3. ModelSim与Vivado的安装
网上已有较多关于安装ModelSim与Vivado的资料,这里不再赘述。
二、使用Icarus Verilog仿真
Makefile 是用于自动化构建过程的脚本文件,可以自动调用iverilog编译代码,运行仿真并生成波形。通过定义简单的规则和命令,Makefile 能自动调用 iverilog 编译代码、vvp 执行仿真并生成波形文件,以及用 gtkwave 查看波形,避免了重复输入复杂命令的麻烦。
在e203_hbirdv2工程中,vsim文件夹下本身有一个Makefile文件,但只适用于linux系统,不能在windows系统上运行,我们准备了下面的适配windows系统的版本。我们的Makefile脚本首先定义了关键工具路径和编译选项,包括Verilog编译器(iverilog)、仿真器(vvp)和波形查看器(gtkwave),并设置了禁用断言和启用测试激励的宏定义。核心功能通过all目标串联了完整的流程:install阶段会创建目录结构并预处理测试文件;compile阶段调用iverilog编译RTL代码和测试平台;run执行仿真并生成波形;wave自动用gtkwave打开波形文件。测试用例默认指向RISC-V官方测试集中的rv32ui-p-add,用户可通过修改TESTNAME变量更换测试。测试用例在e203_hbirdv2工程中的riscv-tools/riscv-tests/isa/generated路径下,里面包含了对各种RISC-V指令的测试。
SIM_DIR := $(subst \,/,$(shell cd))IVERILOG := iverilog
VVP := vvp
GTKWAVE := gtkwave
IVERILOG_FLAGS := -g2012 -Wall -Wno-timescale -Wno-sensitivity-entire-array -Wno-portbind
MCRO := -D ENABLE_TB_FORCE -D DISABLE_SV_ASSERTION -D iverilog
DISABLE := -gno-assertionsTESTCASE := ../riscv-tools/riscv-tests/isa/generated/rv32ui-p-addRTL_FILES :=
TB_FILE := $(SIM_DIR)/bin/tb/tb_top.v
INCLUDE := $(SIM_DIR)/bin/rtl
DUMPWAVE := 1
TARGET := e203_simall: install compile run waveinstall: @mkdir "$(SIM_DIR)\bin\rtl" 2>nul || ver >nul@mkdir "$(SIM_DIR)\bin\tb" 2>nul || ver >nul@mkdir "$(SIM_DIR)\bin\testcase" 2>nul || ver >nul@(for /r "$(SIM_DIR)\..\rtl\e203" %%f in (*.v) do @copy /y "%%f" "$(SIM_DIR)\bin\rtl\%%~nxf" >nul 2>&1)@copy /y "$(SIM_DIR)\..\tb\tb_top.v" "$(TB_FILE)" >nul 2>&1@powershell -Command " \$$content = Get-Content $(TB_FILE); \$$content[99] = ' forever begin: LOOP1'; \$$content[103] = ' disable LOOP1;'; \$$content[126] = ' forever begin: LOOP2'; \$$content[132] = ' disable LOOP2;'; \$$content[140] = ' forever begin: LOOP3'; \$$content[146] = ' disable LOOP3;'; \$$content[154] = ' forever begin: LOOP4'; \$$content[160] = ' disable LOOP4;'; \Set-Content $(TB_FILE) $$content; \$$content2 = Get-Content -Path '$(TESTCASE).verilog' -Raw; \$$newContent = [regex]::Replace($$content2, '@(\d)', '@0'); \Set-Content -Path '$(SIM_DIR)/bin/testcase/$(notdir $(TESTCASE)).verilog' -Value $$newContent" >nul 2>&1@echo [INFO] Install complete.compile:@echo [INFO] Compiling design...$(eval RTL_FILES := $(wildcard $(SIM_DIR)/bin/rtl/*.v))$(IVERILOG) -o $(TARGET) $(IVERILOG_FLAGS) -I $(INCLUDE) $(MCRO) $(DISABLE) -s tb_top $(RTL_FILES) $(TB_FILE)@echo [INFO] Compile complete.run:$(VVP) $(TARGET) +TESTCASE=$(SIM_DIR)/bin/testcase/$(notdir $(TESTCASE)) +DUMPWAVE=$(DUMPWAVE)wave:@if exist "$(SIM_DIR)\tb_top.vcd" ( \echo [INFO] Opening waveform file... & \"$(GTKWAVE)" "$(SIM_DIR)\tb_top.vcd" --autosavename \) else ( \echo [ERROR] Waveform file $(SIM_DIR)\tb_top.vcd not found! \)clean:@if exist "$(SIM_DIR)\bin" rmdir /s /q "$(SIM_DIR)\bin" 2>nul@if exist "$(SIM_DIR)\$(TARGET)" del /q "$(SIM_DIR)\$(TARGET)" >nul 2>&1@if exist "$(SIM_DIR)\tb_top.vcd" del /q "$(SIM_DIR)\tb_top.vcd" >nul 2>&1@echo [INFO] Clean complete..PHONY: all install compile run wave clean
在运行Makefile脚本前,先将vsim文件夹下的Makefile文件替换为上面的内容,同时删除Makefile外的所有文件和文件夹。然后打开在vsim目录下打开终端,输入make后按回车,即可一键完成所有测试。如果需要分步骤执行,可以分别执行make install复制rtl文件,执行make compile进行编译,执行make run进行仿真并生成波形,执行make wave查看波形。当出现以下界面并自动打开gktwave波形查看器则仿真功能正常。
三、使用ModelSim仿真
正确安装ModelSim后,需要将对应可执行文件的路径添加到环境变量中,例如,我们的路径如下图所示,这个步骤是为了方便在脚本中调用ModelSim的编译和仿真工具。
我们需要专门用于ModelSim仿真的工作目录,建议直接在e203_hbirdv2工程目录新建一个文件夹,我们命名为modelsim。进入modelsim文件夹,创建一个Makefile文件,在其中写入下面的脚本。虽然我们可以直接操作ModelSim的图形界面进行工程创建、编译和仿真,但不够快捷且容易出错,我们推荐直接使用Makefile脚本进行这些操作。
SIM_DIR := $(shell cd)
VLOG := vlog
VSIM := vsimTESTCASE := ../riscv-tools/riscv-tests/isa/generated/rv32ui-p-addMACRO_FILES := rtl/config.v rtl/e203_defines.v rtl/i2c_master_defines.v
RTL_FILES := $(wildcard rtl/*.v)
TB_FILE := tb/tb_top.vall: copy compile simcopy: @mkdir "$(SIM_DIR)\rtl" 2>nul || ver >nul@mkdir "$(SIM_DIR)\tb" 2>nul || ver >nul@mkdir "$(SIM_DIR)\testcase" 2>nul || ver >nul@(for /r "$(SIM_DIR)\..\rtl\e203" %%f in (*.v) do @copy /y "%%f" "$(SIM_DIR)\rtl\%%~nxf" >nul 2>&1)@copy /y "$(SIM_DIR)\..\tb\tb_top.v" "$(SIM_DIR)\tb\tb_top.v" >nul 2>&1@powershell -Command "$$content = Get-Content -Path '$(TESTCASE).verilog' -Raw; \$$newContent = [regex]::Replace($$content, '@(\d)', '@0'); \Set-Content -Path '$(SIM_DIR)/testcase/$(notdir $(TESTCASE)).verilog' -Value $$newContent" >nul 2>&1@echo [INFO] Copy complete.compile:vlib work$(VLOG) -work work -O0 +define+DISABLE_SV_ASSERTION $(MACRO_FILES)$(VLOG) -work work -O0 +define+DISABLE_SV_ASSERTION +incdir+rtl $(RTL_FILES)$(VLOG) -work work -O0 +define+DISABLE_SV_ASSERTION +incdir+rtl $(TB_FILE)@echo [INFO] Compile complete.sim:$(VSIM) -novopt +TESTCASE=$(SIM_DIR)/testcase/$(notdir $(TESTCASE)) work.tb_top -do \"add wave -position insertpoint \sim:/tb_top/clk \sim:/tb_top/rst_n;"clean:@if exist "$(SIM_DIR)\work" rmdir /s /q "$(SIM_DIR)\work" 2>nul@if exist transcript del transcript@if exist vsim.wlf del vsim.wlf@if exist modelsim.ini del modelsim.ini@echo [INFO] Clean complete..PHONY: all copy clean
上面的Makefile脚本定义了四个主要目标:all(默认执行完整流程)、copy(复制源文件)、compile(编译设计)和sim(运行仿真)。脚本首先设置环境变量,包括仿真目录、工具命令、测试用例路径和设计文件列表,读者可以根据需要更改测试用例TESTCASE的路径。copy阶段会创建rtl、tb和testcase目录并复制相关文件;compile阶段使用vlog分三步编译宏定义、RTL代码和testbench,注意宏定义要先编译;sim阶段调用vsim启动仿真并加载指定测试用例,同时将顶层模块的clk和rst_n加入波形窗口中。读者可以尝试将其他想要观察的信号添加到波形窗口中,并添加run命令立即执行仿真。
执行make命令后,我们一键完成了绝大部分步骤,运行结果如下图:
除了自动添加的clk和rst_n信号,我们可以在图形界面添加一些额外的信号,例如我们添加了取指单元输出的pc值和指令机器码信号,之后设置仿真时间为1us并点击run按钮(下图红圈)进行仿真,结果如下图所示。
四、使用Vivado进行仿真
1. 创建工程
Vivado的脚本化项目搭建相对更复杂,为了对初学者友好一点,我们用图形化界面进行演示。类似于前面的步骤,我们在e203_hbirdv2工程目录新建一个文件夹,我们命名为vivado。我们首先将rtl和tb文件夹放入vivado文件夹中,然后根据自己的开发板复制对应的引脚约束文件和顶层文件。例如我们采用的是DDR200T开发板,需要进入e203_hbirdv2工程下的fpga/ddr200t目录,将其中的constrs和src文件夹复制到vivado目录下。constrs文件夹下包含了两个引脚约束文件,src文件夹包含了顶层文件。最终我们的vivado目录结构如下:
之后 打开Vivado,点击Creat Project创建工程,点击Next,Project name我们可以自行设置,Project location设置为我们的vivado目录,如下图所示,然后点击Next。
之后选择RTL Project(默认),点击Next。然后点击Add Directories添加源文件目录,我们需要将rtl和src目录添加进去,如下图所示,注意勾上红圈中的选项,然后点击Next。
接着添加约束文件,我们点击Add Files,将constrs目录下的两个约束文件添加进去,如下图所示,然后点击Next。
最后我们选择器件,在Search搜索框中输入200tfbg484-2,选择列表中的第一个选项,然后点击Next和Finish。
2. 添加IP核
创建完工程后,我们会发现工程缺少两个IP核,需要进行手动添加。点击左侧的IP Catalog,在出现窗口的Search搜索框中输入clock wizard,双击下面出现的Clocking Wizard,此时会弹出配置时钟IP核的引导对话框。
我们先将Component Name改为mmcm,这是顶层源文件中定义的名称。然后在Output Clocks界面中设置两个时钟输出,分别为8.388MHz与16MHz,最后下拉右边的滚动条,将下面的Reset Type设为Active Low,点击OK和Generate,完成添加。
之后我们添加第二个IP核,在IP Catalog的Search搜索框中输入system reset,双击下面出现的Processor System Reset。
先将Component Name改为reset_sys,再将下图中的两个Level改为0,点击OK和Generate,即可完成添加。
添加完IP核后,我们可以直接点击左侧的Run Implementation进行综合与实现。此时如果报cannot open include file e203_defines.v的错误,说明Vivado没能将e203_defines.v的路径自动添加到工程中,在Tcl Console中执行下面的命令即可完成添加,注意将e203_defines.v的路径修改为自己的路径。
set_property include_dirs "D:/Code/e203_hbirdv2/vivado/rtl/e203/core/" [current_fileset]
3. 开始仿真
首先需要添加仿真文件,点击左侧Add Sources,或者中间的加号,选择Add or create simulation sources,点击Next。
在新弹出的对话框中点击Add Files,选择tb目录下的tb_top.v文件,点击Finish。
现在默认的顶层文件还是system(黑色粗体为顶层文件),我们需要在仿真源文件目录下将tb_top设置为顶层文件,右键点击tb_top,选择Set as top,如下图所示。
此时我们点击Run Simulation,可以看到Vivado包括前仿真、综合后仿真、实现后仿真。前仿真(RTL仿真)用于在综合前验证RTL代码功能,忽略时序和物理延迟,使用行为级模型,快速排查逻辑错误。综合后仿真是指将源文件综合为门级网表后的仿真,此时加入了门延迟,验证综合后功能是否一致。实现后仿真(布局布线后仿真)基于布局布线后的网表,需要加载精确时序信息,验证时序约束与物理延迟的影响,接近真实硬件行为,但通常比较耗时。
接下来需要添加FPGA_SOURCE宏定义来屏蔽SystemVerilog语句的影响,右键点击Run Simulation,再点击Simulation Settings。
在弹出的对话框中点击Verilog options右侧的三个点,再点击加号输入FPGA_SOURCE,再点击OK和Apply按钮。
下一步为设置测试用例,同样右键点击Run Simulation,再点击Simulation Settings。之后在"Simulation"选项卡下找到xsim.simulate.xsim.more_options选项,在其中添加:-testplusarg TESTCASE=D:/Code/e203_hbirdv2/riscv-tools/riscv-tests/isa/generated/rv32ui-p-add,注意修改为自己的路径。最后点击Apply和OK按钮,即可完成所有配置。
结束后点击Run Simulation中的Run Post-Implementation Timing Simulation开始进行实现后的的时序仿真,读者可以尝试其他类型仿真。我们的仿真结果如下:
五、使用Nuclei Studio生成测试用例
前面的示例使用了e203_hbirdv2工程自带的测试用例,事实上,我们更希望仿真自己的C程序。观察tb_top.v中的代码,代码中使用系统函数$value$plusargs检查仿真命令行参数中是否有TESTCASE=<value>格式的参数,如果找到匹配的参数,则将其值存入testcase寄存器。这其实就是我们每次仿真前都要设置TESTCASE的原因,它其实是一个不能超过300字符的测试用例的路径。
之后使用系统函数$readmemh读取测试用例的数据到itcm_mem中,注意需要先将testcase中的字符串与.verilog拼接,例如我传入路径D:/Code/e203_hbirdv2/riscv-tools/riscv-tests/isa/generated/rv32ui-p-add,实际上读取的是D:/Code/e203_hbirdv2/riscv-tools/riscv-tests/isa/generated/rv32ui-p-add.verilog文件。打开rv32ui-p-add.verilog文件,可以发现这实际上是从编译后的ELF文件中提取出的机器码,并转换成了十六进制格式,专门用于硬件仿真。因此,生成测试用例的方法也很简单,首先将我们编写的C语言程序编译生成ELF格式的二进制文件,然后利用ELF转换工具objcopy提取汇编指令的机器码,并转换成十六进制格式保存到.verilog文件中。
上述步骤可以通过Nuclei Studio软件或HBird SDK软件平台完成,HBird SDK软件平台的使用可参考How to develop with HBird SDK,平台搭建完成后,在C程序工程目录下打开终端,执行make dasm命令即可生成.verilog文件。Nuclei Studio软件的基本使用方法可参考How to develop with Nuclei Studio(Ver.2022-04),这里不再详细介绍Nuclei Studio的基本使用。
接下来我们演示如何利用Nuclei Studio生成.verilog文件。首先我们在Nuclei Studio中创建一个示例工程,命名为example,并在main.c中编写了一个简单的程序,如下所示。
#include <stdio.h>
#include "hbird_sdk_soc.h"int main(void)
{printf("My first test example!\n");return 0;
}
之后点击菜单栏中的Project,再点击下拉框中的Properties。然后在弹出的对话框中选双击左侧的C/C++Build,点击Settings,在Build Steps选项卡下的Command框中输入下面的命令,最后点击Apply and Close,这样设置后每次项目构建完成都会自动执行下面的命令。该命令可以基于ELF工具生成.verilog格式的硬件仿真文件,以及.dasm格式的反汇编文件(方便对照验证指令执行的正确性)。
riscv-nuclei-elf-objcopy -O verilog "${BuildArtifactFileBaseName}.elf" "${BuildArtifactFileBaseName}.verilog"; riscv-nuclei-elf-objdump -S -D "${BuildArtifactFileBaseName}.elf" > "${BuildArtifactFileBaseName}.dump"; riscv-nuclei-elf-objdump -d "${BuildArtifactFileBaseName}.elf" > "${BuildArtifactFileBaseName}.dasm"
最后,我们点击左上角的构建按钮,即可完成测试用例的自动生成,注意生成的文件放在了Debug目录下。
我们复制出example.verilog文件的路径,右键点击文件名,再点击最下方的Properties,之后在弹出的对话框中点击左侧的Resource,此时Location后面会显式文件的完整路径,我们将其复制出来。
回到e203_hbirdv2工程中vsim目录中,这是我们最初进行Icarus Verilog仿真的地方,打开Makefile,将TESTCASE替换为我们的路径,注意去掉后面的.verilog,因为tb_top.v文件中会自动添加一个.verilog。最后打开终端,执行make命令,得到我们预期的输出。对于在Modelsim上的仿真类似,同样只需要修改TESTCASE即可,这里不再演示。
值得注意的是,在 Nuclei Studio生成的.verilog文件中,内存起始地址并不是0x00000000,而是链接脚本中定义的地址,例如0x80000000,我们需要将其修改为0x00000000,否则仿真时不能将.verilog文件中的数据正确导入到ITCM的内存中。在vsim和modelsim目录下的Makefile脚本已经自动帮我们进行了修改,但Vivado仿真时没有使用Makefile脚本,读者需要自行手动修改。另外,生成的.verilog文件中有多个地址标记,修改时不要遗漏。
最后,由于仿真自己的测试用例只有报错或者超时才会停止仿真,默认的超时时间太长,我们可以在tb_top.v文件中将其修改得更短,避免等待太长时间,如下图所示。
六、总结
本文详细介绍了蜂鸟E203处理器SoC的仿真流程,包括使用IcarusVerilog、ModelSim和Vivado三种工具进行功能仿真和时序仿真的具体步骤。首先讲解了仿真环境的搭建,包括工具安装和环境变量配置;然后分别对三种仿真工具提供了完整的Makefile脚本和操作指南,涵盖RTL代码编译、测试用例加载和波形分析等关键环节;最后介绍了如何通过NucleiStudio生成自定义测试用例,并对仿真中可能遇到的问题给出了解决方案。通过系统化的仿真方法验证,可有效确保RISC-V处理器设计的功能正确性和时序可靠性。