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

SystemVerilog仿真:2026年用UVM搭建FPGA验证环境的快速指南

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

Quick Start

本指南帮助您在30分钟内搭建一个可运行的UVM验证环境,并完成对FPGA RTL模块的仿真验证。以下是最短路径步骤:

  • 步骤1:安装支持UVM-1.2或IEEE 1800.2-2023的仿真器(如QuestaSim 2024.3、VCS 2025.06、Xsim 2025.2)。
  • 步骤2:创建工程目录结构(src/rtl, src/tb, scripts, sim)。
  • 步骤3:编写一个简单的DUT(例如FIFO或加法器),保存为.sv文件。
  • 步骤4:编写UVM testbench顶层,包含interface、transaction、driver、monitor、scoreboard、test。
  • 步骤5:编写仿真脚本(Makefile或Tcl),编译所有源文件并运行仿真。
  • 步骤6:观察仿真日志,确认testbench启动、sequence发送、scoreboard比对通过。
  • 步骤7:修改DUT代码引入一个故意错误,验证scoreboard能捕获并报告失败。
  • 步骤8:清理临时文件,将工程纳入版本控制(Git)。

验收点:运行脚本后终端显示“# UVM_INFO @ 0: reporter [RNTST] Running test my_test”和“# UVM_INFO @ 1000: reporter [SCOREBOARD] PASSED: 10 transactions”。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 (xc7a35t)FPGA验证环境与器件无关,但约束与时序需匹配目标器件Intel Cyclone V, Lattice ECP5
EDA版本QuestaSim 2024.3原生支持UVM-1.2,编译速度快VCS 2025.06, Xsim 2025.2, Riviera-PRO 2024.10
仿真器QuestaSim (vsim)用于UVM testbench仿真VCS, Xsim, Icarus Verilog (仅支持部分UVM)
时钟/复位100 MHz时钟,异步低有效复位DUT与testbench共享时钟50 MHz或其他频率
接口依赖AXI4-Stream或简单Valid-Ready便于UVM driver/monitor建模APB, AHB-Lite
约束文件无(仿真阶段)综合与实现时才需要XDC/SDC
操作系统Ubuntu 22.04 LTS 或 Windows 11仿真器均支持CentOS 7, macOS (有限支持)

目标与验收标准

完成本指南后,您应能:

  • 功能点:UVM testbench能对DUT进行定向与随机激励,自动比对结果。
  • 性能指标:仿真速度不低于1000个事务/秒(取决于DUT复杂度)。
  • 资源/Fmax:不适用(仿真阶段)。
  • 关键波形/日志:仿真日志包含UVM_INFO、UVM_ERROR、UVM_FATAL信息;波形文件(.vcd或.wlf)可查看信号变化。

验收方法:运行完整回归测试,scoreboard报告“PASSED: 1000 transactions, 0 errors”。故意在DUT中插入一个错误,重新仿真应报告“FAILED: 1 error detected”。

实施步骤

阶段1:工程结构

创建以下目录结构:

uvm_fpga_demo/
├── src/
│   ├── rtl/
│   │   └── simple_fifo.sv
│   └── tb/
│       ├── fifo_if.sv
│       ├── fifo_transaction.sv
│       ├── fifo_driver.sv
│       ├── fifo_monitor.sv
│       ├── fifo_scoreboard.sv
│       ├── fifo_env.sv
│       ├── fifo_test.sv
│       └── top_tb.sv
├── scripts/
│   └── run_sim.tcl
└── sim/
    └── (仿真输出目录)

逐行说明

  • 第1行:工程根目录,建议与Git仓库同名。
  • 第2行:源代码根目录。
  • 第3-4行:RTL设计文件存放位置,simple_fifo.sv是待测模块。
  • 第5-12行:UVM testbench文件,每个组件一个文件,便于管理。
  • 第13行:仿真脚本目录。
  • 第14行:仿真输出目录,编译与运行中间文件放在这里。

阶段2:关键模块

以下是一个简单的同步FIFO DUT(深度16,数据宽度8位):

// src/rtl/simple_fifo.sv
module simple_fifo #(
    parameter DEPTH = 16,
    parameter WIDTH = 8
)(
    input  logic        clk,
    input  logic        rst_n,
    input  logic        wr_en,
    input  logic [WIDTH-1:0] wr_data,
    output logic        full,
    input  logic        rd_en,
    output logic [WIDTH-1:0] rd_data,
    output logic        empty
);
    logic [WIDTH-1:0] mem [0:DEPTH-1];
    logic [$clog2(DEPTH):0] wr_ptr, rd_ptr;
    logic [$clog2(DEPTH):0] count;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            wr_ptr <= 0;
            rd_ptr <= 0;
            count <= 0;
        end else begin
            if (wr_en && !full) begin
                mem[wr_ptr] <= wr_data;
                wr_ptr <= wr_ptr + 1;
                count <= count + 1;
            end
            if (rd_en && !empty) begin
                rd_data <= mem[rd_ptr];
                rd_ptr <= rd_ptr + 1;
                count <= count - 1;
            end
        end
    end

    assign full  = (count == DEPTH);
    assign empty = (count == 0);
endmodule

逐行说明

  • 第1行:模块声明,参数化深度和宽度。
  • 第2-3行:参数默认值,可在实例化时覆盖。
  • 第4-11行:端口列表,包括时钟、复位、写使能、写数据、满标志、读使能、读数据、空标志。
  • 第12行:内部存储器,二维数组。
  • 第13行:读写指针,位宽为$clog2(DEPTH)+1,用于判断满/空。
  • 第14行:计数器,记录当前存储的数据个数。
  • 第15行:时序逻辑块,敏感列表为时钟上升沿或复位下降沿。
  • 第16-19行:复位逻辑,清零所有指针和计数器。
  • 第20-25行:写操作,当wr_en有效且非满时写入数据,指针递增,计数器加1。
  • 第26-31行:读操作,当rd_en有效且非空时读出数据,指针递增,计数器减1。
  • 第33-34行:组合逻辑赋值满和空标志。

接下来是UVM testbench组件。先定义接口(interface):

// src/tb/fifo_if.sv
interface fifo_if (input logic clk, input logic rst_n);
    logic        wr_en;
    logic [7:0]  wr_data;
    logic        full;
    logic        rd_en;
    logic [7:0]  rd_data;
    logic        empty;

    modport DUT (input clk, rst_n, wr_en, wr_data, rd_en,
                 output full, rd_data, empty);
    modport TB  (output wr_en, wr_data, rd_en,
                 input  full, rd_data, empty);
endinterface

逐行说明

  • 第1行:接口声明,包含时钟和复位作为参数。
  • 第2-7行:信号声明,与DUT端口对应。
  • 第9-10行:DUT modport,方向为DUT视角(输入wr_en等,输出full等)。
  • 第11-12行:TB modport,方向为testbench视角(输出wr_en等,输入full等)。

事务类(transaction):

// src/tb/fifo_transaction.sv
class fifo_transaction extends uvm_sequence_item;
    rand logic        wr_en;
    rand logic [7:0]  wr_data;
    rand logic        rd_en;

    constraint valid_c {
        wr_en dist {0 := 30, 1 := 70}; // 70%概率写
        rd_en dist {0 := 30, 1 := 70}; // 70%概率读
    }

    `uvm_object_utils_begin(fifo_transaction)
        `uvm_field_int(wr_en, UVM_ALL_ON)
        `uvm_field_int(wr_data, UVM_ALL_ON)
        `uvm_field_int(rd_en, UVM_ALL_ON)
    `uvm_object_utils_end

    function new(string name = "fifo_transaction");
        super.new(name);
    endfunction
endclass

逐行说明

  • 第1行:类声明,继承自uvm_sequence_item。
  • 第2-4行:随机变量,对应接口信号。
  • 第6-9行:约束,写使能和读使能各有70%概率为1,模拟随机读写。
  • 第11-15行:UVM自动化宏,注册字段以便于复制、比较、打印。
  • 第17-19行:构造函数。

Driver组件:

// src/tb/fifo_driver.sv
class fifo_driver extends uvm_driver #(fifo_transaction);
    `uvm_component_utils(fifo_driver)

    virtual fifo_if vif;

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db #(virtual fifo_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);
            drive_transaction(req);
            seq_item_port.item_done();
        end
    endtask

    task drive_transaction(fifo_transaction tr);
        @(posedge vif.clk);
        vif.wr_en <= tr.wr_en;
        vif.wr_data <= tr.wr_data;
        vif.rd_en <= tr.rd_en;
    endtask
endclass

逐行说明

  • 第1行:类声明,参数化为fifo_transaction。
  • 第2行:UVM组件注册宏。
  • 第4行:虚拟接口句柄,用于驱动DUT信号。
  • 第6-8行:构造函数。
  • 第10-13行:build_phase,从config_db获取虚拟接口,若失败则报fatal。
  • 第15-20行:run_phase,循环从sequence获取事务并驱动,每次驱动后通知item_done。
  • 第22-27行:驱动任务,在时钟上升沿后给接口赋值。

Monitor组件:

// src/tb/fifo_monitor.sv
class fifo_monitor extends uvm_monitor;
    `uvm_component_utils(fifo_monitor)

    virtual fifo_if vif;
    uvm_analysis_port #(fifo_transaction) mon_ap;

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db #(virtual fifo_if)::get(this, "", "vif", vif))
            `uvm_fatal("NOVIF", "Virtual interface not set")
        mon_ap = new("mon_ap", this);
    endfunction

    task run_phase(uvm_phase phase);
        forever begin
            @(posedge vif.clk);
            if (vif.wr_en || vif.rd_en) begin
                tr = fifo_transaction::type_id::create("tr");
                tr.wr_en   = vif.wr_en;
                tr.wr_data = vif.wr_data;
                tr.rd_en   = vif.rd_en;
                mon_ap.write(tr);
            end
        end
    endtask

    fifo_transaction tr;
endclass

逐行说明

  • 第1行:类声明,继承自uvm_monitor。
  • 第4行:虚拟接口句柄。
  • 第5行:分析端口,用于发送监测到的事务。
  • 第10-14行:build_phase,获取接口并创建分析端口。
  • 第16-26行:run_phase,每个时钟上升沿采样,若wr_en或rd_en有效则创建事务对象并写入分析端口。
  • 第28行:内部事务对象句柄。

Scoreboard组件(简化版,仅计数):

// src/tb/fifo_scoreboard.sv
class fifo_scoreboard extends uvm_scoreboard;
    `uvm_component_utils(fifo_scoreboard)

    uvm_analysis_imp #(fifo_transaction, fifo_scoreboard) mon_export;
    int pass_count, fail_count;

    function new(string name, uvm_component parent);
        super.new(name, parent);
        mon_export = new("mon_export", this);
    endfunction

    function void write(fifo_transaction tr);
        // 简化:仅检查是否同时写和读(合法),否则计数
        if (tr.wr_en && tr.rd_en) begin
            pass_count++;
            `uvm_info("SCOREBOARD", $sformatf("PASS: wr=%0d rd=%0d", tr.wr_en, tr.rd_en), UVM_LOW)
        end else begin
            fail_count++;
            `uvm_error("SCOREBOARD", $sformatf("FAIL: wr=%0d rd=%0d", tr.wr_en, tr.rd_en))
        end
    endfunction

    function void report_phase(uvm_phase phase);
        super.report_phase(phase);
        `uvm_info("SCOREBOARD", $sformatf("PASSED: %0d, FAILED: %0d", pass_count, fail_count), UVM_LOW)
    endfunction
endclass

逐行说明

  • 第1行:类声明,继承自uvm_scoreboard。
  • 第4行:分析实现端口,接收monitor发送的事务。
  • 第5行:通过和失败计数。
  • 第9-11行:构造函数,创建分析实现端口。
  • 第13-21行:write函数,每次收到事务时被调用,这里简单判断wr_en和rd_en同时有效则通过,否则失败。实际应比对预期输出。
  • 第23-26行:report_phase,仿真结束时打印统计信息。

Env组件:

// src/tb/fifo_env.sv
class fifo_env extends uvm_env;
    `uvm_component_utils(fifo_env)

    fifo_driver    driver;
    fifo_monitor   monitor;
    fifo_scoreboard scoreboard;

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        driver    = fifo_driver::type_id::create("driver", this);
        monitor   = fifo_monitor::type_id::create("monitor", this);
        scoreboard = fifo_scoreboard::type_id::create("scoreboard", this);
    endfunction

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);
        monitor.mon_ap.connect(scoreboard.mon_export);
    endfunction
endclass

逐行说明

  • 第1行:类声明,继承自uvm_env。
  • 第4-6行:子组件句柄。
  • 第10-12行:构造函数。
  • 第14-18行:build_phase,创建所有子组件。
  • 第20-23行:connect_phase,连接monitor的分析端口到scoreboard的导出。

Test组件:

// src/tb/fifo_test.sv
class fifo_test extends uvm_test;
    `uvm_component_utils(fifo_test)

    fifo_env env;

    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        env = fifo_env::type_id::create("env", this);
    endfunction

    task run_phase(uvm_phase phase);
        fifo_sequence seq;
        phase.raise_objection(this);
        seq = fifo_sequence::type_id::create("seq");
        seq.start(env.driver.seq_item_port);
        phase.drop_objection(this);
    endtask
endclass

逐行说明

第12-15行:build_phase,创建env。<!-- /
  • 第1行:类声明,继承自uvm_test。
  • 第4行:env句柄。
  • 第8-10行:构造函数。
  • 第12-15行:build_phase,创建env。
  • <!-- /
标签:
本文原创,作者:FPGA小白,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/41687.html
FPGA小白

FPGA小白

初级工程师
成电国芯®的讲师哦,专业FPGA已有10年。
38721.19W7.24W34.40W
分享:
成电国芯FPGA赛事课即将上线
SystemVerilog仿真:基于UVM搭建FPGA验证环境的快速实施指南
SystemVerilog仿真:基于UVM搭建FPGA验证环境的快速实施指南上一篇
2026年5月FPGA毕设选题指南:边缘AI与国产平台设计实践下一篇
2026年5月FPGA毕设选题指南:边缘AI与国产平台设计实践
相关文章
总数:1.03K
2026年AI芯片设计趋势:FPGA如何作为验证加速平台

2026年AI芯片设计趋势:FPGA如何作为验证加速平台

QuickStart步骤1:安装EDA工具下载并安装Vivado2…
技术分享
12天前
0
0
74
0
从零开始学FPGA:2026年入门路线图

从零开始学FPGA:2026年入门路线图

QuickStart步骤一:下载并安装Vivado2024.2(或最…
技术分享
8天前
0
0
16
0
大疆算法工程师笔试题资源下载

大疆算法工程师笔试题资源下载

本文提供了一个名为“大疆算法工程师笔试.pdf”的资源文件下载。该文件包…
技术分享
1年前
0
0
635
1
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容