IC芯片验证 - 手把手教你搭建UVM验证环境

这是一个使用UVM组件库构建的模拟器验证环境,包括 sequencer, driver, monitor, agent 和 scoreboard。主要验证点是放大倍数设置、放大值计算、断言覆盖率和功能覆盖率。通过创建不同类型的序列(如设置增益、写入基数值),模拟器接收并处理这些请求,然后在scoreboard中与预期结果进行比较,确保正确性。此外,还包含了覆盖率组和断言,以评估和验证设计的行为。

这是一个UVM的demo项目:

做一个包含绝大部分组件的uvm(sequencer, driver, monitor, agent, scoreboard, model), 验证一个同向放大器的dut, 主要验证点是

(1)放大倍数设定是否正确(过一个时钟后采集结果比较, 在sequence里直接比较)

(2) 放大值是否计算正确(和调用c++的dll 计算的预期结果, 在scoreboard里比较) 

(3) 断言覆盖率

(4) 功能覆盖率

教学视频

IC验证 - 手把手教你搭建UVM芯片验证环境(含代码)_哔哩哔哩_bilibili

代码下载

IC验证-uvm验证demo代码-其它文档类资源-优快云下载

PPT下载

https://blog.youkuaiyun.com/howard789/article/details/123237280

目录结构

param_def.v


`define  NO_WIDTH 8
`define  BASE_NUMBER_WIDTH 8
`define  SCALER_WIDTH 16

`define  WR_DATA_WIDTH 16 

`define  RES_WIDTH 24
`define  RD_DATA_WIDTH 32

amplifier.v

`include "param_def.v"

module amplifier
(

clk_i,    
rstn_i,
wr_en_i,

set_scaler_i, 
wr_data_i,

rd_val_o,
rd_data_o,
scaler_o

);



input 						                    clk_i;    
input 						                    rstn_i; 

input						                    wr_en_i; //
input						                    set_scaler_i; //是否是修改scaler
input			    [`WR_DATA_WIDTH-1:0]        wr_data_i; //输入序号8位 输入数字8位 ,或 sacaler 16位

output			reg 			                rd_val_o; //data_o有效
output			reg [`RD_DATA_WIDTH-1:0]		rd_data_o; //输出资料包括 序号[31:24], 基本数字[23:16], 放大后的数字[15:0]其中第15位是正负号

output			    [`SCALER_WIDTH-1:0]		    scaler_o; //當前的scaler


//=============================================================================
//****************************     Main Code    *******************************
//=============================================================================


reg   [`SCALER_WIDTH-1:0]  scaler;  //max 65,535
assign scaler_o = scaler;

reg 		  flag;

reg   [`NO_WIDTH-1:0]  no_r;
reg   [`RES_WIDTH-1:0]  res_r;


always @ (posedge clk_i or negedge rstn_i) begin
	if(rstn_i == 1'b0) begin
		no_r <= 1'b0;
    	res_r <= 1'b0;
		scaler <= 1'b0;
		flag <= 1'b0;
	end	

	//bug start 1
	else if(wr_en_i && set_scaler_i && wr_data_i == 16'd5) begin	
		scaler <= 16'd55; 
		no_r <= 1'b0;
    	res_r <= 1'b0;
		flag <= 1'b0;
    end
	// bug end 1

	else if(wr_en_i && set_scaler_i) begin	
		scaler <= wr_data_i;
		no_r <= 1'b0;
    	res_r <= 1'b0;
		flag <= 1'b0;
    end

    //bug start 2
    else if(wr_en_i && !set_scaler_i && wr_data_i[ 7:0]== 8'd123) begin 
    	scaler <= scaler;
    	no_r   <= wr_data_i[15:8];
		res_r  <= wr_data_i[ 7:0] * 100;
    	flag <= 1'b1;
    end
    // bug end 2


    else if(wr_en_i && !set_scaler_i) begin 
    	scaler <= scaler;
    	no_r   <= wr_data_i[15:8];
		res_r  <= wr_data_i[ 7:0] * scaler;
    	flag <= 1'b1;
    end
    else begin 
    	scaler <= scaler;
    	no_r <= 1'b0;
    	res_r <= 1'b0;
    	flag <= 1'b0;
    end

end

always @ (posedge clk_i or negedge rstn_i) begin
	if(rstn_i == 1'b0) begin
		rd_val_o <= 1'b0;
		rd_data_o <= 1'b0;
	end	
	else if(flag) begin	
        rd_val_o <= 1'b1;
        rd_data_o <= {no_r,res_r};
    end
    else begin 
		rd_val_o <= 1'b0;
		rd_data_o <= 1'b0;
    end
end


endmodule

basic_tb.sv

`timescale 1ps/1ps
`include "../dut/param_def.v"

parameter  T    =  2; 

module ref_tb;
  bit         clk,rstn;

  initial begin
    fork
      begin 
        forever #(T/2) clk = !clk;
      end
      begin
        rstn <= 1'b0;
        #T;
        rstn <= 1'b1;
      end
    join_none
  end

  logic wr_en_i;
  logic set_scaler_i;


  logic [`WR_DATA_WIDTH-1:0] wr_data_i;


  logic rd_val_o;
  logic [`RD_DATA_WIDTH-1:0] rd_data_o;
  logic [`SCALER_WIDTH-1:0] scaler_o;



  initial begin
    wait(!rstn);

    #T;
    wr_en_i =1'b0;
    set_scaler_i =1'b0;
    wr_data_i = 16'd0;

    #(T*2);
    // 设定scaler 100
    wr_en_i =1'b1;
    set_scaler_i =1'b1;
    wr_data_i = 16'd100;
    
    $display("input scaler %d ",set_scaler_i);


    #T;
    //查看是否设定成功
    $display("rd_val_o %d ",rd_val_o);
    $display("rd_data_o %d ",rd_data_o);
    $display("scaler_o %d ",scaler_o);
    

    #T;
    //输入base_number
    wr_en_i =1'b1;
    set_scaler_i =1'b0;
    wr_data_i ={8'd5,8'd25};


    $display("input no %d ",wr_data_i[15:8]);
    $display("input base number %d ",wr_data_i[ 7:0]);

    #T;
    
    //查看结果
    $display("rd_val_o %d ",rd_val_o);
    $display("rd_data_o no %d ",rd_data_o[31:24]);
    $display("rd_data_o res %d ",rd_data_o[23:0]);
    $display("scaler_o %d ",scaler_o);

  #T;
    
    //查看结果
    $display("rd_val_o %d ",rd_val_o);
    $display("rd_data_o no %d ",rd_data_o[31:24]);
    $display("rd_data_o res %d ",rd_data_o[23:0]);
    $display("scaler_o %d ",scaler_o);

    
  end


amplifier amplifier_inst
(


.clk_i(clk),    
.rstn_i(rstn),
.wr_en_i(wr_en_i),
.set_scaler_i(set_scaler_i), 
.wr_data_i(wr_data_i),
.rd_val_o(rd_val_o),
.rd_data_o(rd_data_o),
.scaler_o(scaler_o)
);





endmodule

ue_tb.sv

`timescale 1ns/1ps
import uvm_pkg::*;
`include "uvm_macros.svh"
// `include "../uvm/ue_pkg.svh"


module ue_tb;

  parameter  T    =  2; 
  bit         clk,rstn;
  string s;
  int res;
  initial begin
    fork
      begin 
        forever #(T/2) clk = !clk;
      end
      begin
        rstn <= 1'b0;
        #T;
        rstn <= 1'b1;
      end
    join_none
  end

  // ue_interface intf(clk, rstn);
  ue_interface intf(.*);


  amplifier amplifier_inst
  (
  .clk_i(clk),    
  .rstn_i(rstn),
  .wr_en_i(intf.wr_en_i),
  .set_scaler_i(intf.set_scaler_i), 
  .wr_data_i(intf.wr_data_i),
  .rd_val_o(intf.rd_val_o),
  .rd_data_o(intf.rd_data_o),
  .scaler_o(intf.scaler_o)
  );



  initial begin
  	uvm_config_db#(virtual ue_interface)::set(uvm_root::get(), "uvm_test_top.env.i_agt", "vif", intf);
    uvm_config_db#(virtual ue_interface)::set(uvm_root::get(), "uvm_test_top.env.o_agt", "vif", intf);
  end

  initial begin
    // run_test("ue_case0_test");
    // run_test("ue_case1_test");
    // run_test("ue_case2_test");

    run_test();
  end

endmodule

ue_agent.sv

`ifndef UE_AGENT_SV
`define UE_AGENT_SV

//---------------------------------------
// svh
//---------------------------------------

class ue_agent extends uvm_agent;

  uvm_active_passive_enum is_active;
  
  ue_driver drv;
  ue_sequencer sqr;
  ue_monitor mon;

  ue_config cfg;
  virtual ue_interface vif;

  `uvm_component_utils(ue_agent)

  extern function new(string name = "ue_agent",uvm_component parent = null);
  extern function void build();
  extern function void connect();
  extern function void report();
endclass

//---------------------------------------
// sv
//---------------------------------------
function ue_agent::new(string name = "ue_agent" ,uvm_component parent = null);
  super.new(name, parent);
  `uvm_info(get_type_name(), $sformatf("created, is_active: %s" ,is_active), UVM_LOW)
endfunction : new

function void ue_agent::build();
  	super.build();
    

    if(!uvm_config_db#(ue_config)::get(this,"","cfg", cfg)) begin
     cfg = ue_config::type_id::create("cfg");
    end

    `uvm_info(get_type_name(), $sformatf("start to build, is_active: %s",is_active), UVM_LOW)



  	if(!uvm_config_db#(virtual ue_interface)::get(this,"","vif", vif)) begin
  	  `uvm_fatal("GETVIF","cannot get vif handle from config DB")
  	end
    vif.start_report=0;


  	mon = ue_monitor::type_id::create("mon",this);
  	mon.vif=vif;

  	if(is_active==UVM_ACTIVE) begin 
  		sqr = ue_sequencer::type_id::create("sqr",this);
  		drv = ue_driver::type_id::create("drv",this);  		
  		drv.vif = vif;
      mon.monitor_input=1'b1;
  	end
    else
      mon.monitor_input=1'b0;


    `uvm_info(get_type_name(), "built", UVM_LOW)
endfunction:build

function void ue_agent::connect();
	if(is_active==UVM_ACTIVE) begin 
		drv.seq_item_port.connect(sqr.seq_item_export);
	end
  `uvm_info(get_type_name(), "connected", UVM_LOW)
endfunction:connect



function void ue_agent::report();
  super.report();
  vif.start_report=1;
  
endfunction:report

`endif // UE_AGENT_SV

ue_base_test.sv

`ifndef UE_BASE_TEST_SV
`define UE_BASE_TEST_SV

//--------------------------------------------------------------------------------------------------------
// svh
//--------------------------------------------------------------------------------------------------------

class ue_base_test extends  uvm_test;

	ue_env env;

	`uvm_component_utils(ue_base_test)
	extern function new(string name="ue_base_test",uvm_component parent = null);
	extern function void build();
	extern function void report();
endclass 


//--------------------------------------------------------------------------------------------------------
// sv
//--------------------------------------------------------------------------------------------------------

function ue_base_test::new(string name ="ue_base_test",uvm_component parent = null);
  super.new(name, parent);
  `uvm_info(get_type_name(), $sformatf("created"), UVM_LOW)
endfunction : new

function void ue_base_test::build();
	super.build();

	env = ue_env::type_id::create("env",this);

	`uvm_info(get_type_name(), "built", UVM_LOW)
endfunction



function void ue_base_test::report();
	uvm_report_server server;
	int err_num;
评论 18
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值