直接上手 UVM 例子

情况:1.没学过 SystemVerilog2.有Questa仿真工具3.工作中项目

适合走 "边用边学" 的路线 ------ 不先啃 SystemVerilog 语法书,而是直接上手 UVM 例子 ,遇到不懂的语法现学现用。因为你有 Questa 工具,能随时跑起来看波形,这是最好的学习方式。


给你的定制学习计划(4 周,每天 1 小时)

目标 产出
第1周 跑通加法器 UVM 平台 能跑出波形 + 打印
第2周 搞懂 sequence / driver / monitor 会改数据、看结果
第3周 加 scoreboard + coverage 能自动比对
第4周 套用到你的工作项目 能自己搭

第 1 课(今天):先跑起来,再理解

我给你的第一个任务 不是背语法 ,而是 把下面 6 个文件复制到 Questa 里跑通。跑通了你就有感觉了。

文件清单(6 个)

text

Copy

Download

复制代码
adder_if.sv            ← 接口(连接 DUT 和验证平台)
adder.sv               ← DUT(被验证的加法器)
adder_transaction.sv   ← 事务(数据包)
adder_driver.sv        ← 驱动器
adder_monitor.sv       ← 监视器
adder_test.sv          ← 顶层测试
tb_top.sv              ← 仿真顶层(连 DUT + 启动 UVM)

① adder_if.sv

systemverilog

Copy

Download

复制代码
interface adder_if (input logic clk, input logic rst_n);
    logic [7:0] a;
    logic [7:0] b;
    logic [8:0] sum;
endinterface

说明: interface 就是"一组信号的打包",把 a、b、sum 装在一起,方便传递。


adder.sv(DUT)

systemverilog

Copy

Download

复制代码
module adder (
    input  logic       clk,
    input  logic       rst_n,
    input  logic [7:0] a,
    input  logic [7:0] b,
    output logic [8:0] sum
);
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) sum <= 9'd0;
        else        sum <= a + b;
    end
endmodule

③ adder_transaction.sv

systemverilog

Copy

Download

复制代码
class adder_transaction extends uvm_sequence_item;
    rand bit [7:0] a;
    rand bit [7:0] b;
         bit [8:0] sum;

    `uvm_object_utils(adder_transaction)

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

④ adder_driver.sv

systemverilog

Copy

Download

复制代码
class adder_driver extends uvm_driver #(adder_transaction);
    `uvm_component_utils(adder_driver)
    virtual adder_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 adder_if)::get(this, "", "vif", vif))
            `uvm_fatal("DRV", "vif 没拿到!")
    endfunction

    task run_phase(uvm_phase phase);
        adder_transaction tr;
        forever begin
            seq_item_port.get_next_item(tr);
            vif.a <= tr.a;
            vif.b <= tr.b;
            @(posedge vif.clk);
            seq_item_port.item_done();
        end
    endtask
endclass

⑤ adder_monitor.sv

systemverilog

Copy

Download

复制代码
class adder_monitor extends uvm_monitor;
    `uvm_component_utils(adder_monitor)
    virtual adder_if vif;
    uvm_analysis_port #(adder_transaction) ap;  // 分析端口,往外发数据

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

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db#(virtual adder_if)::get(this, "", "vif", vif))
            `uvm_fatal("MON", "vif 没拿到!")
    endfunction

    task run_phase(uvm_phase phase);
        forever begin
            adder_transaction tr;
            @(posedge vif.clk);
            tr = adder_transaction::type_id::create("tr");
            tr.a   = vif.a;
            tr.b   = vif.b;
            tr.sum = vif.sum;
            `uvm_info("MON", $sformatf("采样: a=%0d b=%0d sum=%0d", tr.a, tr.b, tr.sum), UVM_LOW)
            ap.write(tr);
        end
    endtask
endclass

⑥ adder_test.sv(顶层)

systemverilog

Copy

Download

复制代码
class adder_test extends uvm_test;
    `uvm_component_utils(adder_test)

    adder_driver  drv;
    adder_monitor mon;
    uvm_sequencer #(adder_transaction) sqr;

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

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        drv = adder_driver::type_id::create("drv", this);
        mon = adder_monitor::type_id::create("mon", this);
        sqr = uvm_sequencer#(adder_transaction)::type_id::create("sqr", this);

        // 连接 driver 和 sequencer
        drv.seq_item_port.connect(sqr.seq_item_export);
    endfunction

    task run_phase(uvm_phase phase);
        // 简单的 sequence:直接手写
        adder_transaction tr;
        phase.raise_objection(this);
        repeat (5) begin
            tr = adder_transaction::type_id::create("tr");
            tr.randomize();          // 随机 a、b
            sqr.execute_item(tr);    // 发给 sequencer → driver
        end
        #50;
        phase.drop_objection(this);
    endtask
endclass

注意: sqr.execute_item(tr) 是简化写法,正式写法用 sequence 类。第 2 课我们改正式写法。


⑦ tb_top.sv(仿真入口)

systemverilog

Copy

Download

复制代码
`timescale 1ns/1ps
module tb_top;
    import uvm_pkg::*;
    `include "uvm_macros.svh"

    logic clk = 0;
    logic rst_n;

    // 时钟
    always #5 clk = ~clk;

    // 接口
    adder_if intf(.clk(clk), .rst_n(rst_n));

    // DUT
    adder dut (
        .clk(clk), .rst_n(rst_n),
        .a(intf.a), .b(intf.b), .sum(intf.sum)
    );

    // 把接口注册到 config_db
    initial begin
        uvm_config_db#(virtual adder_if)::set(null, "*", "vif", intf);
        run_test("adder_test");
    end

    // 复位
    initial begin
        rst_n = 0;
        #20 rst_n = 1;
    end

    // 把之前几个文件 include 进来(也可以分文件编译)
    `include "adder_transaction.sv"
    `include "adder_driver.sv"
    `include "adder_monitor.sv"
    `include "adder_test.sv"
endmodule

怎么在 Questa 里跑?

步骤 1:建工程

text

Copy

Download

复制代码
vlib work
vmap work work

步骤 2:编译(注意顺序:先接口,再 DUT,最后 top)

text

Copy

Download

复制代码
vlog adder_if.sv adder.sv tb_top.sv

步骤 3:仿真

text

Copy

Download

复制代码
vsim -c work.tb_top -do "run -all; quit"

或者用 GUI:

text

Copy

Download

复制代码
vsim work.tb_top
# 在 GUI 里 run -all

预期输出(你会看到)

text

Copy

Download

复制代码
UVM_INFO ... [MON] 采样: a=42  b=17  sum=59
UVM_INFO ... [MON] 采样: a=133 b=201 sum=334
UVM_INFO ... [MON] 采样: a=7   b=250 sum=257
...(共 5 条左右)
UVM_INFO ... TEST_DONE

如果看到 [MON] 采样 打印 → 恭喜你,第一个 UVM 平台跑通了! 🎉


现在轮到你了 ✋

请你按下面步骤做,然后告诉我结果:

  1. 建一个文件夹 uvm_adder

  2. 把 7 个文件复制进去(我可以再拆开发给你,或你按上面直接建)

  3. 执行 3 条命令(vlib / vlog / vsim)

  4. 把屏幕输出贴给我

可能遇到的坑(提前预警)

报错 原因 解决
uvm_pkg not found 没 import 检查 import uvm_pkg::*;
vif 没拿到 config_db set/get 名字不一致 检查 "vif" 字符串是否一样
run_test 找不到 类名写错 run_test("adder_test")
编译报 include 错 文件顺序 先 interface 再 module 再 top

一句话总结今天

不要试图先懂所有语法,先把平台跑通,看到现象,再回来理解每一行。 这是工程师学习 UVM 最快的方式。

相关推荐
cmc10286 小时前
301.modelsim自动仿真
fpga开发
Riwuarua17 小时前
从近似0基础开始FPGA开发 -- part.10 verilog-ethernet开源UDP协议栈工程学习
fpga开发·udp·开源
my_daling1 天前
紫光FPGA远程升级注意事项(关于GTP_CFGCLK原语使用问题)
笔记·学习·fpga开发
Riwuarua2 天前
从近似0基础开始FPGA开发 -- part.11 AXI总线协议与工程应用
fpga开发
自小吃多2 天前
高云 FPGA 添加时序约束笔记
笔记·fpga开发
学习FPGA的电气小兴兴2 天前
基于FPGA的CORDIC旋转模式实现sin和cos运算
fpga开发·信号处理·数字信号处理·verilog·fpga·verilog hdl·cordic
blue_ice .2 天前
DDS原理及简易实现
开发语言·经验分享·笔记·嵌入式硬件·fpga开发
明德扬2 天前
AD9653采集模块怎样连接FPGA底板?从ADC采样到数据处理
学习·fpga开发·fpga
上海芯聚电子3 天前
ADC与FPGA之间,如何减少板级设计复杂度?
fpga开发·adc