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

SystemVerilog仿真:基于UVM搭建FPGA验证环境的快速实施指南

FPGA小白FPGA小白
技术分享
3小时前
0
0
4

Quick Start

以下步骤帮助你在30分钟内跑通第一个UVM验证环境,并看到仿真通过日志。

    [object Object]

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 (XC7A35T)用于FPGA上板验证,UVM环境独立于器件Intel Cyclone V / Lattice ECP5
EDA版本Questa 2024.2 / VCS 2024.06支持SystemVerilog 2012和UVM 1.2Xcelium 23.09 / Riviera-PRO 2024.04
仿真器QuestaSim(ModelSim升级版)免费版支持UVM基本特性VCS MX / Xcelium
时钟/复位100MHz时钟,异步低电平复位DUT典型接口50MHz / 200MHz;高电平复位
接口依赖AXI4-Stream (axis)示例DUT使用,UVM代理通用AHB / APB / Wishbone
约束文件无(仿真阶段不需要XDC)综合时才需要
操作系统Ubuntu 22.04 LTS / CentOS 7EDA工具官方支持Windows 10/11 (WSL2)

目标与验收标准

  • 功能点:UVM环境能生成随机事务,驱动DUT输入,监测DUT输出,并与参考模型比对。
  • 性能指标:仿真速度不低于10K事务/秒(以100MHz时钟、事务长度64拍计算)。
  • 资源/Fmax:不适用(仿真阶段)。
  • 验收方式:运行vsim -c -do "run -all; exit"后,日志最后一行包含“UVM_INFO: Test completed with 0 errors”。
  • 关键波形:在GUI模式下打开波形,观察axis_tvalidaxis_tready握手,数据正确。

实施步骤

阶段1:工程结构与DUT

  • 创建目录树:uvm_fpga_demo/{rtl, tb, sim}
  • 编写DUT:一个简单的AXI4-Stream FIFO,深度16,数据位宽8位。
  • 常见坑:确保DUT的时钟与复位端口与UVM接口匹配;避免在DUT中使用UVM特有的类型。
// rtl/axis_fifo.sv
module axis_fifo (
    input logic clk,
    input logic rst_n,
    input logic s_axis_tvalid,
    input logic [7:0] s_axis_tdata,
    output logic s_axis_tready,
    output logic m_axis_tvalid,
    output logic [7:0] m_axis_tdata,
    input logic m_axis_tready
);
    localparam DEPTH = 16;
    logic [7:0] mem [0:DEPTH-1];
    logic [$clog2(DEPTH):0] wr_ptr, rd_ptr;
    logic full, empty;
    assign full = (wr_ptr - rd_ptr) == DEPTH;
    assign empty = (wr_ptr == rd_ptr);
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_ptr <= 0;
            rd_ptr <= 0;
        end else begin
            if (s_axis_tvalid && s_axis_tready) begin
                mem[wr_ptr[$clog2(DEPTH)-1:0]] <= s_axis_tdata;
                wr_ptr <= wr_ptr + 1;
            end
            if (m_axis_tvalid && m_axis_tready) begin
                rd_ptr <= rd_ptr + 1;
            end
        end
    end
    assign s_axis_tready = !full;
    assign m_axis_tvalid = !empty;
    assign m_axis_tdata = mem[rd_ptr[$clog2(DEPTH)-1:0]];
endmodule

逐行说明

  • 第1行:模块声明,端口包括时钟、复位、AXI4-Stream从接口(s_axis_*)和主接口(m_axis_*)。
  • 第2-3行:localparam定义FIFO深度为16,内部存储数组mem。
  • 第4行:读写指针,宽度为log2(深度)+1,用于判断满空。
  • 第5-6行:满标志:写指针减去读指针等于深度;空标志:两指针相等。
  • 第7-17行:时序逻辑,复位时指针清零;写使能时存入数据并递增写指针;读使能时递增读指针。
  • 第18-19行:输出赋值:tready为不满,tvalid为非空,tdata从读指针位置读取。

阶段2:UVM环境搭建

  • 创建UVM组件:事务、接口、驱动、监视器、代理、环境、测试。
  • 事务定义:继承uvm_sequence_item,包含tvalid、tdata、tready等字段。
  • 接口定义:virtual interface axis_if,包含时钟块和时钟同步驱动。
  • 常见坑:接口必须声明为virtual并在测试中通过config_db设置;事务中的rand变量需加rand关键字。
// tb/my_transaction.sv
class my_transaction extends uvm_sequence_item;
    rand logic tvalid;
    rand logic [7:0] tdata;
    rand logic tready;
    `uvm_object_utils_begin(my_transaction)
    `uvm_field_int(tvalid, UVM_ALL_ON)
    `uvm_field_int(tdata, UVM_ALL_ON)
    `uvm_field_int(tready, UVM_ALL_ON)
    `uvm_object_utils_end
    function new(string name = "my_transaction");
        super.new(name);
    endfunction
endclass

逐行说明

  • 第1行:类声明,继承自uvm_sequence_item,使其可参与UVM序列/事务机制。
  • 第2-4行rand关键字使字段可随机化;tvalid/tdata/tready对应AXI4-Stream信号。
  • 第5-8行:UVM自动化宏,提供copy/compare/print等方法,UVM_ALL_ON表示所有操作都包含这些字段。
  • 第9-11行:构造函数,调用父类构造函数。
// tb/my_if.sv
interface axis_if (input logic clk, input logic rst_n);
    logic tvalid;
    logic [7:0] tdata;
    logic tready;
    clocking cb @(posedge clk);
        output tvalid, tdata;
        input tready;
    endclocking
    modport DRIVER (clocking cb, output tvalid, tdata, input tready);
    modport MONITOR (input tvalid, tdata, tready);
endinterface

逐行说明

  • 第1行:接口声明,带时钟和复位参数,便于连接到DUT。
  • 第2-4行:内部信号声明。
  • 第5-8行:时钟块cb,在时钟上升沿驱动/采样;output方向表示驱动到DUT,input表示从DUT采样。
  • 第9-10行:modport定义不同角色的信号方向视图;DRIVER使用时钟块,MONITOR直接输入。
// tb/my_driver.sv
class my_driver extends uvm_driver #(my_transaction);
    virtual axis_if vif;
    `uvm_component_utils(my_driver)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
    function void build_phase(uvm_phase phase);
        if (!uvm_config_db #(virtual axis_if)::get(this, "", "vif", vif))
            `uvm_fatal("NOVIF", "Virtual interface not set")
    endfunction
    task run_phase(uvm_phase phase);
        forever begin
            seq_item_port.get_next_item(req);
            vif.cb.tvalid <= req.tvalid;
            vif.cb.tdata <= req.tdata;
            @(posedge vif.clk);
            seq_item_port.item_done();
        end
    endtask
endclass

逐行说明

  • 第1行:参数化驱动类,指定事务类型为my_transaction。
  • 第2行:声明virtual接口句柄,用于连接DUT。
  • 第3行:UVM组件注册宏。
  • 第4-6行:构造函数。
  • 第7-10行:build_phase中通过config_db获取virtual接口;若失败则报UVM_FATAL终止仿真。
  • 第11-17行:run_phase中无限循环:从序列端口获取下一个事务,驱动到时钟块的输出信号,等待一个时钟周期后通知完成。

阶段3:顶层与仿真运行

  • 编写顶层模块top.sv,例化DUT和接口,调用run_test()
  • 编写测试类my_test,在build_phase中创建环境,在run_phase中启动序列。
  • 常见坑:顶层中必须将接口通过config_db设置给测试;run_test()参数需与测试类名一致(字符串)。
// tb/top.sv
module top;
    logic clk, rst_n;
    axis_if if0 (clk, rst_n);
    axis_fifo dut (
        .clk(clk), .rst_n(rst_n),
        .s_axis_tvalid(if0.tvalid),
        .s_axis_tdata (if0.tdata),
        .s_axis_tready(if0.tready),
        .m_axis_tvalid(),
        .m_axis_tdata (),
        .m_axis_tready(1'b1)
    );
    initial begin
        clk = 0;
        forever #5 clk = ~clk; // 100MHz
    end
    initial begin
        rst_n = 0;
        #20 rst_n = 1;
    end
    initial begin
        uvm_config_db #(virtual axis_if)::set(null, "uvm_test_top.env.agent.driver", "vif", if0.DRIVER);
        uvm_config_db #(virtual axis_if)::set(null, "uvm_test_top.env.agent.monitor", "vif", if0.MONITOR);
        run_test("my_test");
    end
endmodule

逐行说明

  • 第1行:模块声明。
  • 第2行:声明时钟和复位信号。
  • 第3行:实例化接口,连接时钟和复位。
  • 第4-12行:例化DUT,将接口信号连接到DUT端口;主接口的tready固定为1(简化)。
  • 第13-16行:时钟生成,周期10ns(100MHz)。
  • 第17-20行:复位序列,低电平有效,20ns后释放。
  • 第21-24行:通过config_db将接口设置给驱动器和监视器;路径字符串需与UVM层次一致;最后调用run_test启动测试。

阶段4:验证与调试

  • 运行仿真命令:cd sim && vlog -sv ../rtl/*.sv ../tb/*.sv +incdir+../tb && vsim -c -do "run -all; exit" work.top
  • 观察日志,确认无UVM_FATAL或UVM_ERROR。
  • 常见坑:若出现“Interface not set”错误,检查config_db路径是否与UVM层次匹配;若出现“Transaction not driven”,检查驱动run_phase中的循环。

原理与设计说明

UVM(Universal Verification Methodology)基于SystemVerilog,提供可重用的验证组件架构。其核心思想是:

  • 事务级建模(TLM):通过事务(transaction)抽象信号级交互,提高仿真效率。
  • 工厂模式:通过uvm_component_utilsuvm_object_utils注册,支持类型覆盖和配置。
  • 层次化结构:test → env → agent(driver + monitor + sequencer) → DUT,便于复用。
  • 配置数据库(config_db):解耦组件间依赖,通过字符串路径传递virtual interface和参数。

关键trade-off

  • 资源 vs Fmax:UVM环境仅用于仿真,不消耗FPGA资源;但仿真速度受事务粒度影响——事务越细(每拍一个事务),仿真越慢;建议每事务包含多个时钟周期(如64拍),提升吞吐。
  • 吞吐 vs 延迟:UVM的TLM端口(put/get)有阻塞和非阻塞模式,阻塞模式代码简单但可能降低仿真器调度效率;非阻塞模式(try_put/try_get)可避免死锁,但需要额外状态管理。
  • 易用性 vs 可移植性:使用UVM 1.2标准库可移植到任何支持SystemVerilog 2012的仿真器;但某些厂商扩展(如Questa的uvm_ml)会降低可移植性,应避免。

验证与结果

测量项结果(示例)测量条件
仿真时间12秒(1000事务)Questa 2024.2,Ubuntu 22.04,i7-12700
事务吞吐~83事务/秒每个事务64个时钟周期,100MHz
UVM错误数0日志最后一行确认
波形正确性所有事务数据匹配比对DUT输出与参考模型

说明:以上数据基于示例配置,实际结果以用户工程为准。

故障排查(Troubleshooting)

  • 现象:编译错误“Unknown type 'uvm_*'”。原因:未包含UVM库。检查:编译命令是否加了+incdir+$UVM_HOME/src或使用-uvm开关。修复:在vlog命令中加入-uvm或手动指定UVM源文件路径。
  • 现象:UVM_FATAL “Virtual interface not set”。原因:config_db未正确设置。检查:顶层中set的路径字符串是否与UVM层次完全匹配。修复:打印uvm_top.get_name()确认层次,修正路径。
  • 现象:仿真卡住或超时。原因:驱动run_phase中未调用item_done(),或序列未发送完成。检查:驱动循环中是否有seq_item_port.item_done()。修复:添加item_done调用。
  • 现象:事务随机化失败。原因:rand变量约束冲突。检查:是否所有rand变量都有合法范围。修复:添加constraint valid { tvalid dist {1:=90, 0:=10}; }等约束。
  • 现象:波形中信号为X。原因:未正确驱动或复位未释放。检查:复位时序是否足够长;驱动是否在复位后开始。修复:确保rst_n释放后至少一个时钟周期再启动事务。
  • 现象:UVM_ERROR “TLM port not connected”。原因:未连接monitor到scoreboard或参考模型。检查:env中是否调用了monitor.ap.connect(scoreboard.analysis_export)。修复:在env的connect_phase中添加连接。
  • 现象:仿真速度极慢。原因:事务粒度过细(每拍一个事务)。检查:每个事务包含的时钟周期数。修复:在sequence中生成事务时,将多个时钟周期的数据打包到一个事务中。
  • 现象:不同仿真器结果不一致。原因:UVM库版本或SystemVerilog标准支持差异。检查:仿真器是否支持UVM 1.2。修复:统一使用UVM 1.2标准,避免使用厂商扩展。
  • 现象:run_test()未找到测试类。原因:测试类名与字符串参数不匹配。检查:run_test("my_test")中的字符串是否与类名一致。修复:确保类名和字符串完全一致(大小写敏感)。

扩展与进阶

本指南提供了一个最小可用的UVM验证环境。在此基础上,你可以:

  • 添加功能覆盖率收集(covergroup)。
  • 实现参考模型与自动比对(scoreboard)。
  • 引入序列库(sequence library)以生成复杂激励。
  • 集成断言(SVA)进行协议检查。
  • 迁移到多DUT或多agent环境。

参考

  • IEEE Std 1800-2017: SystemVerilog Language
  • UVM 1.2 Class Reference
  • Accellera UVM 1.2 User’s Guide

附录

附录A:完整工程文件列表(略)。附录B:常见EDA工具编译选项速查表(略)。

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

FPGA小白

初级工程师
成电国芯®的讲师哦,专业FPGA已有10年。
38721.19W7.24W34.40W
分享:
成电国芯FPGA赛事课即将上线
FPGA上实现YOLOv8n的INT8量化推理部署:实施指南
FPGA上实现YOLOv8n的INT8量化推理部署:实施指南上一篇
SystemVerilog仿真:2026年用UVM搭建FPGA验证环境的快速指南下一篇
SystemVerilog仿真:2026年用UVM搭建FPGA验证环境的快速指南
相关文章
总数:1.03K
从零开始学Verilog:数据类型与运算符详解

从零开始学Verilog:数据类型与运算符详解

QuickStart步骤一:安装EDA工具(如Vivado或Model…
技术分享
8天前
0
0
20
0
FPGA工程师转型指南:系统级验证与AI部署技能实践(2026版)

FPGA工程师转型指南:系统级验证与AI部署技能实践(2026版)

QuickStart:快速上手概述本指南面向希望掌握系统级验证与AI部…
技术分享
1天前
0
0
8
0
数字IC/FPGA校招技术面试准备指南:高频问题解析与实施路径

数字IC/FPGA校招技术面试准备指南:高频问题解析与实施路径

本文旨在为2026届及之后的数字IC/FPGA方向应届毕业生提供一份系统…
技术分享
19天前
0
0
33
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容