(数字ic验证)从零开始的apb_watchdog验证模块搭建(三、计时器触发与中断,复位触发,上锁测试)

apb_watchdog验证模块搭建(三、计时器触发与中断,复位触发,上锁测试)文章目录



前言

提示:这里可以添加本文要记录的大概内容:

书承上文,我们继续完成apb_watchdog的验证工作。前文中已经将寄存器模型加入模块中。在接下来的测试中我们能够更加体会到寄存器模型在验证中的作用。本章我们将完成一些watchdog功能点的实现(不包含check)。

1.功能简单介绍

具体功能可以参考第一章提到的设计文档。

Watchdog Load Register加载寄存器
WDOGLOAD寄存器为加载寄存器,可以读写,向其中写入32位二进制值之后watchdog会将此值加载为计数值(从此值开始减小),在WDOGCTROL.INTEN为高时(正常计数模式),访问此寄存器会使得watchdog重新载入并计数。

Watchdog Value Register计数值寄存器
WDOGVALUE寄存器为只读寄存器,会给出当前的计数值。

Watchdog Control Register控制寄存器
低两位分别为INTEN与RESEN,控制中断信号WDOGINT与复位信号WDOGRES的开关。在INTEN被赋值为1之后计数会立即开始。

Watchdog Clear Interrupt Register中断清除寄存器
WDOGCLR,只写寄存器,任何值的写入都将清楚当前中断信号,并从load寄存器的值中读取并开始重新计数。

Watchdog Raw Interrupt Status Register初始中断状态寄存器
WDOGRIS,只读寄存器,反映watchdog是否出现中断。即使INTEN为低,只要在之前的计数中完成计数即会拉高信号。和INTEN做“与”之后为WDOGMIS。

Watchdog Interrupt Status Register中断状态寄存器
WDOGMIS,只读寄存器,反映中断情况。

Watchdog Lock Register上锁寄存器
WDOGLOCK, 读写,复位后为为上锁状态,最低位1为上锁状态。写入’h1acce551会解锁,写入其他值会上锁。


一、element sequence创建

为了测试一个功能可能需要发送一系列的激励,将这些激励打包为element sequence能够便于在其他测试点复用我们的激励。就好像之前的apb write read一样,element sequence能够方便后面的激励测试。

1.base element sequence

base element sequence几乎是base virtual sequence的翻版,但是为什么不直接使其继承与base virtual seq中呢?因为需要在各个virtual sequence中调用element seq,因此要在base virtual seq中完成对element seq的声明,故将其单独拿出是合适的。base element seq代码如下:

base element seq:

`ifndef RKV_WATCHDOG_BASE_ELEMENT_SEQUENCE_SV
`define RKV_WATCHDOG_BASE_ELEMENT_SEQUENCE_SV

class rkv_watchdog_base_element_sequence extends uvm_sequence;
  `uvm_object_utils(rkv_watchdog_base_element_sequence)
  `uvm_declare_p_sequencer(rkv_watchdog_virtual_sequencer)
  rkv_watchdog_config cfg;
  rkv_watchdog_rgm rgm;  
  int rd_val,wt_val;
  uvm_status_e status = UVM_IS_OK;
  virtual rkv_watchdog_if vif;
  function new (string name = "rkv_watchdog_base_element_sequence");
    super.new(name);
    //cfg = rkv_watchdog_config::type_id::create("cfg");
  endfunction

  virtual task body();
    // get cfg from p_sequencer
    cfg = p_sequencer.cfg;
    // get rgm from p_sequencer
    rgm = cfg.rgm;
    vif = cfg.vif;
    `uvm_info("body", "Entered...", UVM_LOW)
    // TODO in sub-class
    `uvm_info("body", "Exiting...", UVM_LOW)
  endtask


virtual function bit diff_value(int val1, int val2, string id = "value_compare");
      cfg.seq_check_count++;
      if(val1 != val2) begin
        cfg.seq_error_count++;
        `uvm_error("[CMPERR]", $sformatf("ERROR! %s val1 %8x != val2 %8x", id, val1, val2)) 
        return 0;
      end
      else begin
        `uvm_info("[CMPSUC]", $sformatf("SUCCESS! %s val1 %8x == val2 %8x", id, val1, val2), UVM_LOW)
        return 1;
      end
    endfunction
endclass

`endif  

2.sequence中断复位使能、关闭序列

复位使能序列:

resen seq:使得RESEN值为1

`ifndef RKV_WATCHDOG_REG_ENABLE_RST_SV
`define RKV_WATCHDOG_REG_ENABLE_RST_SV
  
class rkv_watchdog_reg_enable_rst extends rkv_watchdog_base_element_sequence;
  `uvm_object_utils(rkv_watchdog_reg_enable_rst)

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

  virtual task body();
    int rd_val;
    super.body();
    rgm.WDOGCONTROL.RESEN.set('b1);  //set resen value = 1
    rgm.WDOGCONTROL.update(status);
  endtask
endclass
`endif

这里简单介绍set+update与直接write的差别。首先前者可以进行寄存器域的访问,其次就是前者不一定会完成一次写操作,只有set之后的rgm值与实际硬件值不同时,才会进行一次写操作。

剩下的复位关闭,中断使能与关闭结构与上述代码相同。只需要改动如下:

  	rgm.WDOGCONTROL.INTEN.set('b0);
    rgm.WDOGCONTROL.update(status);

 	rgm.WDOGCONTROL.RESEN.set('b0);
    rgm.WDOGCONTROL.update(status);

	rgm.WDOGCONTROL.INTEN.set('b1);
    rgm.WDOGCONTROL.update(status);

3.loadcount sequence加载寄存器序列

loadcount seq代码如下:

`ifndef RKV_WATCHDOG_LOADCOUNT_SV
`define RKV_WATCHDOG_LOADCOUNT_SV
  
class rkv_watchdog_loadcount extends rkv_watchdog_base_element_sequence;
  `uvm_object_utils(rkv_watchdog_loadcount)
  rand int load_val;

  constraint cstr{
    soft load_val inside{['h1:'hffffff]};
  };
  function new (string name = "rkv_watchdog_loadcount");
    super.new(name);
  endfunction

  virtual task body();
    int rd_val;
    super.body();
    rgm.WDOGLOAD.write(status, load_val); 
    rgm.WDOGLOAD.read(status, rd_val); 
    void'(this.diff_value( rd_val , load_val));
  endtask
endclass
`endif

此seq对WDOGLOAD寄存器进行了写入,其中传递值load_val可以由外部约束。

4.wait interrupt clear sequence清除中断序列

wait_inrt_clr 代码如下:

`ifndef RKV_WATCHDOG_INRT_WAIT_CLEAR_SV
`define RKV_WATCHDOG_INRT_WAIT_CLEAR_SV
  
class rkv_watchdog_inrt_wait_clear extends rkv_watchdog_base_element_sequence;
  `uvm_object_utils(rkv_watchdog_inrt_wait_clear)
  rand int delay,interval;  //delay is the time for clear after inten asserted
                            //interval is the refresh frequency

  constraint cstr{
    soft delay inside{[10:100]};
    soft interval inside{[1:10]};
  };
  function new (string name = "rkv_watchdog_inrt_wait_clear");
    super.new(name);
  endfunction

  virtual task body();
    super.body();
    forever begin
      rgm.WDOGMIS.mirror(status);
      if(rgm.WDOGMIS.INT.get()) begin  //check if the interrut happened
         break; 
      end
      repeat(interval) @(posedge vif.apb_clk);
    end
    repeat(delay) @(posedge vif.wdg_clk);
    // rgm.WDOGINTCLR.INTCLR.set('b1);
    // rgm.WDOGINTCLR.update(status);
    rgm.WDOGINTCLR.write(status, 'b1);  // clear the inten signal
  endtask
endclass
`endif

此seq会不断等待watchdog的中断,当中断发生之后,会对WDOGINTCLR寄存器进行写操作,从而清除当前中断(即INTEN信号拉低并开始重新根据WDOGLOAD值进行计数)。写入值不会对清除中断这一操作造成影响。

二、计数,中断与上锁测试

1.计数与复位使能测试

对watchdog进行配置,使其能够开始计数。

countdwn 代码如下

`ifndef RKV_WATCHDOG_COUNTDOWN_VIRT_SEQ_SV
`define RKV_WATCHDOG_COUNTDOWN_VIRT_SEQ_SV
  
class rkv_watchdog_countdown_virt_seq extends rkv_watchdog_base_virtual_sequence;
  `uvm_object_utils(rkv_watchdog_countdown_virt_seq)

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

  virtual task body();
    int rd_val;
    uvm_status_e status = UVM_IS_OK;
    super.body(); 
    `uvm_info("body", "Entered...", UVM_LOW)
    `uvm_do_with( loadcount_seq, { load_val == 'h20; })
    `uvm_do( enable_inrt_seq )
     repeat(3) begin  
       `uvm_do_with( int_wait_clr_seq, { delay == 10; interval inside {[10:20]}; })
       wait_int_rise();
     end
    `uvm_info("body", "Exiting...", UVM_LOW)
  endtask
endclass
`endif

开始时对计数值进行配置,随后使能中断信号使得watchdog开始计数。随后进行重复三次的中断消除测试,测试的波形结果如下:
在这里插入图片描述
在这里插入图片描述

波形图中反映确实进行了三次清除中断的操作,清除之后会重新加载计数值,同时计数行为也是正确的。

若在计时同时打开resen信号(复位使能信号),watchdog会在中断结束后重新计时,并在计时结束后WDOGRES信号拉高,如下图所示:
在这里插入图片描述

2.中断功能关闭测试

中断功能关闭时只在正常计数,中断拉高之后,将中断使能信号INTEN信号拉低,检查此时中断信号的变化,以及对应寄存器的值是否发生变化。

inten_disable_seq代码:

`ifndef RKV_WATCHDOG_DISABLE_INRT_VIRT_SEQ_SV
`define RKV_WATCHDOG_DISABLE_INRT_VIRT_SEQ_SV
  
class rkv_watchdog_disable_inrt_virt_seq extends rkv_watchdog_base_virtual_sequence;
  `uvm_object_utils(rkv_watchdog_disable_inrt_virt_seq)

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

  virtual task body();
    int rd_val;
    uvm_status_e status = UVM_IS_OK;
    super.body(); 
    `uvm_info("body", "Entered...", UVM_LOW)
    `uvm_do_with( loadcount_seq, { load_val == 'h20; })
    `uvm_do( enable_inrt_seq )
    wait_int_rise();
    check_mis_ris( 1 , 1 ); 
    repeat(4) @(posedge vif.wdg_clk);
    `uvm_do( disable_inrt_seq )
    check_mis_ris( 0 , 1 );
    #2us;
    `uvm_do( enable_inrt_seq )
    `uvm_info("body", "Exiting...", UVM_LOW)
  endtask
  
  // mis : interrupt status register
  // ris : raw interrupt register
  virtual task check_mis_ris(input bit mis, input bit ris);
    rgm.WDOGRIS.mirror(status);
    void'(this.diff_value( ris , rgm.WDOGRIS.RAWINT.get() ));
    rgm.WDOGMIS.mirror(status);
    void'(this.diff_value( mis , rgm.WDOGMIS.INT.get() ));
  endtask
endclass
`endif

WDOGRIS与WDOGMIS都在前文的寄存器介绍中提到,二者与INTEN的关系是:WDOGRIS&INTEN = WDOG。通过check_mis_ris来比较寄存器的值是否符合预期。

仿真的运行结果如下:
在这里插入图片描述
在这里插入图片描述
在INTEN拉低之后中断信号会立即拉低并且暂停技术,重新将INTEN拉高会直接进入中断状态(中断信号拉高),因为此时并未进行中断清除。

3.上锁测试

上锁测试为了测试watchdog的上锁功能:即在写入除’h1acce551的其他值都将锁定watchdog,使其寄存器无法被访问。

测试的方法是先默认情况下配置watchdog寄存器使其开始计数并检查其上锁情况,随后对其进行锁定与解锁的检查,check_unlock_control_status这一任务的逻辑是,通过读出控制寄存器的值,对控制寄存器进行与读出值相反的写入,最后读出,检查写入是否成功。成功则为非锁定状态,失败则说明处于锁定状态。

上锁测试代码

`ifndef RKV_WATCHDOG_LOCK_VIRT_SEQ_SV
`define RKV_WATCHDOG_LOCK_VIRT_SEQ_SV
  
class rkv_watchdog_lock_virt_seq extends rkv_watchdog_base_virtual_sequence;
  `uvm_object_utils(rkv_watchdog_lock_virt_seq)

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

  virtual task body();
    int rd_val;
    uvm_status_e status = UVM_IS_OK;
    super.body(); 

    `uvm_info("body", "Entered...", UVM_LOW)
    rgm.WDOGLOCK.read( status, rd_val);
    void'(diff_value( rd_val[0] , 'h0 ));  // check current status
    `uvm_do_with( loadcount_seq, { load_val == 'h20; } )
    check_unlock_control_status();
    rgm.WDOGLOCK.write( status, 'b0 );
    rgm.WDOGLOCK.read( status, rd_val );
    void'(diff_value( rd_val[0] , 'b1 )); // lock the watchdog
    check_lock_control_status();

    rgm.WDOGLOCK.write( status, 'h1ACCE551 );
    rgm.WDOGLOCK.read( status, rd_val );
    void'(diff_value( rd_val[0], 'b0 ));  // unlock the watchdog
    check_unlock_control_status();
    `uvm_info("body", "Exiting...", UVM_LOW)
  endtask
 
  virtual task check_unlock_control_status();
    bit rd_val, wt_val;
    rgm.WDOGCONTROL.mirror(status);   
    wt_val = ~rgm.WDOGCONTROL.INTEN.get();
    rgm.WDOGCONTROL.INTEN.set( wt_val );  //write a different value
    rgm.WDOGCONTROL.update(status);
    rgm.WDOGCONTROL.read( status, rd_val );
    void'(diff_value( rd_val, wt_val ));  // check if the write is successful
  endtask
  
  virtual task check_lock_control_status();
    bit rd_val, wt_val;
    rgm.WDOGCONTROL.mirror(status);
    wt_val = ~rgm.WDOGCONTROL.INTEN.get();  
    rgm.WDOGCONTROL.INTEN.set( wt_val );  //write a different value
    rgm.WDOGCONTROL.update(status);
    rgm.WDOGCONTROL.read( status, rd_val );
    rd_val = !rd_val;
    void'(diff_value( rd_val, wt_val ));  //check the write is failed
  endtask 
endclass
`endif

最后仿真的结果如下:
在这里插入图片描述
计数完成后,只有在unlock的情况下才能将INTEN信号拉低。同时仿真无报错,每次lock check都成功。

总结

本文介绍了watchdog验证中的一些testcase创建与仿真,通过波形和一些简单函数完成了简单功能点的测试,也便于验证者熟悉watchdog的各项功能。但是并未对功能点测试中进行自动化的check,比如计数是否按照给定时间完成等,下一章将进行scoreboard的介绍。

  • 2
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值