SV的testbench例子-加法器

    xiaoxiao2025-01-25  46

    下图是一个加法器的框图:

    输入管脚包括a、b、valid和clk、reset,以及一个输出管脚c。

    TestBench Without Monitor, Agent and Scoreboard 

    不带monitor、agent和scoreboard的结构如下:

    (1)定义transaction 

    transaction的作用主要是定义随机化的输入输出管脚,并输出显示.

    class transaction; //declaring the transaction items rand bit [3:0] a; rand bit [3:0] b; bit [6:0] c; function void display(string name); $display("-------------------------"); $display("- %s ",name); $display("-------------------------"); $display("- a = , b = ",a,b); $display("- c = ",c); $display("-------------------------"); endfunction endclass

    (2)定义generator

    generator的作用是实例化transaction,并且随机化trasaction中的随机变量,将随机化后的数据发送到driver.要实现与driver的通信,这就需要用到mailbox.在发送数据完成后,需要触发事件表明发送数据的完成,这需要定义事件.

    注意,这里的mailbox是generator和driver之间共有的!

    class generator; //declaring transaction class rand transaction trans; //定义信箱 mailbox gen2driv; //定义transaction的实例化次数,每实例化一次就发送一组随机数 int repeat_count; //在实例化次数完成后需要触发事件表示发送数据结束 event ended; //构造方法 function new(mailbox gen2driv);// //getting the mailbox handle from env this.gen2driv = gen2driv; endfunction //main task, generates(create and randomizes) the repeat_count number of transaction packets and puts into mailbox task main(); repeat(repeat_count) begin trans = new(); if( !trans.randomize() ) $fatal("Gen:: trans randomization failed"); gen2driv.put(trans); end -> ended; //实例化次数完成后触发事件 endtask endclass

    (3)定义interface

    实现testbench与DUT之间的通信. 

    interface intf(input logic clk,reset);//这个接口中并没有声明采样时序 //declaring the signals logic valid; logic [3:0] a; logic [3:0] b; logic [6:0] c; endinterface

    (4)定义driver

    driver的作用是接收来自generator的随机数据,并将随机数据发送到DUT.

    class driver; //记录transaction实例化的数目 int no_transactions; //创建虚接口句柄 virtual intf vif;//只有虚接口才可以在类中实例化 //创建信箱句柄 mailbox gen2driv; //构造方法 function new(virtual intf vif,mailbox gen2driv); //getting the interface this.vif = vif; //getting the mailbox handles from environment this.gen2driv = gen2driv; endfunction //复位任务 task reset; wait(vif.reset); $display("[ DRIVER ] ----- Reset Started -----"); vif.a <= 0; vif.b <= 0; vif.valid <= 0; wait(!vif.reset); $display("[ DRIVER ] ----- Reset Ended -----"); endtask //主任务,将从信箱获得的数据发送到接口,同时将接口中获得的DUT数据存入到信箱 task main; forever begin transaction trans; gen2driv.get(trans); @(posedge vif.clk);//顺序执行,第一个时钟上升沿 vif.valid <= 1; vif.a <= trans.a; vif.b <= trans.b; @(posedge vif.clk);//第二个时钟上升沿 vif.valid <= 0;//将接口中的vif.c赋给tran.c,vif.c来自DUT trans.c <= vif.c; @(posedge vif.clk); trans.display("[ Driver ]"); no_transactions++; end endtask endclass

     (5)定义environment

    environment包括mailbox、generator、diver,它通过实例化信箱实现driver和gennerator之间的通信.

    `include "transaction.sv" `include "generator.sv" `include "driver.sv"//将文件导入 class environment; //generator and driver instance generator gen; driver driv; //信箱句柄 mailbox gen2driv; //虚接口 virtual intf vif; //构造方法 function new(virtual intf vif); //get the interface from test this.vif = vif; //creating the mailbox (Same handle will be shared across generator and driver) gen2driv = new(); //实例化generator和driver的对象 gen = new(gen2driv); driv = new(vif,gen2driv); endfunction // task pre_test();//测试前任务,用于复位 driv.reset(); endtask task test();//测试任务 fork gen.main(); driv.main(); join_any endtask task post_test();//测试后任务 wait(gen.ended.triggered);//等待generator的事件触发 wait(gen.repeat_count == driv.no_transactions);//transaction随机化次数 endtask //主任务 task run; pre_test(); test(); post_test(); $finish; endtask endclass

    (6)定义test

    test模块是使用program块编写的,这是与前面的不同.其作用主要是实例化environment、初始化激励、配置testbench如产生多少transaction的对象数量.

    `include "environment.sv"//导入environment program test(intf intf); //declaring environment instance environment env; initial begin //creating environment env = new(intf); //setting the repeat count of generator as 10, means to generate 10 packets env.gen.repeat_count = 10; //calling run of env, it interns calls generator and driver main tasks. env.run(); end endprogram

    (7)testbench的顶层模块

    主要包括interface、DUT和test.用prgram块编写test可以实现test与DUT之间的无竞争交互!

    `include "interface.sv" `include "random_test.sv"//导入文件 module tbench_top; //clock and reset signal declaration bit clk; bit reset; //clock generation always #5 clk = ~clk; //reset Generation initial begin reset = 1; #5 reset =0; end //实例化接口 intf i_intf(clk,reset); //实例化test,接口句柄作为参数 test t1(i_intf); //DUT instance, interface signals are connected to the DUT ports adder DUT ( .clk(i_intf.clk), .reset(i_intf.reset), .a(i_intf.a), .b(i_intf.b), .valid(i_intf.valid), .c(i_intf.c) ); //enabling the wave dump initial begin $dumpfile("dump.vcd"); $dumpvars; end endmodule

    DUT模块:

    /* -------------- valid ---->| | | | a -/--->| | | adder |---/-> c b -/--->| | | | -------------- ^ ^ | | clk reset */ module adder( input clk , input reset, input [3:0] a , input [3:0] b , input valid, output [6:0] c ); reg [6:0] tmp_c; //Reset always @(posedge reset) tmp_c <= 0; // Waddition operation always @(posedge clk) if (valid) tmp_c <= a + b; assign c = tmp_c; endmodule

    运行结果如下:

    带monitor、agent和scoreboard的结构如下:

    (1)定义monitor

    monitor的作用是对来自接口的信号进行采样,并将信号级的活动转换为事务级,将它们发送到scoreboard.

    class monitor; //creating virtual interface handle virtual intf vif; //creating mailbox handle mailbox mon2scb; //constructor function new(virtual intf vif,mailbox mon2scb); //getting the interface this.vif = vif; //getting the mailbox handles from environment this.mon2scb = mon2scb; endfunction //Samples the interface signal and send the sample packet to scoreboard task main; forever begin transaction trans; trans = new(); @(posedge vif.clk); wait(vif.valid); trans.a = vif.a;//因为输出相对于输入延时为1个周期 trans.b = vif.b; @(posedge vif.clk); trans.c = vif.c; @(posedge vif.clk); mon2scb.put(trans);//将总线上的数据存入信箱 trans.display("[ Monitor ]"); end endtask endclass

    (2)定义scoreboard

    scoreboard主要是接收来自monitor存入信箱的数据包,并将它们与期望值相比较,如果不匹配则会报错.

    由于这个例子中DUT的行为比较简单,所以添加了一行代码以生成预期结果,对于复杂的设计可以采用参考模型的方法来生成预期结果.

    class scoreboard; //creating mailbox handle mailbox mon2scb; //used to count the number of transactions int no_transactions; //constructor function new(mailbox mon2scb); //getting the mailbox handles from environment this.mon2scb = mon2scb; endfunction //Compares the Actual result with the expected result task main; transaction trans; forever begin mon2scb.get(trans); if((trans.a+trans.b) == trans.c)//左边为预期结果,右边为monitor传送过来的结果 $display("Result is as Expected"); else $error("Wrong Result.\n\tExpeced: Actual: ",(trans.a+trans.b),trans.c); no_transactions++; trans.display("[ Scoreboard ]"); end endtask endclass

    (3)定义environment

    `include "transaction.sv" `include "generator.sv" `include "driver.sv" `include "monitor.sv" `include "scoreboard.sv" class environment; //generator and driver instance generator gen; driver driv; monitor mon; scoreboard scb; //mailbox handle's 两个信箱 mailbox gen2driv; mailbox mon2scb; //virtual interface virtual intf vif; //constructor function new(virtual intf vif); //get the interface from test this.vif = vif; //creating the mailbox (Same handle will be shared across generator and driver) gen2driv = new(); mon2scb = new(); //creating generator and driver gen = new(gen2driv); driv = new(vif,gen2driv); mon = new(vif,mon2scb); scb = new(mon2scb); endfunction // task pre_test(); driv.reset(); endtask task test(); fork gen.main(); driv.main(); mon.main(); scb.main(); join_any endtask task post_test(); wait(gen.ended.triggered); wait(gen.repeat_count == driv.no_transactions); //Optional wait(gen.repeat_count == scb.no_transactions); endtask //run task task run; pre_test(); test(); post_test(); $finish; endtask endclass

    其余组件不变,运行结果如下:

    最新回复(0)