FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

VHDL入门:2026年用GHDL与VUnit搭建测试环境

二牛学FPGA二牛学FPGA
技术分享
9小时前
0
0
2

Quick Start

  • 步骤1:安装GHDL(Windows/macOS/Linux均可从GitHub Releases下载预编译包,或通过包管理器安装,如sudo apt install ghdl)。
  • 步骤2:安装VUnit(Python 3.8+环境,执行pip install vunit_hdl)。
  • 步骤3:创建项目目录vhdl_tb_demo,内含src(设计源码)、tb(测试平台)、run.pyVUnit启动脚本)。
  • 步骤4:编写一个简单的VHDL设计(如计数器)保存为src/counter.vhdl
  • 步骤5:编写对应的VUnit测试平台tb/tb_counter.vhdl,使用VUnit的check库进行断言。
  • 步骤6:编写run.py,指定GHDL后端与源文件列表。
  • 步骤7:在终端执行python run.py,观察VUnit输出:应显示“PASSED”或“FAILED”汇总,以及波形文件(如ghdl.vcd)。
  • 步骤8:用GTKWave打开VCD文件,查看信号波形,验证计数器行为。

前置条件与环境

项目/推荐值说明替代方案
操作系统Ubuntu 22.04 LTS(示例)Windows 10/11、macOS 12+
GHDL版本4.0.0(2026年稳定版)3.0.x(旧版,部分VHDL-2008特性不支持)
VUnit版本4.7.04.6.x(功能差异不大)
Python版本3.10+3.8/3.9(需注意pip兼容性)
仿真器后端GHDL(mcode或llvm)ModelSim/Questa(商业)
波形查看器GTKWave 3.3+Surfer(轻量)、VS Code插件
时钟/复位测试平台内生成100MHz时钟与异步复位外部激励文件
约束文件无(纯仿真,不需综合约束)

目标与验收标准

  • 功能点:实现一个4位二进制计数器,支持同步复位与使能,计数范围0~15。
  • 性能指标:仿真运行时间小于1秒(典型设计)。
  • 资源/Fmax:不适用(纯仿真,无综合)。
  • 验收方式:VUnit报告“PASSED”(所有测试用例通过);GTKWave波形显示计数从0递增至15后回绕。

实施步骤

工程结构与设计代码

创建以下目录结构:

vhdl_tb_demo/
├── src/
│   └── counter.vhdl
├── tb/
│   └── tb_counter.vhdl
└── run.py

逐行说明

  • 第1行:项目根目录。
  • 第2-3行:设计源码目录与文件。
  • 第4-5行:测试平台目录与文件。
  • 第6行:VUnit启动脚本。

设计代码src/counter.vhdl

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity counter is
    port (
        clk     : in  std_logic;
        rst_n   : in  std_logic;
        en      : in  std_logic;
        count   : out std_logic_vector(3 downto 0)
    );
end entity counter;

architecture rtl of counter is
    signal cnt_reg : unsigned(3 downto 0);
begin
    process(clk, rst_n)
    begin
        if rst_n = '0' then
            cnt_reg <= (others => '0');
        elsif rising_edge(clk) then
            if en = '1' then
                cnt_reg <= cnt_reg + 1;
            end if;
        end if;
    end process;

    count <= std_logic_vector(cnt_reg);
end architecture rtl;

逐行说明

  • 第1-3行:引入IEEE标准库,包含std_logic_1164(多值逻辑)和numeric_unsigned(无符号算术)。
  • 第5-11行:实体定义,声明时钟、复位(低有效)、使能、计数输出端口。
  • 第13-14行:架构体与内部信号cnt_reg,类型为unsigned(3 downto 0),便于算术运算。
  • 第16-24行:时序进程,敏感列表包含clk和rst_n(异步复位)。
  • 第18-19行:复位逻辑,当rst_n为低时,计数器清零。
  • 第20-23行:时钟上升沿触发,使能有效时递增。
  • 第26行:将unsigned转换为std_logic_vector输出。

测试平台代码

tb/tb_counter.vhdl

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library vunit_lib;
context vunit_lib.vunit_context;

entity tb_counter is
    generic (
        runner_cfg : string
    );
end entity tb_counter;

architecture tb of tb_counter is
    signal clk   : std_logic := '0';
    signal rst_n : std_logic := '0';
    signal en    : std_logic := '0';
    signal count : std_logic_vector(3 downto 0);

    constant CLK_PERIOD : time := 10 ns;
begin
    test_runner : process
    begin
        test_runner_setup(runner, runner_cfg);
        -- 初始化
        rst_n <= '0';
        en <= '0';
        wait for 20 ns;
        rst_n <= '1';
        wait for 10 ns;
        en <= '1';
        wait for 160 ns;  -- 16个时钟周期
        en <= '0';
        wait for 20 ns;
        test_runner_cleanup(runner);
        wait;
    end process;

    clk_gen : process
    begin
        clk <= not clk after CLK_PERIOD / 2;
        wait for CLK_PERIOD / 2;
    end process;

    -- 实例化设计
    uut : entity work.counter
        port map (
            clk   => clk,
            rst_n => rst_n,
            en    => en,
            count => count
        );

    -- 检查计数器行为
    check_proc : process
    begin
        wait until rst_n = '1';
        wait until rising_edge(clk);
        check_equal(count, std_logic_vector(to_unsigned(0, 4)), "Initial count should be 0");
        for i in 1 to 15 loop
            wait until rising_edge(clk);
            check_equal(count, std_logic_vector(to_unsigned(i, 4)), "Count mismatch at step " & integer'image(i));
        end loop;
        wait;
    end process;
end architecture tb;

逐行说明

  • 第1-3行:引入IEEE库。
  • 第5-6行:引入VUnit库与上下文,提供test_runner_setup、check_equal等函数。
  • 第8-11行:实体声明,必须包含generic runner_cfg,VUnit通过它传递配置。
  • 第13-17行:架构体与信号声明,clk初始为'0',rst_n初始为'0'(复位有效)。
  • 第19行:定义时钟周期常量10 ns(100 MHz)。
  • 第21-35行:test_runner进程,调用setup/cleanup管理测试生命周期。
  • 第24-26行:复位20 ns后释放。
  • 第28-30行:使能有效160 ns(16个时钟周期),计数器应完成一次完整循环。
  • 第31-34行:使能无效后等待,清理并结束。
  • 第37-40行:时钟生成进程,每半周期翻转一次。
  • 第43-48行:实例化被测试设计(UUT),端口映射。
  • 第51-61行:check_proc进程,在复位释放后每个时钟上升沿检查计数值。
  • 第54行:等待复位释放。
  • 第55行:等待第一个时钟上升沿。
  • 第56行:使用check_equal检查初始值是否为0。
  • 第57-60行:循环检查1~15每个值,若失败会报告具体步骤。

VUnit启动脚本

run.py

from vunit import VUnit

vu = VUnit.from_argv()
vu.add_library("lib").add_source_files("src/*.vhdl")
vu.add_library("tb_lib").add_source_files("tb/*.vhdl")
vu.set_sim_option("ghdl.elab_flags", ["--std=08"])
vu.main()

逐行说明

  • 第1行:导入VUnit Python模块。
  • 第3行:创建VUnit实例,自动解析命令行参数(如--gui、--list-tests)。
  • 第4行:添加名为“lib”的库,并包含src目录下所有.vhdl文件。
  • 第5行:添加名为“tb_lib”的库,包含tb目录下所有.vhdl文件。
  • 第6行:设置GHDL仿真选项,指定使用VHDL-2008标准(--std=08),以支持VUnit的context特性。
  • 第7行:启动仿真,VUnit会自动编译、运行并报告结果。

常见坑与排查

  • 坑1:VUnit报“library vunit_lib not found”。原因:未安装VUnit Python包或版本不匹配。检查:运行pip list | grep vunit。修复:重新安装pip install vunit_hdl
  • 坑2:GHDL编译错误“cannot find std_logic_1164”。原因:GHDL未正确安装或标准库路径缺失。检查:运行ghdl --version。修复:重新安装GHDL,或设置环境变量GHDL_PREFIX指向库路径。
  • 坑3:仿真运行但无波形输出。原因:未启用波形导出。修复:在run.py中添加vu.set_sim_option("ghdl.sim_flags", ["--vcd=wave.vcd"])
  • 坑4:check_equal报告类型不匹配。原因:比较的信号类型不一致。修复:确保使用相同子类型,或显式转换(如std_logic_vector与unsigned)。

原理与设计说明

为什么选择GHDL+VUnit而非商业仿真器?核心权衡如下:

  • 资源 vs Fmax:GHDL基于GCC/LLVM后端,编译速度接近ModelSim,但仿真性能略低(约70%)。对于中小型设计(<10万门),差异不显著。
  • 吞吐 vs 延迟:VUnit的自动化测试框架支持并行测试(多进程),可显著提升回归测试吞吐量。而传统do文件需要手动编写循环。
  • 易用性 vs 可移植性:VUnit用Python控制仿真流程,比TCL更现代、易扩展;但依赖Python环境,在纯Linux服务器上可能需额外配置。
  • 标准支持:GHDL 4.0支持VHDL-2008大部分特性(包括context、protected types),但不支持VHDL-2019。若需最新标准,建议使用Questa。

验证与结果

指标测量值(示例)条件
编译时间0.8秒GHDL mcode后端,2个源文件
仿真时间0.3秒200 ns仿真时长
测试用例通过数16(0~15步)VUnit check_equal断言
波形文件大小12 KBVCD格式,仅顶层信号

波形特征:复位后count从0开始,每个时钟上升沿递增1,使能无效时保持当前值,达到15后下一个时钟回绕到0。与预期完全一致。

故障排查(Troubleshooting)

  • 现象1:VUnit报告“FAILED”但无详细信息。原因:check_equal未指定消息。修复:在断言中添加描述字符串,如check_equal(count, "0", "Count should be 0");
  • 现象2:GTKWave打开VCD文件无信号。原因:波形文件未包含内部信号。修复:在GHDL仿真选项中加入--vcd=wave.vcd,并确保信号在顶层可见。
  • 现象3:Python报错“ModuleNotFoundError: No module named 'vunit'”。原因:未安装或虚拟环境未激活。修复:执行pip install vunit_hdl,或使用python -m pip
  • 现象4:GHDL编译错误“cannot open source file”。原因:路径错误。修复:检查run.py中add_source_files的路径,建议使用绝对路径或正确相对路径。
  • 现象5:仿真无限循环。原因:时钟生成进程未正确停止。修复:在test_runner末尾添加wait语句,或使用test_runner_cleanup
  • 现象6:VUnit报告“test runner not found”。原因:tb实体缺少generic runner_cfg。修复:在实体声明中添加generic (runner_cfg : string);
  • 现象7:波形中计数不变化。原因:使能信号en未置高。修复:检查test_runner进程中en的赋值顺序与时间。
  • 现象8:GHDL报错“error: std_logic_1164 not in library”。原因:GHDL标准库未编译。修复:运行ghdl --build --std=08先编译标准库,或安装完整版GHDL。

扩展与下一步

  • 参数化测试:使用VUnit的generic_list功能,测试不同位宽或初始值的计数器。
  • 覆盖率分析:集成GHDL的覆盖率选项(--coverage),生成语句/分支覆盖率报告。
  • CI/CD集成:将run.py加入GitHub Actions或GitLab CI,实现自动回归测试。
  • 跨平台仿真:使用VUnit的--backend选项切换至ModelSim/Questa,无需修改测试代码。
  • 形式验证:对关键属性(如计数器无溢出)使用VHDL断言(PSL)或引入SymbiYosys进行形式验证。

参考与信息来源

  • GHDL官方文档:https://ghdl.readthedocs.io/
  • VUnit用户指南:https://vunit.github.io/
  • GTKWave手册:http://gtkwave.sourceforge.net/
  • VHDL-2008标准(IEEE 1076-2008)

技术附录

术语表

  • GHDL:开源VHDL仿真器,基于GCC/LLVM。
  • VUnit:开源VHDL测试框架,提供断言、测试管理、自动编译。
  • VCD:Value Change Dump,标准波形存储格式。
  • UUT:Unit Under Test,被测试单元。

检查清单

  • GHDL安装并可通过命令行调用。
  • VUnit安装(pip list包含vunit_hdl)。
  • 项目目录结构正确。
  • 测试平台实体包含generic runner_cfg。
  • run.py中库与源文件路径正确。
  • 仿真选项--std=08已设置。
  • 波形文件生成选项已添加。

关键约束速查

  • VUnit要求测试平台实体必须包含generic runner_cfg: string。
  • GHDL默认使用VHDL-1993,需--std=08启用2008特性。
  • VCD文件路径建议使用绝对路径,避免相对路径歧义。
标签:
本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/41379.html
二牛学FPGA

二牛学FPGA

初级工程师
这家伙真懒,几个字都不愿写!
98619.59W4.01W3.67W
分享:
成电国芯FPGA赛事课即将上线
VHDL入门:2026年用GHDL与VUnit搭建测试环境
VHDL入门:2026年用GHDL与VUnit搭建测试环境上一篇
FPGA时序收敛:set_false_path在异步接口中的正确用法与实施指南下一篇
FPGA时序收敛:set_false_path在异步接口中的正确用法与实施指南
相关文章
总数:1.02K
FPGA时序约束:set_max_delay在异步路径中的精确用法与实施指南

FPGA时序约束:set_max_delay在异步路径中的精确用法与实施指南

QuickStart:快速上手步骤1:打开Vivado2026.1…
技术分享
2天前
0
0
9
0
FPGA仿真调试技巧:使用Modelsim高效定位Bug

FPGA仿真调试技巧:使用Modelsim高效定位Bug

QuickStart准备环境:安装ModelSim(SE/DE版)或Q…
技术分享
7天前
0
0
16
0
Verilog实战:基于三段式状态机的简易交通灯控制器设计与实现指南

Verilog实战:基于三段式状态机的简易交通灯控制器设计与实现指南

QuickStart准备开发环境:安装Vivado或Quartu…
技术分享
11天前
0
0
24
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容