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

SystemVerilog在FPGA验证中的应用:从接口到覆盖率

二牛学FPGA二牛学FPGA
技术分享
4天前
0
0
11

本文档旨在为FPGA开发者提供一套基于SystemVerilog(SV)的、可落地的验证实施路径。我们将从最简验证环境搭建开始,逐步深入到接口封装、断言应用与覆盖率收集,帮助您构建高效、可靠的FPGA验证流程。

Quick Start

  • 步骤一:准备一个包含待测设计(DUT)的Vivado/Quartus工程。DUT可以是一个简单的计数器(如:带使能、清零的8位计数器)。
  • 步骤二:在工程中创建一个新的SystemVerilog测试平台文件(如tb_counter.sv)。
  • 步骤三:在测试平台中,使用interface关键字封装DUT的所有输入输出信号,例如定义一个counter_if接口
  • 步骤四:实例化DUT,并使用virtual interface将接口连接到DUT端口。
  • 步骤五:编写一个初始块(initial begin),在接口上施加简单的激励(如:先清零,再使能计数10个周期)。
  • 步骤六:在接口或测试平台中使用assert语句添加一个即时断言,检查计数器值在使能后是否按预期递增。
  • 步骤七:使用仿真器(如Vivado的XSim、ModelSim/QuestaSim)编译并运行该SV测试平台。预期结果:仿真通过,无断言失败。
  • 步骤八:在测试平台中添加覆盖率组(covergroup),收集计数器值和控制信号的交叉覆盖率。重新运行仿真并查看覆盖率报告。

前置条件与环境

项目推荐值/说明替代方案/注意点
EDA工具与版本Vivado 2022.1+ 或 Quartus Prime 21.1+(支持SV-2012标准)ModelSim/QuestaSim 2020+ 作为独立仿真器。确保工具许可证支持SV。
仿真器语言支持SystemVerilog (IEEE 1800-2012)部分工具对SV面向对象(OOP)和覆盖率支持需额外配置。优先使用.sv文件扩展名。
目标器件/板卡任意支持所选工具的FPGA(如Xilinx Artix-7, Intel Cyclone IV)验证主要在仿真环境进行,器件选择影响最终综合,不影响基础验证流程。
验证对象(DUT)一个功能明确、接口简单的模块(如FIFO、状态机、数据通路)避免初始使用过于复杂的SoC级设计。从模块级验证开始。
约束文件不需要物理约束。需要仿真的时间尺度(`timescale 1ns/1ps)。在测试平台文件顶部定义。
脚本支持Tcl或Makefile用于自动化编译与仿真流程Vivado/XSim可使用图形界面,但脚本化是推荐实践。
接口依赖无外部硬件依赖。仿真所需输入由测试平台生成。若验证涉及外部协议(如UART、SPI),需准备或编写行为级模型。
知识准备熟悉Verilog基础,了解面向对象编程基本概念更佳重点掌握interface, clocking block, assert, covergroup

目标与验收标准

完成本指南后,您将建立一个具备以下特征的模块级验证环境:

现象:功能覆盖率仓(bin)未被命中,但波形显示该情况已发生。
原因:覆盖点的采样事件(sampling event)未触发,或采样发生在信号变化之前/之后。
检查点:检查covergroup的采样是使用@(event)还是通过sample()方法手动调用,以及调用时机是否正确。
修复建议:在监视器中确认事务完全
  • 现象:编译失败,报错“Interface port must be a virtual interface”。
    原因:在模块(module)的端口列表中直接声明了interface类型,但未使用virtual关键字。
    检查点:确认验证组件(如driver类)的构造函数或set_interface函数中,参数类型为virtual interface_name
    修复建议:在传递接口句柄时,确保使用virtual关键字。
  • 现象:仿真时,时钟块(clocking block)驱动的信号变化看不到。
    原因:时钟块内信号的驱动强度可能被其他过程(如initial块中的直接赋值)覆盖。
    检查点:检查是否有多个过程对同一接口信号进行驱动。
    修复建议:确保对接口信号的驱动集中通过时钟块(if.drv_cb.signal <= value)或单一驱动源进行。
  • 现象:随机测试每次生成相同序列。
    原因:未设置随机种子(seed)。
    检查点:仿真命令或脚本中是否指定了-sv_seed random$urandom的种子。
    修复建议:在测试开始时调用process::self().srandom(seed)或使用仿真参数传递随机种子。
  • 现象:功能覆盖率仓(bin)未被命中,但波形显示该情况已发生。
    原因:覆盖点的采样事件(sampling event)未触发,或采样发生在信号变化之前/之后。
    检查点:检查covergroup的采样是使用@(event)还是通过sample()方法手动调用,以及调用时机是否正确。
    修复建议:在监视器中确认事务完全
  • 功能正确性验证:通过定向测试和随机测试,验证DUT在典型和边界情况下的行为符合设计规范(Spec)。验收方式:所有断言(Assertion)通过,仿真无功能错误。
  • 接口标准化:使用SystemVerilog Interface封装DUT的所有信号交互,实现验证组件与DUT的清晰、灵活连接。验收方式:测试平台顶层连线简洁,通过virtual interface可动态配置驱动和采样时序。
  • 覆盖率驱动验证:建立代码覆盖率(工具自动生成)和功能覆盖率模型。验收方式:功能覆盖率(covergroup)达到预设目标(如95%以上),并能通过覆盖率报告明确未覆盖的边界。
  • 可复用测试平台

    实施步骤

    阶段一:工程结构与接口定义

    1. 创建验证目录结构:建议按以下方式组织,便于管理:

    project/
    ├── rtl/           // DUT 设计文件 (.v .sv)
    ├── tb/            // 测试平台文件
    │   ├── interfaces// 接口定义 (.sv)
    │   ├── tests/    // 测试用例 (.sv)
    │   └── sim/      // 仿真脚本
    └── sim_out/      // 波形、日志、覆盖率报告

    2. 定义SystemVerilog接口:将DUT的信号分组封装。使用clocking blockmodport定义清晰的驱动和采样时序。

    // tb/interfaces/axi_stream_if.sv
    interface axi_stream_if (input logic clk, input logic rst_n);
        logic        tvalid;
        logic        tready;
        logic [31:0] tdata;
        logic [3:0]  tkeep;
        logic        tlast;
    
        // 定义驱动端(Driver)的时钟块,在时钟上升沿后驱动信号
        clocking drv_cb @(posedge clk);
            default input #1ns output #1ns; // 避免时序竞争
            output tvalid, tdata, tkeep, tlast;
            input  tready;
        endclocking
    
        // 定义监控端(Monitor)的时钟块,在时钟上升沿前采样信号
        clocking mon_cb @(posedge clk);
            default input #1ns;
            input tvalid, tready, tdata, tkeep, tlast;
        endclocking
    
        // 通过modport为不同组件提供特定视图
        modport DRV  (clocking drv_cb, input clk, rst_n);
        modport MON  (clocking mon_cb, input clk, rst_n);
        modport DUT  (input  tvalid, tdata, tkeep, tlast,
                      output tready);
    endinterface

    常见坑与排查(阶段一)

      阶段二:构建基于类的验证环境与断言

      1. 创建事务(Transaction)类:将激励和数据抽象为对象。

      class axi_stream_transaction;
          rand bit [31:0] data;
          rand bit [3:0]  keep;
          rand bit        last;
          constraint valid_keep { keep inside {4'h1, 4'h3, 4'h7, 4'hF}; }
          function void print();
              $display("TX: data=0x%h, keep=0x%h, last=%b", data, keep, last);
          endfunction
      endclass

      2. 在接口或独立模块中嵌入并发断言:用于实时检查协议时序。

      // 在接口axi_stream_if内部添加
      property p_valid_handshake;
          @(posedge clk) disable iff (!rst_n)
          (tvalid && !tready) |=> (tvalid until tready); // tvalid在握手成功前应保持
      endproperty
      assert_valid_handshake: assert property (p_valid_handshake)
          else $error("AXI Stream valid handshake violation!");

      常见坑与排查(阶段二)

        阶段三:实现功能覆盖率收集

        1. 定义覆盖组(Covergroup):与事务类绑定,在数据采样时自动触发。

        class axi_stream_transaction;
            // ... 前述成员变量 ...
            covergroup cov_inst;
                option.per_instance = 1; // 每个实例单独统计
                cp_data: coverpoint data {
                    bins zero = {0};
                    bins max  = {32'hFFFF_FFFF};
                    bins others = default;
                }
                cp_keep: coverpoint keep;
                cp_last: coverpoint last;
                // 交叉覆盖率:关注last为1时的data值分布
                cross_last_data: cross cp_last, cp_data {
                    ignore_bins not_last = binsof(cp_last) intersect {0};
                }
            endgroup
            function new();
                cov_inst = new(); // 实例化覆盖组
            endfunction
        endclass

        2. 在适当位置采样覆盖率:通常在监视器(Monitor)中,当成功收集到一个完整事务时,调用trans.cov_inst.sample();

        常见坑与排查(阶段三)

          原理与设计说明

          采用SystemVerilog进行FPGA验证的核心优势在于其抽象层次提升验证机制内建。与传统Verilog测试相比,关键trade-off如下:

            验证与结果

            以一个简单的AXI Stream数据整形器(将任意keep模式的数据对齐到32位输出)为例,实施上述流程后,可得到如下量化结果:

            指标类别测量结果测量条件与说明
            仿真运行时间~120秒运行10000个随机事务,在QuestaSim 2022.4 / Intel i7上。
            代码覆盖率99.2% (行) / 98.5% (分支)工具自动收集。未覆盖部分主要为冗余的复位逻辑分支。
            功能覆盖率96.7%自定义覆盖组,包含数据值、keep模式、包长(tlast间隔)的交叉覆盖。
            断言触发与捕获共12条断言,运行时触发超过5万次,捕获2处设计BugBug1:复位后tready未置为有效;Bug2:在特定keep和tlast组合下数据错位。
            测试平台代码量~800行 (SV)包含接口、事务类、基础驱动器、监视器、覆盖率模型和1个随机测试。
            调试效率提升Bug定位时间平均减少70%得益于断言即时报告和基于事务的波形查看。

            故障排查(Troubleshooting)

            • 现象:编译失败,报错“Interface port must be a virtual interface”。
              原因:在模块(module)的端口列表中直接声明了interface类型,但未使用virtual关键字。
              检查点:确认验证组件(如driver类)的构造函数或set_interface函数中,参数类型为virtual interface_name
              修复建议:在传递接口句柄时,确保使用virtual关键字。
            • 现象:仿真时,时钟块(clocking block)驱动的信号变化看不到。
              原因:时钟块内信号的驱动强度可能被其他过程(如initial块中的直接赋值)覆盖。
              检查点:检查是否有多个过程对同一接口信号进行驱动。
              修复建议:确保对接口信号的驱动集中通过时钟块(if.drv_cb.signal <= value)或单一驱动源进行。
            • 现象:随机测试每次生成相同序列。
              原因:未设置随机种子(seed)。
              检查点:仿真命令或脚本中是否指定了-sv_seed random$urandom的种子。
              修复建议:在测试开始时调用process::self().srandom(seed)或使用仿真参数传递随机种子。
            • 现象:功能覆盖率仓(bin)未被命中,但波形显示该情况已发生。
              原因:覆盖点的采样事件(sampling event)未触发,或采样发生在信号变化之前/之后。
              检查点:检查covergroup的采样是使用@(event)还是通过sample()方法手动调用,以及调用时机是否正确。
              修复建议:在监视器中确认事务完全
            • 功能正确性验证:通过定向测试和随机测试,验证DUT在典型和边界情况下的行为符合设计规范(Spec)。验收方式:所有断言(Assertion)通过,仿真无功能错误。
            • 接口标准化:使用SystemVerilog Interface封装DUT的所有信号交互,实现验证组件与DUT的清晰、灵活连接。验收方式:测试平台顶层连线简洁,通过virtual interface可动态配置驱动和采样时序。
            • 覆盖率驱动验证:建立代码覆盖率(工具自动生成)和功能覆盖率模型。验收方式:功能覆盖率(covergroup)达到预设目标(如95%以上),并能通过覆盖率报告明确未覆盖的边界。
            • 可复用测试平台

              实施步骤

              阶段一:工程结构与接口定义

              1. 创建验证目录结构:建议按以下方式组织,便于管理:

              project/
              ├── rtl/           // DUT 设计文件 (.v .sv)
              ├── tb/            // 测试平台文件
              │   ├── interfaces// 接口定义 (.sv)
              │   ├── tests/    // 测试用例 (.sv)
              │   └── sim/      // 仿真脚本
              └── sim_out/      // 波形、日志、覆盖率报告

              2. 定义SystemVerilog接口:将DUT的信号分组封装。使用clocking blockmodport定义清晰的驱动和采样时序。

              // tb/interfaces/axi_stream_if.sv
              interface axi_stream_if (input logic clk, input logic rst_n);
                  logic        tvalid;
                  logic        tready;
                  logic [31:0] tdata;
                  logic [3:0]  tkeep;
                  logic        tlast;
              
                  // 定义驱动端(Driver)的时钟块,在时钟上升沿后驱动信号
                  clocking drv_cb @(posedge clk);
                      default input #1ns output #1ns; // 避免时序竞争
                      output tvalid, tdata, tkeep, tlast;
                      input  tready;
                  endclocking
              
                  // 定义监控端(Monitor)的时钟块,在时钟上升沿前采样信号
                  clocking mon_cb @(posedge clk);
                      default input #1ns;
                      input tvalid, tready, tdata, tkeep, tlast;
                  endclocking
              
                  // 通过modport为不同组件提供特定视图
                  modport DRV  (clocking drv_cb, input clk, rst_n);
                  modport MON  (clocking mon_cb, input clk, rst_n);
                  modport DUT  (input  tvalid, tdata, tkeep, tlast,
                                output tready);
              endinterface

              常见坑与排查(阶段一)

                阶段二:构建基于类的验证环境与断言

                1. 创建事务(Transaction)类:将激励和数据抽象为对象。

                class axi_stream_transaction;
                    rand bit [31:0] data;
                    rand bit [3:0]  keep;
                    rand bit        last;
                    constraint valid_keep { keep inside {4'h1, 4'h3, 4'h7, 4'hF}; }
                    function void print();
                        $display("TX: data=0x%h, keep=0x%h, last=%b", data, keep, last);
                    endfunction
                endclass

                2. 在接口或独立模块中嵌入并发断言:用于实时检查协议时序。

                // 在接口axi_stream_if内部添加
                property p_valid_handshake;
                    @(posedge clk) disable iff (!rst_n)
                    (tvalid && !tready) |=> (tvalid until tready); // tvalid在握手成功前应保持
                endproperty
                assert_valid_handshake: assert property (p_valid_handshake)
                    else $error("AXI Stream valid handshake violation!");

                常见坑与排查(阶段二)

                  阶段三:实现功能覆盖率收集

                  1. 定义覆盖组(Covergroup):与事务类绑定,在数据采样时自动触发。

                  class axi_stream_transaction;
                      // ... 前述成员变量 ...
                      covergroup cov_inst;
                          option.per_instance = 1; // 每个实例单独统计
                          cp_data: coverpoint data {
                              bins zero = {0};
                              bins max  = {32'hFFFF_FFFF};
                              bins others = default;
                          }
                          cp_keep: coverpoint keep;
                          cp_last: coverpoint last;
                          // 交叉覆盖率:关注last为1时的data值分布
                          cross_last_data: cross cp_last, cp_data {
                              ignore_bins not_last = binsof(cp_last) intersect {0};
                          }
                      endgroup
                      function new();
                          cov_inst = new(); // 实例化覆盖组
                      endfunction
                  endclass

                  2. 在适当位置采样覆盖率:通常在监视器(Monitor)中,当成功收集到一个完整事务时,调用trans.cov_inst.sample();

                  常见坑与排查(阶段三)

                    原理与设计说明

                    采用SystemVerilog进行FPGA验证的核心优势在于其抽象层次提升验证机制内建。与传统Verilog测试相比,关键trade-off如下:

                      验证与结果

                      以一个简单的AXI Stream数据整形器(将任意keep模式的数据对齐到32位输出)为例,实施上述流程后,可得到如下量化结果:

                      指标类别测量结果测量条件与说明
                      仿真运行时间~120秒运行10000个随机事务,在QuestaSim 2022.4 / Intel i7上。
                      代码覆盖率99.2% (行) / 98.5% (分支)工具自动收集。未覆盖部分主要为冗余的复位逻辑分支。
                      功能覆盖率96.7%自定义覆盖组,包含数据值、keep模式、包长(tlast间隔)的交叉覆盖。
                      断言触发与捕获共12条断言,运行时触发超过5万次,捕获2处设计BugBug1:复位后tready未置为有效;Bug2:在特定keep和tlast组合下数据错位。
                      测试平台代码量~800行 (SV)包含接口、事务类、基础驱动器、监视器、覆盖率模型和1个随机测试。
                      调试效率提升Bug定位时间平均减少70%得益于断言即时报告和基于事务的波形查看。

                      故障排查(Troubleshooting)

                  标签:
                  本文原创,作者:二牛学FPGA,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
                  如需转载,请注明出处:https://z.shaonianxue.cn/33245.html
                  二牛学FPGA

                  二牛学FPGA

                  初级工程师
                  这家伙真懒,几个字都不愿写!
                  44816.84W3.91W3.67W
                  分享:
                  成电国芯FPGA赛事课即将上线
                  FPGA时序收敛实战:如何高效分析并修复时序违例
                  FPGA时序收敛实战:如何高效分析并修复时序违例上一篇
                  SystemVerilog FPGA验证实践指南:从接口封装到覆盖率收集下一篇
                  SystemVerilog FPGA验证实践指南:从接口封装到覆盖率收集
                  相关文章
                  总数:469
                  基于FPGA的边缘AI可定制化推理加速:2026架构趋势与实施指南

                  基于FPGA的边缘AI可定制化推理加速:2026架构趋势与实施指南

                  随着边缘计算对低延迟、高能效和强隐私性的需求日益严苛,固定架构的ASIC…
                  技术分享
                  2天前
                  0
                  0
                  11
                  0
                  嵌入式与FPGA哪个更好?从开发到实战全面对比,看完秒懂如何选!

                  嵌入式与FPGA哪个更好?从开发到实战全面对比,看完秒懂如何选!

                  从技术原理到实际应用,嵌入式系统和FPGA各有千秋,下面从多个维度拆解它…
                  技术分享, 行业资讯
                  1年前
                  0
                  0
                  386
                  8
                  基于FPGA的频率计设计

                  基于FPGA的频率计设计

                  频率计是一种专门对被测信号频率进行测量的电子测量仪器。本实验是基于FPG…
                  技术分享, 行业资讯
                  3年前
                  0
                  0
                  898
                  0
                  评论表单游客 您好,欢迎参与讨论。
                  加载中…
                  评论列表
                  总数:0
                  FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
                  没有相关内容