草庐IT

(数字ic验证)从零开始的apb_watchdog验证模块搭建(一、功能介绍与环境搭建)

peop1e 2023-09-20 原文

apb_watchdog验证模块搭建(一)文章目录


文章目录


前言

本文介绍了apb_watchdog验证模块的搭建过程与其具体代码,主要包括其中验证环境的构建,测试序列的发送与回归测试收集。主要运用systemvorilog与uvm在已有apb2总线vip(来源路科验证)的基础上进行验证测试,仿真验证工具为vcs。本项目的设计代码由路科验证提供。

一、apb_watchdog简单功能介绍

关于apb_watchdog的具体功能可以参考arm提供的设计功能描述文档**《Cortex™-M System Design Kit》**其中第四节component部分对apb_watchdog进行了详细介绍。下文对其主要功能进行简单介绍,具体功能与寄存器描述将在后文验证中提到。

下图为apb_watchdog接口,左侧接口为apb2总线接口(从信号方向可知其为从端),通过总线端的数据传输完成对watchdog的寄存器进行功能配置,右侧为watchdog本身接口。

watchdog为一个32bit逐减计数器,由重载寄存器初始化,WDOGLOAD。当watchdog计数器减少到预计的数值时,输出端WDPGINT将拉高,输出中断信号。

在WDOGCLKEN信号为高时,计数器会在每个WDOGCLK的上升沿减一。计数到0时,若此时WDOGCONCROL寄存器部分的INT域与RES域为高则使能reset与interrupt功能。即输出中断信号并且watchdog会安装WDOGLAOD中预定值进行重置。若WDOGCONTROL域中有未使能的情况则回等待重置或无法输出中断。

二、测试平台环境搭建

1.测试所用模板介绍

利用gvim可以创建与打开文件如下:
vorilog文件夹中存放设计代码。
uvm文件夹中存放验证代码与其环境结构。
cfg为配置文件,由顶层basetest创建,其中包括一些配置信息,也可将interface,regmodel打包传入。
env为验证环境部分,主体为pkg,env与virtual sequencer
reg为寄存器模型
seq_lib为存放测试用例sequence文件夹
sim为仿真文件夹
tb为testbench与interface文件夹
test用以存放不同测试文件
vip_lib中存放了apb2总线的vip
在创建模板时,除vip部分其他基本为空

2.tb与interface

interface代码:


interface rkv_watchdog_if;

  logic [3:0] ecorevnum = 4'b1011;
  logic       wdogint;
  logic       wdogres;
  logic   	  apb_clk;
  logic 	  apb_rstn;
  logic	      wdg_clk;
  logic	      wdg_rstn;
endinterface 

tb中提供了apb与watchdog的时钟与复位信号,以及wdogint与wdogres。ecorevnum是注册修订号,赋值即可。

tb代码:




module rkv_watchdog_tb;
  import uvm_pkg::*;
  `include "uvm_macros.svh"
  import rkv_watchdog_pkg::*;

bit apb_clk;
bit apb_rstn;
bit wdg_clk;
bit wdg_rstn;

cmsdk_apb_watchdog dut(
  .PCLK(apb_clk),                         // APB clock
  .PRESETn(apb_rstn),                     // APB reset
  .PENABLE(apb_if_inst.penable),          // APB enable
  .PSEL(apb_if_inst.psel),                // APB periph select
  .PADDR(apb_if_inst.paddr[11:2]),        // APB address bus
  .PWRITE(apb_if_inst.pwrite),            // APB write
  .PWDATA(apb_if_inst.pwdata),            // APB write data

  .WDOGCLK(wdg_clk),                      // Watchdog clock
  .WDOGCLKEN(1'b1),                       // Watchdog clock enable
  .WDOGRESn(wdg_rstn),                    // Watchdog clock reset

  .ECOREVNUM(wdg_if_inst.ecorevnum),      // ECO revision number

  .PRDATA(apb_if_inst.prdata),            // APB read data

  .WDOGINT(wdg_if_inst.wdogint),          // Watchdog interrupt
  .WDOGRES(wdg_if_inst.wdogres)           // Watchdog timeout reset
);

apb_if apb_if_inst(apb_clk, apb_rstn);
assign wdg_if_inst.apb_clk = apb_clk;
assign wdg_if_inst.wdg_clk = wdg_clk;
assign wdg_if_inst.apb_rstn = apb_rstn;
assign wdg_if_inst.wdg_rstn = wdg_rstn;

rkv_watchdog_if wdg_if_inst();

initial begin : clk_gen
  fork
    forever #5ns  apb_clk <= !apb_clk; // 100MHz
    forever #25ns wdg_clk <= !wdg_clk; // 20MHz
  join
end

initial begin : rstn_gen
  #2ns;
  apb_rstn <= 1;
  #20ns;
  apb_rstn <= 0;
  #20ns;
  apb_rstn <= 1;
end
assign wdg_rstn = apb_rstn;

initial begin : vif_assign
  uvm_config_db#(virtual apb_if)::set(uvm_root::get(), "uvm_test_top.env.apb_mst", "vif", apb_if_inst);
  uvm_config_db#(virtual rkv_watchdog_if)::set(uvm_root::get(), "uvm_test_top.env", "vif", wdg_if_inst);
  uvm_config_db#(virtual rkv_watchdog_if)::set(uvm_root::get(), "uvm_test_top.env.virt_sqr", "vif", wdg_if_inst);
  run_test("");
end


endmodule

tb中主要包括时钟复位信号的产生,watchdog模块的例化,interface传递于env以及各部分的连接。值得注意的是:总线地址的连接应按照设计文件的使用要求,PADDR(apb_if_inst.paddr[11:2]),以访问得到正确的地址。

2.env与watchdog_pkg

env代码:

class rkv_watchdog_env extends uvm_env;

  apb_master_agent apb_mst;
  rkv_watchdog_virtual_sequencer virt_sqr;

  `uvm_component_utils(rkv_watchdog_env)

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

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    apb_mst = apb_master_agent::type_id::create("apb_mst", this);
    virt_sqr = rkv_watchdog_virtual_sequencer::type_id::create("virt_sqr", this);
  endfunction

  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    virt_sqr.apb_mst_sqr = apb_mst.sequencer;
  endfunction

endclass


此时env中包括apb_mst_agt与sequencer,并且将apb_mst中的sequencer与watchdog中的sequencer连接起来,这样后期在写sequence时可以通过watchdog的sequence完成对apb总线部分的访问,vip的连接也就完成了。

env代码:

`ifndef RKV_WATCHDOG_PKG_SV
`define RKV_WATCHDOG_PKG_SV

package rkv_watchdog_pkg;

  import uvm_pkg::*;
  `include "uvm_macros.svh"
  import apb_pkg::*;

  `include "rkv_watchdog_config.svh"
  `include "rkv_watchdog_reg.svh"
  `include "rkv_watchdog_cov.svh"

  `include "rkv_watchdog_virtual_sequencer.sv"
  `include "rkv_watchdog_env.sv"

  `include "rkv_watchdog_seq_lib.svh"
  `include "rkv_watchdog_tests.svh"

endpackage


`endif

在pkg中包括了仿真时需要编译的文件,后期在添加新文件时需注意是否加入仿真,同时应注意编译文件的顺序:如config等object类型应在env等组件类型之前,否则容易报错。sequencer代码较为简单,只需要声明即可,值得注意的是,需要在sequencer中声明apb_master_sqr以完成对apb部分vip的调用。

3.test与base_sequence

test代码:

`ifndef RKV_WATCHDOG_BASE_TEST_SV
`define RKV_WATCHDOG_BASE_TEST_SV

virtual class rkv_watchdog_base_test extends uvm_test;

  rkv_watchdog_env env;

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

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env = rkv_watchdog_env::type_id::create("env", this);
  endfunction

  task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);
    do_init_clks();
    do_init_regs();
    phase.drop_objection(this);
  endtask

  virtual task do_init_clks();
    
  endtask

  virtual task do_init_regs();
    
  endtask
endclass

`endif

base test中例化了env,并给出了时钟与寄存器初始化的task,后期在不同test中可以对其进行相应的修改。

base_sequence代码:

`ifndef RKV_WATCHDOG_BASE_VIRTUAL_SEQUENCE_SV
`define RKV_WATCHDOG_BASE_VIRTUAL_SEQUENCE_SV

class rkv_watchdog_base_virtual_sequence extends uvm_sequence;
  `uvm_object_utils(rkv_watchdog_base_virtual_sequence)
  `uvm_declare_p_sequencer(rkv_watchdog_virtual_sequencer)

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

  virtual task body();
    `uvm_info("body", "Entered...", UVM_LOW)
    // TODO in sub-class
    `uvm_info("body", "Exiting...", UVM_LOW)
  endtask

endclass

`endif  

base test中完成了声明,并将watchdog sequencer注册为p_sequencer,这一步的目的与uvm中激励发送的driver,sequence和sequencer之间的关系有关。笔者在这里的简单理解是sequence在被挂载至sequencer上时会将当前sqr传递给底层的m_sequencer,由m_sequencer完成driver与sequence之间的桥梁工作,在激励发送时会检查m_sequncer与p_sequncer的指向是否相同,以此保证激励发送符合使用者的理解。当然,这一点理解对此简单项目不构成影响。

4.Makefile

Makefile代码:

#############################
# User variables
#############################
TB       	= rkv_watchdog_tb
SEED     	= 1
GUI      ?= 0
DOTCL    ?= 1
TESTNAME ?= rkv_watchdog_integration_test
DFILES  	= ../../verilog/{cmsdk_apb_watchdog_frc.v,cmsdk_apb_watchdog.v} 
VFILES   +=  ../vip_lib/apb_pkg/apb_pkg.sv \
						 ../vip_lib/apb_pkg/apb_if.sv \
						 ../env/rkv_watchdog_pkg.sv \
						 ../tb/rkv_watchdog_if.sv \
						 ../tb/rkv_watchdog_tb.sv


#############################
# Environment variables
#############################
VCOMP_INC = +incdir+../../verilog \
						+incdir+../vip_lib/apb_pkg \
						+incdir+../{cfg,cov,reg,env,seq_lib,test} 
VCOMP    = vlogan -full64 -ntb_opts uvm-1.2 -sverilog -timescale=1ps/1ps -nc -l comp.log $(VCOMP_INC)
ELAB     = vcs -full64 -ntb_opts uvm-1.2 -debug_acc+all -l elab.log -sim_res=1ps 
RUN      = ./$(TB).simv -l run.log -sml +ntb_random_seed=$(SEED) +UVM_TESTNAME=$(TESTNAME)
SIMRUNFILE 	= rkv_watchdog_sim_run.do
ifeq ($(GUI),1)
RUN += -gui
endif
ifeq ($(DOTCL),1)
RUN += -ucli -do $(SIMRUNFILE)
endif

comp:
	$(VCOMP) 
	$(VCOMP) $(DFILES) $(VFILES)

elab: comp
	$(ELAB) -top $(TB) -o $(TB).simv

run:
	$(RUN) 

clean:
	rm -rf 64 AN.DB DVEfiles csrc *.simv *.simv.daidir *.simv.vdb ucli.key
	rm -rf *.log* *.vpd *.h urgReport

makefile中将设计与验证代码加入编译队列


总结——当前验证结构

本文介绍了apb watchdog验证的框架搭建,主要是env,sequence,sequencer,test,tb,interface与Makefile脚本的建立,将验证的骨架搭建起来,但是并没有发送任何激励,下一节将更新寄存器激励发送与寄存器模型的融入。目前的验证框图可如下表示:(由viso完成)

本项目来源路科验证,有疑问可私信

有关(数字ic验证)从零开始的apb_watchdog验证模块搭建(一、功能介绍与环境搭建)的更多相关文章

  1. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  3. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  4. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  5. ruby-on-rails - 如何将验证与模型分开 - 2

    我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:

  6. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  7. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  8. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  9. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  10. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

随机推荐