在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 286|回复: 2

[原创] 一种用AI生成完整的UVM Testbench的方法

[复制链接]
发表于 5 天前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

x
虽然已经入夏,但一直阴雨绵绵,闲来无事,无意中发现一个用AI生成完整且可运行的UVM Testbench的方法。以一个FIFO为DUT,下面是AI生成对应的Testbench的一个总结,麻雀虽小,五脏俱全。关键点概述
  • [size=16.002px]DUT 接口

    • [size=16.002px]定义了 fifo_if 接口,包含 FIFO 的所有信号:时钟、复位、读写使能、数据输入输出、状态标志等
    • [size=16.002px]接口信号包括:full, empty, almost_full, almost_empty, overflow, underflow

  • [size=16.002px]事务项 (Transaction)
    • [size=16.002px]fifo_transaction 类继承自 uvm_sequence_item
    • [size=16.002px]包含随机化的数据、写使能和读使能
    • [size=16.002px]约束确保写和读操作不会同时进行 (wr_en != rd_en)

  • [size=16.002px]序列 (Sequence)
    • [size=16.002px]fifo_sequence 生成测试序列
    • [size=16.002px]先填充 FIFO (16 次写操作)
    • [size=16.002px]等待 100ns 让 FIFO 变满
    • [size=16.002px]然后清空 FIFO (16 次读操作)

  • [size=16.002px]驱动 (Driver)
    • [size=16.002px]从序列获取事务并驱动到 DUT 接口
    • [size=16.002px]在时钟上升沿应用信号

  • [size=16.002px]监视器 (Monitor)
    • [size=16.002px]观察 DUT 接口上的活动
    • [size=16.002px]检测有效的读写操作并发送到分析端口
    • [size=16.002px]忽略当 FIFO 满时的写操作和空时的读操作

  • [size=16.002px]记分板 (Scoreboard)
    • [size=16.002px]比较写入和读出的数据
    • [size=16.002px]统计匹配和不匹配的数量
    • [size=16.002px]检查写入和读取的事务数量是否一致
    • [size=16.002px]在报告阶段输出结果

  • [size=16.002px]代理 (Agent)
    • [size=16.002px]包含驱动、监视器和序列器
    • [size=16.002px]根据配置决定是否创建主动组件 (driver 和 sequencer)

  • [size=16.002px]环境 (Environment)
    • [size=16.002px]包含 agent 和 scoreboard
    • [size=16.002px]连接 monitor 的分析端口到 scoreboard

  • [size=16.002px]测试 (Test)
    • [size=16.002px]base_test 提供基本框架
    • [size=16.002px]basic_test 执行基本 FIFO 测试
    • [size=16.002px]使用 objection 机制控制测试流程

  • [size=16.002px]顶层模块 (Top Module)
    • [size=16.002px]实例化 DUT 和接口
    • [size=16.002px]生成时钟和复位信号
    • [size=16.002px]设置 UVM 配置数据库
    • [size=16.002px]运行测试并检查最终结果


测试流程
  • [size=16.002px]初始化阶段:时钟、复位生成
  • [size=16.002px]UVM 启动:配置虚拟接口并运行测试
  • [size=16.002px]测试执行:
    • [size=16.002px]先执行 16 次写操作填充 FIFO
    • [size=16.002px]等待 FIFO 变满
    • [size=16.002px]再执行 16 次读操作清空 FIFO

  • [size=16.002px]结果检查:
    • [size=16.002px]比较写入和读出的数据
    • [size=16.002px]检查事务数量匹配

  • [size=16.002px]报告结果:输出匹配/不匹配统计

验证功能
  • [size=16.002px]验证 FIFO 的基本读写功能
  • [size=16.002px]检查 FIFO 满和空状态行为
  • [size=16.002px]验证数据一致性 (写入和读出的数据是否匹配)
  • [size=16.002px]检查事务完整性 (写入和读取的数量是否一致)

可扩展性
  • [size=16.002px]可以通过继承 base_test 创建更多测试场景
  • [size=16.002px]可以添加覆盖率收集
  • [size=16.002px]可以扩展记分板功能以检查更多边界条件
  • [size=16.002px]可以添加断言检查 FIFO 的行为


[size=16.002px]

[size=16.002px]下面是生成的testbench的代码:(对天发誓,一个字母没改)
[size=16.002px]

`timescale 1ns/1ps
`include "uvm_macros.svh"

import uvm_pkg::*;

// Interface for FIFO DUT
interface fifo_if(input bit clk);
    logic rst_n;
    logic wr_en;
    logic [31:0] data_in;
    logic rd_en;
    logic [31:0] data_out;
    logic full;
    logic empty;
    logic almost_full;
    logic almost_empty;
    logic overflow;
    logic underflow;
endinterface

// FIFO Transaction Item
class fifo_transaction extends uvm_sequence_item;
    rand bit [31:0] data;
    rand bit wr_en;
    rand bit rd_en;

    `uvm_object_utils_begin(fifo_transaction)
        `uvm_field_int(data, UVM_ALL_ON)
        `uvm_field_int(wr_en, 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

    constraint valid_ops {
        wr_en != rd_en;
    }
endclass

// FIFO Sequence
class fifo_sequence extends uvm_sequence #(fifo_transaction);
    `uvm_object_utils(fifo_sequence)

    rand int num_transactions = 20;
    fifo_transaction transactions[$];

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

    task body();
        // First fill the FIFO
        for (int i = 0; i < 16; i++) begin
            fifo_transaction tx;
            `uvm_create(tx)
            tx.wr_en = 1;
            tx.rd_en = 0;
            assert(tx.randomize());
            transactions.push_back(tx);
            `uvm_send(tx)
        end

        // Wait for FIFO to be full
        #100;

        // Then empty the FIFO
        for (int i = 0; i < 16; i++) begin
            fifo_transaction tx;
            `uvm_create(tx)
            tx.wr_en = 0;
            tx.rd_en = 1;
            tx.data = 0; // Data doesn't matter for reads
            transactions.push_back(tx);
            `uvm_send(tx)
        end
    endtask
endclass

// FIFO Driver
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)) begin
            `uvm_fatal("NOVIF", "Virtual interface not set for driver")
        end
    endfunction

    task run_phase(uvm_phase phase);
        forever begin
            fifo_transaction tx;
            seq_item_port.get_next_item(tx);

            @(posedge vif.clk);
            vif.wr_en <= tx.wr_en;
            vif.rd_en <= tx.rd_en;
            vif.data_in <= tx.data;

            seq_item_port.item_done();
        end
    endtask
endclass

// FIFO Monitor
class fifo_monitor extends uvm_monitor;
    `uvm_component_utils(fifo_monitor)

    virtual fifo_if vif;
    uvm_analysis_port #(fifo_transaction) ap;

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

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

    task run_phase(uvm_phase phase);
        forever begin
            fifo_transaction tx = fifo_transaction::type_id::create("tx");

            @(posedge vif.clk);
            if (vif.wr_en && !vif.full) begin
                tx.wr_en = 1;
                tx.rd_en = 0;
                tx.data = vif.data_in;
                ap.write(tx);
            end
            if (vif.rd_en && !vif.empty) begin
                tx = fifo_transaction::type_id::create("tx");
                tx.wr_en = 0;
                tx.rd_en = 1;
                tx.data = vif.data_out;
                ap.write(tx);
            end
        end
    endtask
endclass

// FIFO Scoreboard
class fifo_scoreboard extends uvm_scoreboard;
    `uvm_component_utils(fifo_scoreboard)

    uvm_analysis_imp #(fifo_transaction, fifo_scoreboard) item_export;

    fifo_transaction write_q[$];
    fifo_transaction read_q[$];

    int write_count = 0;
    int read_count = 0;
    int match_count = 0;
    int mismatch_count = 0;

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

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        item_export = new("item_export", this);
    endfunction

    function void write(fifo_transaction tx);
        if (tx.wr_en) begin
            write_q.push_back(tx);
            write_count++;
        end
        else if (tx.rd_en) begin
            read_q.push_back(tx);
            read_count++;
        end
    endfunction

    function void report_phase(uvm_phase phase);
        super.report_phase(phase);

        // Compare write and read transactions
        while (write_q.size() > 0 && read_q.size() > 0) begin
            fifo_transaction write_tx = write_q.pop_front();
            fifo_transaction read_tx = read_q.pop_front();

            if (write_tx.data !== read_tx.data) begin
                `uvm_error("SCOREBOARD", $sformatf("Data mismatch! Expected: 0x%0h, Actual: 0x%0h",
                          write_tx.data, read_tx.data))
                mismatch_count++;
            end
            else begin
                match_count++;
            end
        end

        // Check for transaction count mismatch
        if (write_count != read_count) begin
            `uvm_error("SCOREBOARD", $sformatf("Transaction count mismatch! Writes: %0d, Reads: %0d",
                      write_count, read_count))
        end

        `uvm_info("SCOREBOARD", $sformatf("Scoreboard Results: Matches=%0d, Mismatches=%0d, Writes=%0d, Reads=%0d",
                  match_count, mismatch_count, write_count, read_count), UVM_LOW)
    endfunction
endclass

// FIFO Agent
class fifo_agent extends uvm_agent;
    `uvm_component_utils(fifo_agent)

    fifo_driver driver;
    fifo_monitor monitor;
    uvm_sequencer #(fifo_transaction) sequencer;

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

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);

        monitor = fifo_monitor::type_id::create("monitor", this);

        if (get_is_active() == UVM_ACTIVE) begin
            driver = fifo_driver::type_id::create("driver", this);
            sequencer = uvm_sequencer#(fifo_transaction)::type_id::create("sequencer", this);
        end
    endfunction

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);

        if (get_is_active() == UVM_ACTIVE) begin
            driver.seq_item_port.connect(sequencer.seq_item_export);
        end

        if (!uvm_config_db#(virtual fifo_if)::get(this, "", "vif", driver.vif)) begin
            `uvm_fatal("NOVIF", "Virtual interface not set for driver")
        end
        if (!uvm_config_db#(virtual fifo_if)::get(this, "", "vif", monitor.vif)) begin
            `uvm_fatal("NOVIF", "Virtual interface not set for monitor")
        end
    endfunction
endclass

// FIFO Environment
class fifo_env extends uvm_env;
    `uvm_component_utils(fifo_env)

    fifo_agent agent;
    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);

        agent = fifo_agent::type_id::create("agent", this);
        scoreboard = fifo_scoreboard::type_id::create("scoreboard", this);

        uvm_config_db#(uvm_active_passive_enum)::set(this, "agent", "is_active", UVM_ACTIVE);
    endfunction

    function void connect_phase(uvm_phase phase);
        super.connect_phase(phase);

        agent.monitor.ap.connect(scoreboard.item_export);
    endfunction
endclass

// Base Test
class base_test extends uvm_test;
    `uvm_component_utils(base_test)

    fifo_env env;
    fifo_sequence seq;

    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);
        seq = fifo_sequence::type_id::create("seq");
    endfunction

    task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        seq.start(env.agent.sequencer);
        phase.drop_objection(this);
    endtask
endclass

// Basic Test
class basic_test extends base_test;
    `uvm_component_utils(basic_test)

    function new(string name = "basic_test", uvm_component parent = null);
        super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
    endfunction

    task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        `uvm_info(get_type_name(), "Starting basic FIFO test", UVM_LOW)

        seq.start(env.agent.sequencer);

        // Wait for all transactions to complete
        #200;

        phase.drop_objection(this);
    endtask
endclass

// Top Module
module top;
    import uvm_pkg::*;

    bit clk;
    bit rst_n;

    // Instantiate DUT interface
    fifo_if fif_if(clk);

    // Instantiate DUT
    FIFO #(
        .DATA_WIDTH(32),
        .FIFO_DEPTH(16),
        .ALMOST_FULL_THRESH(12),
        .ALMOST_EMPTY_THRESH(4)
    ) dut (
        .clk(clk),
        .rst_n(fif_if.rst_n),
        .wr_en(fif_if.wr_en),
        .data_in(fif_if.data_in),
        .rd_en(fif_if.rd_en),
        .data_out(fif_if.data_out),
        .full(fif_if.full),
        .empty(fif_if.empty),
        .almost_full(fif_if.almost_full),
        .almost_empty(fif_if.almost_empty),
        .overflow(fif_if.overflow),
        .underflow(fif_if.underflow)
    );

    // Clock generation
    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end

    // Reset generation
    initial begin
        fif_if.rst_n = 0;
        #20 fif_if.rst_n = 1;
    end

    // UVM test setup
    initial begin
        uvm_config_db#(virtual fifo_if)::set(null, "uvm_test_top.env*", "vif", fif_if);
        run_test("basic_test");
    end

    // Final check
    final begin
        if (uvm_report_server::get_server().get_severity_count(UVM_FATAL) > 0) begin
            $display("Simulation finished with UVM_FATAL errors");
            $finish(2);
        end
        else if (uvm_report_server::get_server().get_severity_count(UVM_ERROR) > 0) begin
            $display("Simulation finished with UVM_ERROR errors");
            $finish(1);
        end
        else begin
            $display("Test passed successfully");
            $finish(0);
        end
    end
endmodule

完整代码
完整版DUT,Testbench和Testcase 代码打包文件: FIFO_release.zip (8.1 KB, 下载次数: 3 )



实现方式
1. AIDEV Project Workspace
2. 输入dut fifo的功能需求,选择生成和编译选项,填入额外的test需求,提交,坐等代码生成,发入你邮箱。

                               
登录/注册后可看大图


发表于 3 天前 | 显示全部楼层
顶一顶
发表于 昨天 17:10 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

站长推荐 上一条 /1 下一条

X

小黑屋| 手机版| 关于我们| 联系我们| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2025-6-25 15:08 , Processed in 0.024280 second(s), 9 queries , Gzip On, MemCached On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表