前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SystemVerilog的一个简单验证demo

SystemVerilog的一个简单验证demo

作者头像
数字IC小站
发布2020-07-01 10:43:35
1.9K0
发布2020-07-01 10:43:35
举报
文章被收录于专栏:数字IC小站数字IC小站

文后阅读原文附本文所有代码。

DUT:

是一个简单的memory。就六个信号,时钟信号clk,复位信号reset(高有效),读使能信号rd_en,写使能信号wr_en,写数据信号wdata,读数据信号rdata。

对于写操作:

address, wr_en和wdata 在同一时钟进行驱动。

对于读操作:

address和rd_en在同一时钟进行驱动,系统在下一时钟出现反应。

代码语言:javascript
复制
  //Memory
  reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH];
  //Reset 
  always @(posedge reset) 
    for(int i=0;i<2**ADDR_WIDTH;i++) mem[i]=8'hFF;
   
  // Write data to Memory
  always @(posedge clk) 
    if (wr_en)    mem[addr] <= wdata; 
  // Read data from memory
  always @(posedge clk)
    if (rd_en) rdata <= mem[addr];

Testbench:

对于验证来说,主要就是以下几个类组成:transaction,generator,driver,monitor,scoreboard,environment,top。

对于transaction:

主要是对生成激励中所需的字段进行声明,还可以用作DUT信号上监视器监视的活动的占位符。

  • 编写transaction的步骤:
  • 首先对域进行声明:
代码语言:javascript
复制
class transaction;

  //declaring the transaction items
  bit [1:0] addr;
  bit       wr_en;
  bit       rd_en;
  bit [7:0] wdata;
  bit [7:0] rdata;
  bit [1:0] cnt;
   
endclass
  • 对变量使用rand进行随机化
代码语言:javascript
复制
class transaction;
 
  //declaring the transaction items
  rand bit [1:0] addr;
  rand bit       wr_en;
  rand bit       rd_en;
  rand bit [7:0] wdata;
       bit [7:0] rdata;
       bit [1:0] cnt;
   
endclass
  • 对部分变量进行约束
代码语言:javascript
复制
 //constaint, to generate any one among write and read
  constraint wr_rd_c { wr_en != rd_en; };
  • generator类的编写

主要是对transaction随机化产生激励,并且把产生的类发送给driver。

  • 声明事务类句柄
代码语言:javascript
复制
class generator;  
  //declaring transaction class
  rand transaction trans;  
endclass
  • 随机化事务类
代码语言:javascript
复制
class generator;
   
  //declaring transaction class
  rand transaction trans; 
  //main task, generates(create and randomizes) the packets and puts into mailbox
  task main();
      trans = new();
      if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
      gen2driv.put(trans);
  endtask
   
endclass
  • 利用信箱把随机化后的事务类发送给driver
代码语言:javascript
复制
class generator;
   
  //declaring transaction class
  rand transaction trans;
   
  //declaring mailbox
  mailbox gen2driv; 
 
  //constructor
  function new(mailbox gen2driv);
    //getting the mailbox handle from env
    this.gen2driv = gen2driv;
  endfunction
   
  //main task, generates(create and randomizes) the packets and puts into mailbox
  task main();
      trans = new();
      if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
      gen2driv.put(trans);
  endtask
   
endclass
  • 增加变量去控制事务产生的数量
代码语言:javascript
复制
class generator;
   
  //declaring transaction class
  rand transaction trans;
   
  //declaring mailbox
  mailbox gen2driv;
   
  //repeat count, to specify number of items to generate
  int  repeat_count; 
 
  //constructor
  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
  endtask
   
endclass
  • 添加一个事件来指示生成过程的完成,该事件将在生成过程完成时触发。
代码语言:javascript
复制
class generator;
   
  //declaring transaction class
  rand transaction trans;
   
  //declaring mailbox
  mailbox gen2driv;
   
  //repeat count, to specify number of items to generate
  int  repeat_count; 
 
  //event
  event ended;
 
  //constructor
  function new(mailbox gen2driv,event ended);
    //getting the mailbox handle from env
    this.gen2driv = gen2driv;
    this.ended    = ended;
  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
  • interface的编写

接口将对信号进行分组,指定方向(Modport)并同步信号(时钟块)。

  • driver时钟块
代码语言:javascript
复制
//driver clocking block
clocking driver_cb @(posedge clk);
  default input #1 output #1;
  output addr;
  output wr_en;
  output rd_en;
  output wdata;
  input  rdata; 
endclocking
  • monitor时钟块
代码语言:javascript
复制
//monitor clocking block
clocking monitor_cb @(posedge clk);
  default input #1 output #1;
  input addr;
  input wr_en;
  input rd_en;
  input wdata;
  input rdata; 
endclocking
  • 指定方向
代码语言:javascript
复制
//driver modport
modport DRIVER  (clocking driver_cb,input clk,reset);
 
//monitor modport 
modport MONITOR (clocking monitor_cb,input clk,reset);
  • Driver类的编写

该类通过将事务类值分配给接口信号,接收从生成器生成的激励并驱动到DUT。

  • 声明接口和信箱,通过构造函数获取接口和信箱句柄
代码语言:javascript
复制
//creating virtual interface handle
  virtual mem_intf mem_vif;

  //creating mailbox handle
  mailbox gen2driv;

  //constructor
  function new(virtual mem_intf mem_vif,mailbox gen2driv);
    //getting the interface
    this.mem_vif = mem_vif;
    //getting the mailbox handle from  environment 
    this.gen2driv = gen2driv;
  endfunction
  • 添加复位任务,把接口信号初始化
代码语言:javascript
复制
//Reset task, Reset the Interface signals to default/initial values
  task reset;
    wait(mem_vif.reset);
    $display("--------- [DRIVER] Reset Started ---------");
    `DRIV_IF.wr_en <= 0;
    `DRIV_IF.rd_en <= 0;
    `DRIV_IF.addr  <= 0;
    `DRIV_IF.wdata <= 0;        
    wait(!mem_vif.reset);
    $display("--------- [DRIVER] Reset Ended ---------");
  endtask
  • 添加驱动任务,把generator产生的事务驱动给DUT
代码语言:javascript
复制
 //drive the transaction items to interface signals
  task drive;
    forever begin
      transaction trans;
      `DRIV_IF.wr_en <= 0;
      `DRIV_IF.rd_en <= 0;
      gen2driv.get(trans);
      $display("--------- [DRIVER-TRANSFER: %0d] ---------",no_transactions);
      @(posedge mem_vif.DRIVER.clk);
        `DRIV_IF.addr <= trans.addr;
      if(trans.wr_en) begin
        `DRIV_IF.wr_en <= trans.wr_en;
        `DRIV_IF.wdata <= trans.wdata;
        $display("\tADDR = %0h \tWDATA = %0h",trans.addr,trans.wdata);
        @(posedge mem_vif.DRIVER.clk);
      end
      if(trans.rd_en) begin
        `DRIV_IF.rd_en <= trans.rd_en;
        @(posedge mem_vif.DRIVER.clk);
        `DRIV_IF.rd_en <= 0;
        @(posedge mem_vif.DRIVER.clk);
        trans.rdata = `DRIV_IF.rdata;
        $display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
      end
      $display("-----------------------------------------");
      no_transactions++;
    end
  endtask
  • 添加本地变量,记录传输的次数
代码语言:javascript
复制
 //used to count the number of transactions
  int no_transactions;

  //drive the transaction items to interface signals
  task drive;
    ------
    ------
    no_transactions++;
  endtask
  • Monitor类的编写
  • 声明接口和信箱,通过构造函数获取接口和信箱句柄。
代码语言:javascript
复制
//creating virtual interface handle
virtual mem_intf mem_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
  • 采样逻辑并将采样的事务发送到记分板
代码语言:javascript
复制

task main;
  forever begin
    transaction trans;
    trans = new();
 
    @(posedge mem_vif.MONITOR.clk);
    wait(`MON_IF.rd_en || `MON_IF.wr_en);
      trans.addr  = `MON_IF.addr;
      trans.wr_en = `MON_IF.wr_en;
      trans.wdata = `MON_IF.wdata;
      if(`MON_IF.rd_en) begin
        trans.rd_en = `MON_IF.rd_en;
        @(posedge mem_vif.MONITOR.clk);
        @(posedge mem_vif.MONITOR.clk);
        trans.rdata = `MON_IF.rdata;
      end     
      mon2scb.put(trans);
  end
endtask
  • Scoreboard类的编写
  • 声明信箱和变量以保持事务计数,通过构造函数连接句柄
代码语言:javascript
复制
//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
  • 存储wdata并将rdata与存储的数据进行比较的逻辑
代码语言:javascript
复制
//stores wdata and compare rdata with stored data
task main;
  transaction trans;
  forever begin
    #50;
    mon2scb.get(trans);
    if(trans.rd_en) begin
      if(mem[trans.addr] != trans.rdata)
        $error("[SCB-FAIL] Addr = %0h,\n \t   Data :: Expected = %0h Actual = %0h",trans.addr,mem[trans.addr],trans.rdata);
      else
        $display("[SCB-PASS] Addr = %0h,\n \t   Data :: Expected = %0h Actual = %0h",trans.addr,mem[trans.addr],trans.rdata);
    end
    else if(trans.wr_en)
      mem[trans.addr] = trans.wdata;
 
    no_transactions++;
  end
endtask
  • Environment类的编写
  • 声明句柄
代码语言:javascript
复制
//generator and driver instance
generator gen;
driver    driv;
monitor    mon;   
scoreboard scb; 
//mailbox handle's
mailbox gen2driv;
mailbox mon2scb; 
//event for synchronization between generator and test
event gen_ended;
 
//virtual interface
virtual mem_intf mem_vif;
  • 在构造函数中创造信箱,driver,generator等
代码语言:javascript
复制

//constructor
function new(virtual mem_intf mem_vif);
  //get the interface from test
  this.mem_vif = mem_vif;
   
  //creating the mailbox (Same handle will be shared across generator and driver)
  gen2driv = new();
  mon2scb  = new();
   
  //creating generator and driver
  gen  = new(gen2driv,gen_ended);
  driv = new(mem_vif,gen2driv);
  mon  = new(mem_vif,mon2scb);
  scb  = new(mon2scb);
endfunction
  • 为了更好的访问,使用三级任务
代码语言:javascript
复制

task pre_test();
  driv.reset();
endtask
 
task test();
  fork
    gen.main();
    driv.main();
    mon.main();   //---NEW CODE---
    scb.main();   //---NEW CODE---
  join_any
endtask
 
task post_test();
  wait(gen.ended.triggered);
  wait(gen.repeat_count == driv.no_transactions);
  wait(gen.repeat_count == scb.no_transactions);   //---NEW CODE---
endtask
  • 添加一个运行任务以调用上述方法,在post_test()之后调用$ finish以结束模拟。
代码语言:javascript
复制
task run;
  pre_test();
  test();
  post_test();
  $finish;
endtask
  • 测试类的编写

测试负责,创造环境,配置测试平台,即设置要生成的事务的类型和数量,启动仿真。

  • 声明并创建环境
代码语言:javascript
复制
//declaring environment instance
environment env;
 
initial begin
  //creating environment
  env = new(intf);
end
  • 配置要生成的事务数
代码语言:javascript
复制
//setting the repeat count of generator as 10, means to generate 10 packets
env.gen.repeat_count = 10;
  • 启动仿真
代码语言:javascript
复制
//calling run of env, it interns calls generator and driver main tasks.
env.run();
  • 顶层测试的编写

这是最顶层的文件,用于连接DUT和TestBench。由DUT,Test和Interface实例组成。连接DUT和TestBench。

  • 声明并生成时钟并设置
代码语言:javascript
复制
//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
  • 创建接口实例
代码语言:javascript
复制
//creatinng instance of interface, inorder to connect DUT and testcase
mem_intf intf(clk,reset);
  • 创建设计实例并连接接口信号
代码语言:javascript
复制
//DUT instance, interface signals are connected to the DUT ports
memory DUT (
  .clk(intf.clk),
  .reset(intf.reset),
  .addr(intf.addr),
  .wr_en(intf.wr_en),
  .rd_en(intf.rd_en),
  .wdata(intf.wdata),
  .rdata(intf.rdata)
 );
  • 创建一个测试实例并通过接口句柄
代码语言:javascript
复制
//Testcase instance, interface handle is passed to test as an argument
test t1(intf);

End

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数字IC小站 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档