天津商城网站建设公司,广州设计公司排行榜,搜索引擎优化怎么做的,建设h网站风险大吗目录 1.前言
2.DRIVER实现
2.1 AHB二级流水时序
2.2 “队列错位法”实现driver
2.3 driver代码
2.4 仿真log与波形
2.5 多级流水拓展方法 1.前言
UVM driver在接口协议的实现中起着非常重要的作用#xff0c;因为它一端处理基于类的事务级sequence#xff0c;另一端处…目录 1.前言
2.DRIVER实现
2.1 AHB二级流水时序
2.2 “队列错位法”实现driver
2.3 driver代码
2.4 仿真log与波形
2.5 多级流水拓展方法 1.前言
UVM driver在接口协议的实现中起着非常重要的作用因为它一端处理基于类的事务级sequence另一端处理基于时钟的信号/引脚级的总线行为。因此如何实现 UVM driver及其与sequence的同步对于 DUT 和 UVM 环境之间的交互以及避免 UVM driver和sequence之间的任何死锁情况都是至关重要的。 而UVM reg model则提供了强大的前/后门访问寄存器的方式以便于对寄存器进行高效地配置和读取主要是通过UVM源码中所提供的uvm_reg_map::do_bus_read、uvm_reg_map::do_bus_write方法实现该方法的实现依赖于sequencer和adapter这2个组件。 其中adpter完全处理基于类的事务级sequence它能够将uvm_reg_item类和uvm_sequence_item类做相互转译。通过reg2bus方法将寄存器模型能够读懂的uvm_reg_bus_op翻译为总线bus_item事务级sequence如源码第2009行调用adapter.reg2busrw_access这一步相当于adapter充当了sequence产生bus_req transaction。 由于环境中指定了寄存器模型使用的sequencer因此源码中第2014行将bus_req transaction交给该sequencer随后调用start_item()finish_item()从而完成sequencer对sequence的仲裁及传输确保driver能够井然有序地拿到这些transaction。 环境中打开了auto_predict功能因此寄存器模型会根据driver返回的读取值更新寄存器的期望值和镜像值。因此driver中要完成对读写寄存器的反馈逻辑这一部分通常都是通过driver中的seq_item_port.item_done(bus_req)来完成的前提是未使用adapter.provides_responses功能在低速、简单的寄存器操作接口比如I2C、SPI、APB等这种方式较为常见因为对寄存器的操作不会涉及到复杂的总线行为driver只要按顺序调用seq_item_port.get_next_itembus_req从sequencer拿到sequence再将bus_req按照时序驱动到总线上随后按顺序调用seq_item_port.item_done(bus_req)即可这样我们是可以直接把返回信息通过req返回的。 但对于复杂的总线协议例如AHB、AXI等driver就必须要用put_responsebus_rsp来返回信息。比如AHB时序中因为读数据有可能在多拍之后才能从总线上获取此时master早已经将发送了下一笔transaction如果采用bus_req来返回信息那么driver没办法模拟真实的总线行为不能完成诸如burst类型的传输此时必须要开启adapter.provides_responses功能。从源码第2024~2030行可以看出一旦开启该功能adapter的bus2reg方法会将bus_rsp而非bus_req转译为uvm_reg_bus_op类型从而使得寄存器模型能够根据读数据正确地更新镜像值和期望值的同时driver还能模拟真实的AHB总线行为。
本文就是从UVM的源码do_bus_read/do_bus_write出发采用adapter.provides_responses功能结合rm.default_map.set_auto_predict(1)方法通过reg_model-adapter-sequencer-driver这样的通路实现了通过寄存器模型读写产生AHB时序的pin级接口时序的寄存器操作接口方法。
本文将分为几个部分分别阐述reg_modelsequenceadapterdriver的具体实现方式。
具体的环境架构如下 2.DRIVER实现
2.1 AHB二级流水时序 在流水线总线协议中数据传输被分解为两个或多个不同的阶段这些阶段一个接一个地执行。通常这些相位涉及总线上不同的信号集。以二级流水为例driver要驱动的时序如上图。
2.2 “队列错位法”实现driver 要实现driver驱动二级流水可以巧妙利用队列错位的方式实现。具体实现的流程图如下
主程序主要由2个forever线程构成
其中thread1seq_item_port.try_next_item(req)采用非阻塞的方式从seq不断地得到数据包得到非null数据包后将其装入队列中因为seq产生的数据包其地址和控制信号以及写数据信号全部同相位因此drv从seq得到的数据包其地址相和数据相是对齐的需要拆包后分离其地址相和数据相并且将地址和控制信号装入一个队列数据相单独装入另一个队列。同时要将得到的req打上标签set_id_info克隆为rsp用于反馈寄存器模型读数据hrdata如果是写操作当拍反馈rsp即调用seq_item_port.put_response(rsp)函数如果是读操作需要等到vif上正确的hrdata到来后修改rsp.hrdata随后反馈rsp。因此一笔由寄存器发起的read操作最快也要2拍才能完成hready为高时。
thread2drv_pkt_item(req)负责将得到的数据包按流水线的规则以及HREADY信号的高低发出并采集读数据hrdata。当该线程被触发后会根据3种情况判断走不同的分支
当前trans是否为第一笔传输当前trans是否为一系列传输的中间传输当前trans是否为一系列传输的最后一笔传输
如果为第一笔传输则将其地址和控制相发送至总线上数据相不发。此刻就完成了地址相和数据相的错位操作。同时将sop_cnt从1改为2用于标记非第一笔trans。
如果为中间传输或者是最后一笔传输则根据HREADY信号的高低将错位后的地址和控制相一并发送至总线上。
当数据队列的size0并且地址队列的size0时表示此时只剩下HWDATA信号没有驱动即为最后一笔传输只需要将写数据hwdata驱动至总线上即可并且将sop_cnt从2改为1用于标记下次传输为第一笔trans。
2.3 driver代码
driver的具体实现代码如下
1.宏定义 2.driver classclass中定义了成员变量和方法 3.main_phase在main phase中有2个forever进程get_pkt_item(got_pkt)drv_pkt_item()在begin end中顺序执行。 4. thread1: 在get_pkt_item进程中第一步首先driver采用try_get非阻塞方法反复从seqr获取数据每当得到一笔非空数据包req利用clone函数将其深复制为rsp随后采用set_id_info方法将rsp打上标签放入rsp_q队列中这一步是为了将每一笔得到的数据反馈给seqr或是寄存器模型的read/write方法。第二步将数据包拆包其地址和控制相放入队列haddr_hctrl_q中同时识别当前数据包是读还是写如果是写操作则将其写数据放入数据相队列hwdata_q中同时立刻返还rsp调用seq_item_port.put_response如果是读操作暂时不返还rsp。上述过程重复执行共2次队列中最多存放2笔未完成的数据。 5. thread2在drv_pkt_item进程中根据sop_cnt、haddr_hctrl_q队列和hwdata_q队列的size大小来识别当前trans属于第几笔传输在第一笔传输时只需将地址和控制相输出至总线上在随后的传输过程中根据HREADY信号高低决定地址控制相保留还是更新。在最后一笔传输时只将数据相传输至总线上并将hsel拉低。 在thread2的整个过程中若在地址相发现为有效的读操作则在数据相将读数据从总线上取得并更改rsp中的hrdata随后put_response返回给寄存器 第163~168行的代码对应UVM源码中的2028行此时若不返回rsp则会造成do_bus_read和driver之间的死锁仿真会在执行源码的2028行时卡死。 7.完整代码
/* -- -- File name : reg_cfg_driver.sv -- Author : IC_SH
-- Date : Sat Jan 7 10:36:57 CST 2023
-- Abstract : description of this reg_cfg_driver.sv -- */ ifndef _reg_cfg_driver_sv_ define _reg_cfg_driver_sv_
define _HREADY hready_m define _HRDATA hrdata
define haddr_hctrl_drv2bus(_HSEL,_HTRANS,_HSIZE,_HWRITE,_HADDR) \ vif._HSEL temp_haddr_hctrl._HSEL ; \ vif._HTRANS temp_haddr_hctrl._HTRANS; \ vif._HSIZE temp_haddr_hctrl._HSIZE ; \ vif._HWRITE temp_haddr_hctrl._HWRITE; \ vif._HADDR temp_haddr_hctrl._HADDR ; \
define hwdata_drv2bus(_HWDATA) \ vif._HWDATA temp_hwdata; \
define sample_rdata_func(_HWRITE) \ this.got_rsp_tmp this.got_rsp_q.pop_front(); \ if(this.got_rsp_tmp._HWRITE h0)begin \ this.sample_rdata 1; \ end \
typedef class reg_cfg_driver;
virtual class reg_cfg_driver_cb extends uvm_callback;//drv callback virtual task before_drv_item(reg_cfg_driver drv,reg_cfg_drv_pkt pkt); endtask:before_drv_item virtual task after_drv_item(reg_cfg_driver drv,reg_cfg_drv_pkt pkt); endtask:after_drv_item function new(string name reg_cfg_driver_cb); super.new(name); endfunction:new
endclass:reg_cfg_driver_cb
class reg_cfg_driver extends uvm_driver #(reg_cfg_drv_pkt); int sop_cnt ; uvm_event reg_cfg_drv_finished;//used for ctrl next seq in vseq virtual reg_cfg_if vif ;//used for data transferring from driver to DUT bit sample_rdata ; reg_cfg_config cfg ; reg_cfg_drv_pkt got_pkt ;//pkt that got from seq reg_cfg_drv_pkt got_rsp_q[$] ;//pkt that got from seq reg_cfg_drv_pkt got_rsp ;//pkt that got from seq reg_cfg_drv_pkt got_rsp_tmp ;//pkt that got from seq reg_cfg_drv_pkt haddr_hctrl_q[$] ;//addr and ctrl signal queue logic [31:0] hwdata_q[$] ;//wdata queue reg_cfg_drv_pkt temp_haddr_hctrl ; logic [31:0] temp_hwdata ; uvm_component_utils_begin(reg_cfg_driver) uvm_field_object(cfg,UVM_ALL_ON) uvm_field_object(got_pkt,UVM_ALL_ON) uvm_field_object(got_rsp,UVM_ALL_ON) uvm_field_object(haddr_hctrl_q[$],UVM_ALL_ON) uvm_field_object(temp_haddr_hctrl,UVM_ALL_ON) uvm_component_utils_end uvm_register_cb(reg_cfg_driver,reg_cfg_driver_cb) function new(string name reg_cfg_driver,uvm_component parent); super.new(name,parent); endfunction:new function void build_phase(uvm_phase phase); super.build_phase(phase); if (!uvm_config_db #(virtual reg_cfg_if) :: get(this,,vif,vif)) begin uvm_fatal(NOVIF,{virtual interface must be set for:,get_full_name(),.vif}) end if (!uvm_config_db #(reg_cfg_config) :: get(this,,cfg,cfg)) begin uvm_fatal(NOCFG,{reg_cfg_config must be set for:,get_full_name(),.cfg}) end reg_cfg_drv_finished uvm_event_pool::get_global(reg_cfg_drv_finished); endfunction:build_phase task reset_phase(uvm_phase phase); super.reset_phase(phase); reset_process(); endtask:reset_phase extern virtual task main_phase(uvm_phase phase); extern virtual function void report_phase(uvm_phase phase); extern virtual task reset_process(); extern virtual task get_pkt_item(reg_cfg_drv_pkt got_pkt);//get pkt from seq extern virtual task drv_pkt_item();//drive pkt to AHB bus extern virtual task delay_n_cyc(int N); endclass:reg_cfg_driver
task reg_cfg_driver::main_phase(uvm_phase phase); process job_id; super.main_phase(phase); wait(vif.rstn 1); // wait reset end,rst is low active forever begin fork begin job_id process::self(); forever begin get_pkt_item(got_pkt);//forever get_pkt loop drv_pkt_item();//forever drv_pkt loop delay_n_cyc(1); end end begin wait(vif.rstn 0); //detected reset end join_any //only wait rst could finish //kill other threads and wait reset end if (job_id ! null) begin job_id.kill(); end reset_process(); if (got_pkt ! null) begin seq_item_port.item_done(); got_pkt null; end wait (vif.rstn 1); repeat (10) (posedge vif.clk); end // forever begin endtask:main_phase
task reg_cfg_driver::get_pkt_item(reg_cfg_drv_pkt got_pkt); repeat(2)begin if(hwdata_q.size() 2)begin seq_item_port.try_next_item(got_pkt); //seq_item_port.get_next_item(got_pkt); if(got_pkt ! null)begin $cast(this.got_rsp,got_pkt.clone()); this.got_rsp.set_id_info(got_pkt); this.got_rsp_q.push_back(this.got_rsp); haddr_hctrl_q.push_back(got_pkt); if(got_pkt.hwrite 1b1)begin//only recieve the hwdata when hwrite 1b1 hwdata_q.push_back(got_pkt.hwdata); end else begin hwdata_q.push_back(h0); end uvm_info(REG_CFG_DRV,$sformatf(thread1: get pkt seq! haddr_hctrl_q.size() %0d, hwdata_q.size() %0d, haddr_hctrl_q.size(), hwdata_q.size()),UVM_HIGH) seq_item_port.item_done(); if(this.got_rsp.hwrite 1)begin seq_item_port.put_response(this.got_rsp); end got_pktnull; end end end endtask:get_pkt_item
task reg_cfg_driver::drv_pkt_item(); if(this.sample_rdata 1)begin this.got_rsp_tmp._HRDATA vif._HRDATA; seq_item_port.put_response(this.got_rsp_tmp); this.sample_rdata 0; end if((this.sop_cnt 1) (haddr_hctrl_q.size() 0))begin//the first data has been drived temp_haddr_hctrl haddr_hctrl_q.pop_front(); haddr_hctrl_drv2bus(hsel,htrans,hsize,hwrite,haddr) uvm_info(REG_CFG_DRV,thread2: drv first seq!,UVM_HIGH) this.sop_cnt ; uvm_info(REG_CFG_DRV,$sformatf(thread2: sop_cnt %0d,sop_cnt),UVM_HIGH) end else if((haddr_hctrl_q.size() 0) (haddr_hctrl_q.size() hwdata_q.size()))begin//the middle data has been drived uvm_info(REG_CFG_DRV,$sformatf(thread2: vif.hready %0d,vif._HREADY),UVM_HIGH) if(vif._HREADY 1b1)begin temp_haddr_hctrl haddr_hctrl_q.pop_front(); temp_hwdata hwdata_q.pop_front(); sample_rdata_func(hwrite) end haddr_hctrl_drv2bus(hsel,htrans,hsize,hwrite,haddr) hwdata_drv2bus(hwdata) uvm_info(REG_CFG_DRV,thread2: drv middle seq!,UVM_HIGH) end else if((haddr_hctrl_q.size() 0) (hwdata_q.size() 1))begin//the last data has been drived. uvm_info(REG_CFG_DRV,$sformatf(thread2: vif.hready %0d,vif._HREADY),UVM_HIGH) if(vif._HREADY 1b1)begin temp_haddr_hctrl.hsel h0; temp_haddr_hctrl.haddr h0; temp_haddr_hctrl.htrans h0; temp_haddr_hctrl.hwrite h0; temp_haddr_hctrl.hsize h0; temp_hwdata hwdata_q.pop_front() ; sample_rdata_func(hwrite) end haddr_hctrl_drv2bus(hsel,htrans,hsize,hwrite,haddr) hwdata_drv2bus(hwdata) uvm_info(REG_CFG_DRV,thread2: drv last seq!,UVM_HIGH) this.sop_cnt h1; reg_cfg_drv_finished.trigger(); end endtask:drv_pkt_item
task reg_cfg_driver::reset_process(); vif.hsel h0; vif.haddr h0; vif.htrans h0; vif.hwrite h0; vif.hsize h0; vif.hwdata h0; sop_cnt h1; endtask:reset_process
function void reg_cfg_driver::report_phase(uvm_phase phase); endfunction:report_phase
task reg_cfg_driver::delay_n_cyc(int N); repeat (N) begin (posedge vif.clk); end endtask:delay_n_cyc
undef _HREADY undef _HRDATA undef haddr_hctrl_drv2bus undef hwdata_drv2bus undef sample_rdata_func endif
2.4 仿真log与波形 2.5 多级流水拓展方法
多级流水同样可以采取该种方法举个例子如果是3级流水只需要在代码中根据haddr_hctrl_q队列和hwdata_q队列的size大小识别到第一笔传输、第二笔传输、中间传输、倒数第二笔传输、最后一笔传输然后根据HREADY的高低决定是否更新地址和数据相即可。