如何将 Quartus Prime IP 库链接到 VUnit
您是否曾经想通过 VUnit 验证框架运行包含 Quartus IP 内核的 VHDL 仿真?
这就是 FPGA 工程师 Konstantinos Paraskevopoulos 的想法,但他找不到合适的教程。幸运的是,他利用自己的才能弄清楚如何通过这篇客座文章与 VHDLwhiz 分享它 .
让我们转告康斯坦丁诺斯!
在使用 VUnit 仿真您的系统时,通常需要将 Quartus IP 目录中的预定义 IP 合并到您的设计中。因此,以下教程旨在为读者提供生成、合并和链接外部 Quartus IP 库到 VUnit 环境的知识。
对 VUnit 不熟悉?查看本教程:VUnit 入门
概述
本教程由三个主要部分组成:
- 所选 IP 的简要说明
- 生成和链接适当的库所需的步骤
- 使用 VUnit 和 Modelsim 进行验证
要求
- Quartus
- 下载 Quartus Prime
- QSYS 应该在 STEP 2 的路径中(CMD 选项)
- 英特尔 Modelsim
- 请参阅本文了解如何免费安装 ModelSim
- ModelSim 应该在您的路径中
- VUnit
- 如何免费安装 VUnit,请参考这篇文章
- Python 3.6 或更高
- 下载 Python
- Python 应该在你的路径中
它还假设具有基本的 VHDL 知识和 ModelSim 技能。
被测设计
对于我们的场景,我们使用 Quartus Integer Arithmetic IP 列表中的 Parallel Adder IP。
我们的设计接受三个 16 位的输入向量,并将相加的结果输出到一个 17 位的向量中。
第 1 步:生成 IP
我们在 IP 目录窗口中通过双击 Library/Basic functions/Arithmetic 下的并行加法器组件来生成我们的加法器。
在我们提供一个名称并根据我们的需要自定义我们的组件后,我们点击右下角的 Generate HDL 按钮。
此时会出现一个窗口,如下图所示。
注意: 我们必须设置 Create simulation model
Simulation
下 部分到 VHDL 或 Verilog 以生成仿真文件,因为默认选项是无。如果我们不选择一个,given_ip_name.spd
不会生成文件,导致下一步失败。
以上过程在我们的quartus
下生成了一个文件和一个文件夹 目录:
- 文件:
given_ip_name.ip
- 文件夹:
given_ip_name
该文件夹包含 .vhd
和 .v
后面需要在我们的run.py
中添加的文件 脚本。
第 2 步:生成 IP 模拟文件
- 图形界面: 选择 Tools> Generate Simulator Setup Script for IP 并在提示窗口中指定输出目录,
- CMD: 通过使用 Qsys 命令,我们可以通过在终端中输入以下命令来生成相同的文件:
ip-setup-simulation --quartus-project= <project's_QPF_filepath> --output-directory= <my_dir>
使用上述两种方法之一,我们指示 Quartus 为每个支持的模拟器生成一个目录,该目录包含一个用于创建和编译 IP 库的脚本。
第 3 步:为 Modelsim 生成和编译 IP 库
下一步是找到msim_setup.tcl
mentor
中的脚本 上一步创建的文件夹并将其复制为名称 setup.tcl
.然后,在 setup.tcl
文件,取消注释说明的命令并设置 $QSYS_SIMDIR
变量。
# # QSYS_SIMDIR is used in the Quartus-generated IP simulation script to # # construct paths to the files required to simulate the IP in your Quartus # # project. By default, the IP script assumes that you are launching the # # simulator from the IP script location. If launching from another # # location, set QSYS_SIMDIR to the output directory you specified when you # # generated the IP script, relative to the directory from which you launch # # the simulator. # # set QSYS_SIMDIR <script generation output directory> # # # # Source the generated IP simulation script. source $QSYS_SIMDIR/mentor/msim_setup.tcl # # # # Set any compilation options you require (this is unusual). # set USER_DEFINED_COMPILE_OPTIONS <compilation options> # set USER_DEFINED_VHDL_COMPILE_OPTIONS <compilation options for VHDL> # set USER_DEFINED_VERILOG_COMPILE_OPTIONS <compilation options for Verilog> # # # # Call command to compile the Quartus EDA simulation library. dev_com # # # # Call command to compile the Quartus-generated IP simulation files. com # #
更改并保存 setup.tcl
后 ,我们可以使用 vsim
安全地执行 Tcl 文件 命令。
vsim -c -do "do setup.tcl; quit"
这会在 mentor
中生成已编译的库 文件夹。
第 4 步:VUnit 链接
现在已经生成了 IP 库,我们应该使用 python run.py
链接它们 脚本。
查看下图以更好地理解我们示例的目录结构。初始拓扑由根文件夹 demo
组成 , tb
, vunit
, 和 quartus
文件夹。 quartus
下的所有子文件夹和文件 创建项目并完成步骤 1 至 3 后,通过 Quartus 框架生成文件夹。
注意: Quartus 生成更多文件和文件夹,但下图显示了我们感兴趣的那些。
使用这种独特的拓扑视图作为参考,我们可以指定我们的 ROOT 路径和生成库的路径,如下所示。
注意 sim_files
就是我们在第2步中指定的存放导师文件夹的目录。
from vunit import VUnit from os.path import join, dirname, abspath # ROOT root = join(dirname(__file__), '../') # Path to generated libraries path_2_lib = '/quartus/sim_files/mentor/libraries/' # ROOT
创建一个名为 vu
的 VUnit 实例后 ,我们可以为我们的VHDL代码指定一个设计库并链接任何需要的外部库:
# Create VUnit instance by parsing command line arguments vu = VUnit.from_argv() # create design's library my_lib = vu.add_library('my_lib') # Link external library vu.add_external_library("parallel_adder", root + path_2_lib + "parallel_adder")
最后,添加我们的源文件。它们位于 given_ip_name
下的三个子文件夹中 目录:
parallel_add_191
synth
sim
synth
和 sim
dirs包含相同的信息,即我们IP的顶层设计。但是,在我们的案例中,这些文件的格式是 VHDL。它们可能使用 Verilog,这取决于第 1 步选择的语言。
如果我们的顶层设计需要子组件,我们还必须包含它们的源文件。它们位于 given_ip_name
中的子文件夹下 目录,例如 parallel_add_191
在我们的例子中是组件。
my_lib.add_source_files(join(root,'quartus','parallel_adder','sim','parallel_adder.vhd')) my_lib.add_source_files(join(root,'quartus','parallel_adder','parallel_add_191','sim','parallel_adder_parallel_add_191_oh4guxa.vhd')) my_lib.add_source_files(join(root,'tb','tb_demo.vhd')) testbench = my_lib.entity("tb_demo") vu.main()
测试台
首先,您可以查看此链接以了解 VUnit 测试平台形成的基础知识。
回到我们的测试平台,我们添加必要的 VUnit 库以及我们想要使用和定义信号的任何其他库。
注意: 我们示例中的流程执行是顺序的。因此,控制信号(称为 flags ) 用于通知进程是否应该开始或终止。
library IEEE; use IEEE.std_logic_1164.all; use ieee.numeric_std.all; library vunit_lib; context vunit_lib.vunit_context; entity tb_demo is generic ( runner_cfg : string:= runner_cfg_default); end tb_demo; architecture sim of tb_demo is constant clk_period : time := 10 ns; signal clk : std_logic := '0'; signal rst : std_logic := '0'; -- INPUTS signal data_a : std_logic_vector(0 to 15):= (others => '0'); signal data_b : std_logic_vector(0 to 15):= (others => '0'); signal data_c : std_logic_vector(0 to 15):= (others => '0'); -- OUTPUTS signal result : std_logic_vector(0 to 16); -- CONTROL FLAGS signal reset_done :boolean := false; signal sim_done :boolean := false; signal start_sim :boolean := false;
接下来,我们实例化我们的 UUT。 Quartus 在文件名约定 ip_name_inst.vhd
下为 VHDL 和 Verilog 提供组件实例化示例 和 ip_name_inst.v
.
begin -- Unit Under Test UUT : entity work.parallel_adder port map ( data0x => data_a, -- parallel_add_input.data0x data1x => data_b, -- .data1x data2x => data_c, -- .data2x result => result -- parallel_add_output.result );
开始的前两个进程是 clk_process
和 reset_rel
.而后者在重置和驱动 reset_done
后暂停 标记为 true
, clk_process
在整个模拟时间内运行。
clk_process : process begin clk <= '1'; wait for clk_period/2; clk <= '0'; wait for clk_period/2; end process clk_process; reset_rel : process begin rst <= '1'; wait for clk_period*2; wait until rising_edge(clk); rst <= not rst; reset_done <= true; wait; end process reset_rel;
现在重置完成,我们可以调用 test_runner
执行我们的测试的过程。此外,测试运行程序保持活动状态,直到 sim_done
标志被驱动到 true
,发生在最后一个过程中。
test_runner : process begin test_runner_setup(runner, runner_cfg); wait until reset_done and rising_edge(clk); iterate : while test_suite loop start_sim <= true; if run("test_case_1") then info ("Start"); info (running_test_case); wait until sim_done; end if; end loop; test_runner_cleanup(runner); end process test_runner;
最后,data_generator
进程通过使用 for
为并行加法器的三个输入分配值来执行多个加法 循环。
注意: 当 test_runner
过程通过设置 start_sim
来指示 旗帜。在此过程结束时,它会引发 sim_done
flag,命令测试运行器暂停模拟。
data_generator : process constant tag2 : log_level_t := new_log_level("INFO", fg => blue, bg => black, style => bright); variable a,b,c,d : integer; begin wait until start_sim; wait until rising_edge(clk); show(display_handler, tag2); if running_test_case = "test_case_1" then for i in 0 to 10 loop data_a <= std_logic_vector(to_unsigned(i+10,data_a'length)); data_b <= std_logic_vector(to_unsigned(i+20,data_a'length)); data_c <= std_logic_vector(to_unsigned(i+30,data_a'length)); wait until rising_edge(clk); a := to_integer(unsigned(data_a)); b := to_integer(unsigned(data_b)); c := to_integer(unsigned(data_c)); d := to_integer(unsigned(result)); log( integer'image(a) &" + "& integer'image(b) &" + "& integer'image(c) &" = "& integer'image(d), tag2); end loop; end if; sim_done <= true; end process data_generator;
验证
要运行测试用例并验证一切是否按预期运行,我们可以执行 run.py
只需在终端中键入以下命令即可从它所在的目录中获取脚本。
python ./run.py -v
注意: 自定义记录器已用于更好地说明我们的输出,通过提供详细的 -v
可见 选项。另外,由于只定义了一个测试用例,我们不必提供一个选项来指定它。
最后,为了在 ModelSim 中验证我们的结果,我们可以输入以下命令:
python ./run.py --gui
(点击图片放大)
结论
最后,我们在本教程中学习了如何将驻留在 IP 目录中的 Quartus IP 合并到 VUnit 并对其进行测试。我们使用了预定义的 IP。但是,我们也可以通过这种方式将打包的定制 IP 集成到我们的 VUnit 环境中。
如果您还没有,请查看此 VUnit 教程:
开始使用 VUnit
VHDL