直接上手 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 最快的方式。

相关推荐
szxinmai主板定制专家4 小时前
工业视觉新思路:FPGA图像预处理+RK NPU推理,实现高速缺陷检测
人工智能·嵌入式硬件·fpga开发·rk3576+codesys
FPGA小徐7 小时前
【硬件实战】立创 EDA 绘制 TF/SD 卡模块电路|SPI 模式原理图 + 阻抗匹配 + PCB 布局布线全教程
单片机·嵌入式硬件·fpga开发
狂奔蜗牛(bradley)8 小时前
EtherCAT DC 分布式时钟同步的 ModelSim 仿真:从帧模板到从站行为建模
人工智能·嵌入式硬件·fpga开发·架构
ALINX技术博客21 小时前
8 通道同步采样 ADC!AN706 波形显示实验 【FPGA黑金云课堂】
fpga开发
szxinmai主板定制专家1 天前
RK3568+FPGA+CODESYS异构控制方案:赋能半导体设备与工业自动化升级
人工智能·fpga开发·自动化·rk3576·半导体设备
辰丁电子1 天前
allegro入门教程-菜单栏Logic
fpga开发·pcb设计·pcb工艺·layout·pcb layout
FPGA小徐2 天前
数字IC/FPGA设计基础_ILA原理与使用
fpga开发
FPGA小徐2 天前
FPGA 部署 YOLO 完整指南:从模型量化到 Zynq PS+PL 硬件加速
yolo·fpga开发
dadaobusi2 天前
学习:chiesel,scala
学习·fpga开发·scala
爱吃汽的小橘2 天前
异步FIFO核心揭秘:格雷码与指针同步
fpga开发