当前位置: 首页 > news >正文

IP验证学习之env集成编写

目录

前言

寄存器同步机制:

env集成思想:

env_config:

reference model:

DPI:

总结



前言


env中包含的组件比较多,读者对于其中的组件想必也是有大概的了解的,有关概念在前文的UVM入门基础中阐述了,这里就直接进入话题了。

本文重点讲述的是寄存器同步的思想以及env常规集成方法,读者可以细致体会一下。  


寄存器同步机制:

编写env之前我们首先要确定内的含有的组件以及连接方式,接着就可以着手先写一个粗略的框架再细致的打磨细节了。

现在假设我们的DUT内部存在两个寄存器,一个是apb配置寄存器,通过apb总线来配置DUT的工作模式以及参数等。

另一个是DUT内部实际工作的寄存器,这个寄存器与apb配置寄存器属于不同的时钟域,DUT通过这个实际工作寄存器来进行配置。

那么DUT实际同步过程可能是:

APB配置阶段UVM测试平台通过APB总线写入到UDT的apb配置寄存器DUT会从APB从机接口(Slave)接收写入的数据,将其更新内部配置寄存器。

同步触发可能通过以下方式触发同步:

        软件触发:CPU写入一个特定的 update 寄存器中,DUT采样update寄存器。

        硬件触发:模块在特定时钟沿(如 posedge clk)自动采样配置寄存器。同步逻辑将配置寄存器的值复制到实际工作寄存器中

模块中使用工作寄存器DUT的功能模块从工作寄存器读取配置值,而不是直接读取APB配置寄存器(这样可以避免跨时钟域问题或中间状态)。

为了验证这种寄存器同步机制,同步寄存器孕育而生,其具体的验证机制为:

同步寄存器模型:

在 UVM中的建立同步寄存器模型,用于与DUT的实际工作寄存器比较,这样可以验证同步逻辑的正确性。

同时跟踪APB配置寄存器的值,模拟同步过程,生成预期的工作寄存器值,这是寄存器同步 参考模型的作用。

好了,了解完上述的同步流程后,我们可以开始编写env环境了,有关内部的组件这里不做介绍了。

如图是一个常见的env内部常见组件。

env集成思想:

集成架构简图:

其中agt_cfg可以在agent里例化,也可以在上层例化,包括virtual sequencer等组件,需要读者自行考量合适的UV,验证架构。

下述代码中并没有实例化  env_cfg,其原因在于env_cfg通常在test层实例化,在代码中仅仅只是声明了句柄,并没有创建该实例。

class logic_op_env extends uvm_env;`uvm_component_utils(logic_op_env)//--------------------------------------//Data Member//--------------------------------------apb_shared_cfg    apb_cfg ;            //SVT APB 的config ,作为备份也将它放到env中。
logic_op_env_config    env_cfg;        //管理整个环境的组件配置和变量//----------------------------------------// Component Member//----------------------------------------//scb fifo inst//refm fifo inst//tlm inst//env inst//agent inst
op_in_agent#(`DATA_WIDTH)    input_agt;
apb_basic_env    apb_env;                        //例化apb的环境,作为子env使用
op_out_agent#(`DATA_WIDTH)    output_agt;//virtual sequencer inst
logic_op_vsequencer    logic_op_vseqr;        //例化一个virtual sequencer,控制全局的sequencer,和virtual sequence 搭配使用//scb inst
logic_op_scoreboard#(`DATA_WIDTH,`DATA_WIDTH)    logic_op_scb;    //定义了一个带参数的记分板实例//参考模型
logic_sync_reg_refm    logic_sync_reg_rm ;        //同步寄存器机制的参考模型实例
logic_op_refm#(`DATA_WIDTH,`DATA_WIDTH)    logic_op_rm;            //DUT的参考模型实例//function cov inst
logic_op_func_cov    cov;                        //例化覆盖率组件,覆盖率组件需要自己编写//寄存器模型
string hdl_path = "logic_op_tb_top.u_logic_op" ;    //定义了一个字符串变量hdl_path,用于存储DUT的寄存器路径
logic_reg_model    rgm;                            //定义寄存器模型的实例//--------------------------------------------------------//Methods//--------------------------------------------------------//standard UVM Methods
extern function new(string name,uvm_component parent);
extern function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);endclass: logic_op_envfunction logic_op_env::new(string name, uvm_component parent);super.new(name, parent);`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH)
endfunction: newfunction void logic_op_env::build_phase(uvm_phase phase);super.build_phase(phase);`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH)if(!uvm_config_db #(logic_op_env_config):: get(this, " " , "logic_op_env_config", env_cfg))begin   //从base_test或者上级env中获取当前env的env_cfg`uvm_error("build_phase", "environment config not found")end//获取VIP的configif (env_cfg.apb_cfg!=null) begin    //如果检测到env_cfg里面没有apb_cfguvm_config_db#(apb_shared_cfg)::set(this, "apb_env", "cfg" , env_cfg.apb_cfg) ;  //就在当前env里为子环境apb_env设置apb_cfg(如果当前env是主env的情况)endelse beginapb_cfg = apb_shared_cfg::type_id_create("apb_cfg" , this);    //如果当前env_cfg检测到了apb_cfg,就需要实例化uvm_config_db#(apb_shared_cfg)::set(this, "apb_env", "cfg" , apb_cfg) ;    //实例化完成后为子环境apb_env设置apb_cfgend//建立寄存器模型if(!uvm_config_db#(logic_reg_model)::get(this, "" , "logic_reg_model", rgm) && env_cfg.rgm == null)    //test或者上级env中获取不到寄存器模型或者env_config里的寄存器模型是空指针,就要自己做实例化begin`uvm_info("GETRGM" , " no top_down RGM hangle is assigned" ,UVM_LOW)rgm = logic_reg_model::type_id_create({get_full_name(), ".logic_reg_model"}, this) ;    //实例化寄存器模型env_cfg.rgm = rgm ;    //保证env_cfg里的寄存器模型和当前实例化的一样`uvm_info("NEWRGM", "created rgm instance locally" , UVM_LOW)endelse if(env_cfg.rgm != null) begin    //检测到env_cfg里面有寄存器模型rgm = env_cfg.rgm;      //保证env_cfg里的寄存器模型与当前实例化的一样end//建立子寄存器模型,这个子寄存器模型用于同步检测,读者可以理解为同步后的寄存器模型if(env_cfg.sync_rgm == null) begin    //检测到env_cfg里没有同步寄存器模型就要自己实例化env_cfg.sync_rgm = logic_reg_model::type_id_create({get_full_name(), ".sync_rgm"}, this) ;    env_cfg.sync_rgm.build();    //调用寄存器模型的build()方法完成寄存器模型的构建env_cfg.sync_rgm.lock_model();    //锁定寄存器模型,禁止再修改endif(rgm.get_parent() == null) begin    //如果当前寄存器模型是顶层寄存器模型,不是子寄存器模型rgm.build() ;rgm.add_hdl_path(hdl_path) ;rgm.lock_model() ;rgm.reset();rgm.amp.set_auto_predict(0) ;enduvm_config_db#(uvm_reg_block)::set(this, "apb_env.apb_master_env.master", " apb_regmodel1", rgm) ;    //因为是集成vip,所以连接寄存器模型使用uvm_config_db,如果是自己手写的,还需要关联sequencer并使用adapter与DUT连接。//sub_env reg_model1 connect
//Example:
//if(env_cfg.has_sub_env) begin
//    env_cfg.sub_env_cfg.rgm = rgm.sub_env_blk;    //将寄存器模型的子块sub_env_blk赋值给sub_env_cfg里面的寄存器模型
//    env_cfg.sub_env_cfg.sync_rgm = env_cfg.sync_rgm.sub_env_blk;
//end//scb fifo new//refm fifo new//viltual sequencer 实例化
if(env_cfg.has_logic_op_vseqr) beginlogic_op_vseqr = logic_op_vsequencer::type_id::create("logic_op_vseqr", this);
end//env exists or not //agent exists or not
if(env_cfg.has_input_agt) beginuvm_config_db #(op_in_agent_config#(DATA_WIDTH))::set(this, $sformatf("input_agt"), "op_in_agent_config", env_cfg.input_agt_cfg);    //在当前env环境中配置agt_cfg到agent里input_agt = op_in_agent#(`DATA_WIDTH)::type_id::create($sformatf("input_agt"), this);
endif(env_cfg.has_output_agt) beginuvm_config_db #(op_out_agent_config#(DATA_WIDTH))::set(this, $sformatf("output_agt"), "op_out_agent_config", env_cfg.output_agt_cfg);    //在当前env环境中配置agt_cfg到agent里output_agt = op_out_agent#(`DATA_WIDTH)::type_id::create($sformatf("output_agt"), this);
endif(env_cfg.has_apb_agt) beginapb_env = apb_basic_env::type_id::create("apb_env" , this);    //实例化子环境apb_env
end//参考模型实例化
if(env_cfg.has_logic_op_refm) beginlogic_op_rm = logic_op_refm#(`DATA_WIDTH,`DATA_WIDATH)::type_id_create($sformatf("logic_op_rm"), this) ;
endlogic_sync_reg_rm = logic_sync_reg_refm::type_id_create($sformatf("logic_sync_reg_rm"), this);//scoreboard 实例化
if(env_cfg.has_logic_op_scoreboard) beginlogic_op_scb = logic_op_scoreboard#(`DATA_WIDTH,`DATA_WIDTH)::type_id_create($sformatf("logic_op_scb"), this) ;
end//refm config//function coverage config
if(env_cfg.has_functional_coverage) begincov = logic_op_func_cov::type_id::create("cov" , this);
endendfunction: build_phasefunction void logic_op_env::connect_phase(uvm_phase phase);super.connect_phase(phase);`uvm_info("TRACE" , $sformatf("%m") , UVM_HIGH)// reg_model connect//function coverage connect
//if(env_cfg.has_functional_coverage) begin
//    xxx.agt.analysis_port.connect(cov.cov_imp);
//end//v_seqr connect //tlm connect //v_seqr config
if(env_cfg.has_logic_op_vseqr && env_cfg.has_input_agt == 1 && env_cfg.input_agt_cfg.active == UVM_ACTIVE) beginlogic_op_vseqr.input_seqr = input_agt.seqr;
endif(env_cfg.has_logic_op_vseqr && env_cfg.has_apb_agt == 1) beginlogic_op_vseqr.apb_seqr = apb_env.apb_master_env.master.sequencer;
endif(env_cfg.has_logic_op_vseqr && env_cfg.has_output_agt == 1 && env_cfg.output_agt_cfg.active == UVM_ACTIVE) beginlogic_op_vseqr.output_seqr = output_agt.seqr;
end//连接apb_vip的接口到寄存器模型的接口
if(env_cfg.has_apb_agt) begin                   apb_env.apb_master_env.slave[0].monitor.item_observed_port.connect(logic_sync_reg_rm.reg_            fifo.analysis_export)
end   //refm connect
if(env_cfg.has_logic_op_refm) begininput_agt.analysis_port.connect(logic_op_rm.op_in1_fifo.analysis);
end//scoreboard
if(env_cfg.has_logic_op_scoreboard) beginoutput_agt.analysis_port.connect(logic_op_scb.acutal_op_out1_fifo.analysis_export) ;logic_op_rm.op_out1_ap.connect(logic_op_scb.golden_op_out1_fifo.analysis_export) ;
endendfunction: connect_phase

看以看到上述的env环境中使用了两个参考模型和一个寄存器模型,这个寄存器模型还包括了子寄存器模型。不过上述的env环境主要是为了集成,因此寄存器模型实例化写得复杂,实际上一般的env环境只需集成好各组件即可,所有的配置通常都会在env_config中配置。

此外有的env里还会例化上adapter(转换器),用于寄存器模型传输数据,详细的传输过程在前文中已有讲述。

env_config:

env_config里面存放了所有有关env和agent的配置,有的还包括了接口配置等。所有的配置都放到env_config里面主要是为了方便集成者修改环境。

class logic_op_env_config extends uvm_object;`uvm_object_utils(logic_op_env_config)//virtual interfacevirtual clock_if#(300 , 0)    sys_clk;            //同步时钟,给dut使用virtual clock_if#(100 , 0)    apb_clk;            //apb总线时钟,给apb_vip使用virtual reset_if#(100)        sys_rst;            //同步复位接口,给dut使用virtual reset_if#(100)        apb_rst;            //apb复位接口,给apb_vip使用//用户自定义中断程序process    main_process;                        //定义了一个进程句柄//func_cov  activebit has_functional_coverage = 0;//refm activebit has_logic_op_refm = 1;//scoreboard activebit has_logic_op_scoreboard = 1;//virtual sequencer activebit has_logic_op_vseqr = 1 ;//have env or not //have agent or notbit has_input_agt = 1;bit has_output_agt = 1;bit has_apb_agt = 1;//add env_config//add agent configop_in_agent_config#(`DATA_WIDTH)     input_agt_cfg ;        //在env_config里面例化agent_config,实现env_config全局控制apb_shared_cfg    apb_cfg;op_out_agent_config#(`DATA_WIDTH)    output_agt_cfg;//register modellogic_reg_model    rgm;logic_reg_model    sync_rgm;extern function new(string name = "logic_op_env_config");extern virtual function config_env(string name = "LOGIC_OP");//vip config functionextern virtual function config_apb();    //针对VIP的配置函数endclassfunction logic_op_env_config::new(string name = "logic_op_env_config");super.new(name) ;//create env config//create agent configinput_agt_cfg = op_in_agent_config#(`DATA_WIDTH)::type_id::create("in[ut_agt_cfg") ;apb_cfg = apb_shared_cfg::type_id::create("apb_cfg") ;output_agt_cfg = op_out_agent_config#(`DATA_WIDTH)::tyep_id::create("output_agt_cfg");endfunction: new function logic_op_env_config::config_env(string name = "LOGIC_op");`uvm_info("CONFIG_ENV", "starting config environment", UVM_MEDIUM)//get interface to agent configif(!uvm_resource_db #(virtual op_in_if#(`DATA_WIDTH))::read_by_name("interface_pool", {name,"_input_if"}, input_agt_cfg.vif)) begin    //从全局资源库中读取接口,这个资源库在顶层命名为interface_pool`uvm_fatal("VIF_NOT_FOUND",{"Fail to get ", name , "_input_if from resource_db: interface_pool"})endif(!uvm_resource_db #(virtual op_out_if#(`DATA_WIDTH))::read_by_name("interface_pool", {name,"_output_if"}, output_agt_cfg.vif)) begin`uvm_fatal("VIF_NOT_FOUND",{"Fail to get ", name , "_output_if from resource_db: interface_pool"})endif(!uvm_resource_db #(virtual reset_if#(100))::read_by_name("interface_pool", {name,"_sys_rst"}, sys_rst)) begin`uvm_fatal("VIF_NOT_FOUND",{"Fail to get ", name , "_sys_rst from resource_db: interface_pool"})endif(!uvm_resource_db #(virtual reset_if#(100))::read_by_name("interface_pool", {name,"_apb_rst"}, apb_rst)) begin`uvm_fatal("VIF_NOT_FOUND",{"Fail to get ", name , "_apb_rst from resource_db: interface_pool"})endif(name =="LOGIC_OP") begin        //如果当前env是主env//agent configurationinput_agt_cfg.active = UVM_ACTIVE;output_agt_cfg.active = UVM_PASSIVE;//End agent configurationend//call vip config functionconfig_apb();            //调用定义好的apb配置函数`indef    PREFIX`define PREFIX LOGIC_OP`endif//sub environment configuration`undef PREFIXendfunction//vip function 
function logic_op_env_config::config_apb()apb_cfg.master_cfg.uvm_reg_enable = 1;apb_cfg.master_cfg.apb4_enable = 0;apb_cfg.master_cfg.apb3_enable = 0;endfunction

reference model:

参考模型未必都是用sv编写的,更多复杂的参考模型是用外部函数导入的,导入一般使用DPI接口。

基本的参考模型需要实现:

1.等待agent里传输过来的transaction

2.获取寄存器模型的配置信息(前后门访问)

3.根据获取到的数据和配置,实现DUT的功能。

4.进行数据输出,将要输出的数据通过tlm接口传输出去到其他组件。

class logic_op_refm extends uvm_component;`uvm_component_utils(logic_op_refm)//获取env_cfg里的配置信息logic_op_env_config    env_cfg;op_in_seq_item    op_in1_trans;        //接受到的数据流,也就是agent输入的transactionop_out_seq_item    op_out1_exp_trans;        //用于输出transaction,这个transaction和out_agent输出的事务格式是一致的。op_out_seq_item    op_out1_exp_trans_clone;        //用于将处理出来的结果在复制一份后输出,起到延时作用,可用可不用。uvm_tlm_analysis_fifo #(op_in_seq_item)    op_in1_fifo;        //例化tlm接口,这里使用的是fifo,主要用于获取输入uvm_analysis_port #(op_out_seq_item)        op_out1_ap;        //例化ap接口,这里使用广播接口,也可以使用其他接口。......endclassfunction logic_op_refm::new(string name = "logic_op_refm" , uvm_component parent);super.new(name,parent);op_in1_fifo = new("op_in1_fifo", this);op_out1_ap = new("op_out1_ap", this) ;op_in1_trans = op_in_seq_item::type_id::create("op_in1_trans");op_out1_exp_trans = op_out_seq_item::type_id::create("op_out1_exp_trans");endfunction: newfunction void logic_op_refm::build_phase(uvm_phase phase);super.build_phase(phase);//获取env_config里面的配置if(~uvm_config_db#(logic_op_env_config)::get(this, "*", "logic_op_env_config", env_cfg))`uvm_error("CFGERR" , "environment config is not set to refm! ")endfunction: bulid_phasetask logic_op_refm::run_phase(uvm_phase phase);...forever begin
]        op_in1_fifo.get(op_in1_trans) ;    //获取输入数据op_out1_exp_trans.data = op_in1_trans.data1 & op_in1_trans.data2;        //实现DUT的功能,这里假设DUT的功能就是将两个输入数据与一下$cast(op_out1_exp_trans_clone, op_out1_exp_trans.clone());        //复制一份在传出去,防止复制前数据被修改导致传出去的数据错误op_out1_ap.write(op_out1_exp_trans_clone) ;         //传出数据endendtask

DPI:

实际中的参考模型不一定非得要用sv,更多的会用其他语言,比如C语言,那么如何将C语言编写的模型导入到我们的参考模型中使用呢?

 首先需要在C语言侧定义cmodel函数,这个过程中需要注意接口的数据类型。

下面是一个极为简单的C模型代码,include用于交互使用。

#include <svdpi.h>
void logic_op_dpi (int data1 , int  data2, int sel , int *data)    {if(sel == 0)    *data = data1 & data2;else if(sel == 1)*data = data1 | data2;else if(sel == 2)*data = data1 ^ data2;else if(sel == 3)*data = data1 ^~ data2;}

在sv侧需要定义对应名字的函数,这个函数需要和C模型一样有相同的返回值和接口类型

import "DPI-C" function void logic_op_dpi(input int data1, input int data2, input int sel, output int data) ;

如何引入仿真?

 1.可以随着sv一起加入到编译中,若每次都要修改C模型都需要编译。

 2. 使用gcc编译成xxx.so文件,仿真选项加入-sv_ilb xxx。默认路径可以任意选择。

总结

env环境的集成主要是通过env_config配置集成的,读者拿到一个自己不熟悉的环境时首先看config里的东西有哪些,这样可以快速了解当前验证环境并集成出来。


文章转载自:

http://FbhgBY5K.mkczm.cn
http://gEv7fsvH.mkczm.cn
http://xFxDhIG7.mkczm.cn
http://FF9lQS7X.mkczm.cn
http://kl2TG5xX.mkczm.cn
http://WYlVgwDe.mkczm.cn
http://oundvKmJ.mkczm.cn
http://Z301nY1y.mkczm.cn
http://Hkcz3hNL.mkczm.cn
http://mOrWDyaP.mkczm.cn
http://dhVDX8P2.mkczm.cn
http://h3DS8vAT.mkczm.cn
http://zbblPw3n.mkczm.cn
http://L61F3Tns.mkczm.cn
http://bQW4eCtu.mkczm.cn
http://TA2p7G6f.mkczm.cn
http://j08c29bI.mkczm.cn
http://b81BUk2U.mkczm.cn
http://ygjNmHDU.mkczm.cn
http://FIv31jc9.mkczm.cn
http://Nzpe4aTT.mkczm.cn
http://Wxpzu9ZP.mkczm.cn
http://junLxAZp.mkczm.cn
http://TRoEAU6O.mkczm.cn
http://FiAXXHDW.mkczm.cn
http://tSprYX3P.mkczm.cn
http://IEOh2diM.mkczm.cn
http://RPk1nmfr.mkczm.cn
http://edK61kSP.mkczm.cn
http://LcFVgn2g.mkczm.cn
http://www.dtcms.com/a/380627.html

相关文章:

  • Android8 binder源码学习分析笔记(四)——ServiceManager启动
  • fastapi搭建Ansible Playbook执行器
  • 第四阶段C#通讯开发-1:通讯基础理论,串口,通讯模式,单位转换,代码示例
  • 微信小程序——云函数【使用使用注意事项】
  • 【java】常见排序算法详解
  • HarmonyOS 应用开发深度解析:基于声明式UI的现代化状态管理实践
  • Linux 中 exec 等冷门命令的执行逻辑探究
  • Qt多语言翻译实战指南:常见陷阱与动态切换解决方案
  • 【秋招笔试】2025.09.11阿里云秋招算法岗笔试真题
  • Ethernaut Level 1: Fallback - 回退函数权限提升攻击
  • 【VPX637】基于XCKU115 FPGA+ZU15EG MPSOC的6U VPX双FMC接口通用信号处理平台
  • Flutter基础(②④事件回调与交互处理)
  • 软考系统架构设计师之软件架构篇
  • 软考-系统架构设计师 访问控制和数字签名技术详细讲解
  • C语言初学者笔记【预处理】
  • android中ViewModel 和 onSaveInstanceState 的最佳使用方法
  • 达梦:将sql通过shell脚本的方式放在后台执行
  • 进阶向:从零开始理解Python音频处理系统
  • Centos7安装nginx
  • 数字图像处理-巴特沃斯高通滤波、低通滤波
  • Knockout数据绑定语法的入门教程
  • Serdes专题(1)Serdes综述
  • 2025年机器人项目管理推荐:三款工具破解机械设计到量产交付的协同难题
  • 后端post请求返回页面,在另一个项目中请求过来会出现的问题
  • 前端菜单权限方案
  • 【运维】-- 前端会话回放与产品分析平台之 openreplay
  • 前后端开发Mock作用说明,mock.ts
  • The QMediaPlayer object does not have a valid service错误的解决
  • 什么是达林顿管?
  • 每日算法题推送-->今日专题——双指针法