[CU]reg model构建篇-uvm_reg_predictor(与前门访问相关)
参考资料:
(1) Explicit Register Predictor (verificationacademy.com)
(2) 《uvm_cookbook》;
(3) SystemVerilo | UVM | 精讲RAL寄存器模型基础 (qq.com)
1.简介及uvm_reg_predictor源码
(1) uvm_predictor派生于uvm_subscriber(属于uvm_component组件类),并且是一个参数化的类,其参数类型为target bus analysis transaction;
(2) 使用uvm_predictor时,不需要对其进行派生出新类,只需要传递正确的参数类型;
(3) 使用uvm_predictor时,需要指定其uvm_reg_adapter及uvm_reg_map变量; uvm_predictor使用uvm_reg_adapter将monitor传递来的bus transaction转换成uvm_reg_item, 然后使用uvm_reg_map根据uvm_reg_item中的address信息找到对应的register,并update其内容,比如镜像值;
(4) Predictor是保持寄存器模型“实时”复刻RTL寄存器值的关键组件。
1 class uvm_predict_s; 2 bit addr[uvm_reg_addr_t]; 3 uvm_reg_item reg_item; 4 endclass 5 6 //------------------------------------------------------------------------------ 7 // 8 // CLASS: uvm_reg_predictor 9 // 10 // Updates the register model mirror based on observed bus transactions 11 // 12 // This class converts observed bus transactions of type ~BUSTYPE~ to generic 13 // registers transactions, determines the register being accessed based on the 14 // bus address, then updates the register's mirror value with the observed bus 15 // data, subject to the register's access mode. See <uvm_reg::predict> for details. 16 // 17 // Memories can be large, so their accesses are not predicted. 18 // 19 //------------------------------------------------------------------------------ 20 21 class uvm_reg_predictor #(type BUSTYPE=int) extends uvm_component; 22 23 `uvm_component_param_utils(uvm_reg_predictor#(BUSTYPE)) 24 25 uvm_analysis_imp #(BUSTYPE, uvm_reg_predictor #(BUSTYPE)) bus_in; 26 uvm_analysis_port #(uvm_reg_item) reg_ap; 27 28 // Variable: map 29 // The map used to convert a bus address to the corresponding register 30 // or memory handle. Must be configured before the run phase. 31 uvm_reg_map map; 32 33 // The adapter used to convey the parameters of a bus operation in 34 // terms of a canonical <uvm_reg_bus_op> datum. 35 // The <uvm_reg_adapter> must be configured before the run phase. 36 uvm_reg_adapter adapter; 37 38 function new (string name, uvm_component parent); 39 super.new(name, parent); 40 bus_in = new("bus_in", this); 41 reg_ap = new("reg_ap", this); 42 endfunction 43 44 // This method is documented in uvm_object 45 static string type_name = ""; 46 virtual function string get_type_name(); 47 if (type_name == "") begin 48 BUSTYPE t; 49 t = BUSTYPE::type_id::create("t"); 50 type_name = {"uvm_reg_predictor #(", t.get_type_name(), ")"}; 51 end 52 return type_name; 53 endfunction 54 55 virtual function void pre_predict(uvm_reg_item rw); 56 endfunction 57 58 local uvm_predict_s m_pending[uvm_reg]; 59 60 virtual function void write(BUSTYPE tr); 61 uvm_reg rg; 62 uvm_reg_bus_op rw; 63 if (adapter == null) 64 `uvm_fatal("REG/WRITE/NULL","write: adapter handle is null") 65 66 // In case they forget to set byte_en 67 rw.byte_en = -1; 68 adapter.bus2reg(tr,rw); 69 rg = map.get_reg_by_offset(rw.addr, (rw.kind == UVM_READ)); 70 71 // ToDo: Add memory look-up and call <uvm_mem::XsampleX()> 72 73 if (rg != null) begin 74 bit found; 75 uvm_reg_item reg_item; 76 uvm_reg_map local_map; 77 uvm_reg_map_info map_info; 78 uvm_predict_s predict_info; 79 uvm_reg_indirect_data ireg; 80 uvm_reg ir; 81 82 if (!m_pending.exists(rg)) begin 83 uvm_reg_item item = new; 84 predict_info =new; 85 item.element_kind = UVM_REG; 86 item.element = rg; 87 item.path = UVM_PREDICT; 88 item.map = map; 89 item.kind = rw.kind; 90 predict_info.reg_item = item; 91 m_pending[rg] = predict_info; 92 end 93 predict_info = m_pending[rg]; 94 reg_item = predict_info.reg_item; 95 96 if (predict_info.addr.exists(rw.addr)) begin 97 `uvm_error("REG_PREDICT_COLLISION",{"Collision detected for register '", 98 rg.get_full_name(),"'"}) 99 // TODO: what to do with subsequent collisions? 100 m_pending.delete(rg); 101 end 102 103 local_map = rg.get_local_map(map,"predictor::write()"); 104 map_info = local_map.get_reg_map_info(rg); 105 ir=($cast(ireg, rg))?ireg.get_indirect_reg():rg; 106 107 foreach (map_info.addr[i]) begin 108 if (rw.addr == map_info.addr[i]) begin 109 found = 1; 110 reg_item.value[0] |= rw.data << (i * map.get_n_bytes()*8); 111 predict_info.addr[rw.addr] = 1; 112 if (predict_info.addr.num() == map_info.addr.size()) begin 113 // We've captured the entire abstract register transaction. 114 uvm_predict_e predict_kind = 115 (reg_item.kind == UVM_WRITE) ? UVM_PREDICT_WRITE : UVM_PREDICT_READ; 116 117 if (reg_item.kind == UVM_READ && 118 local_map.get_check_on_read() && 119 reg_item.status != UVM_NOT_OK) begin 120 void'(rg.do_check(ir.get_mirrored_value(), reg_item.value[0], local_map)); 121 end 122 123 pre_predict(reg_item); 124 125 ir.XsampleX(reg_item.value[0], rw.byte_en, 126 reg_item.kind == UVM_READ, local_map); 127 begin 128 uvm_reg_block blk = rg.get_parent(); 129 blk.XsampleX(map_info.offset, 130 reg_item.kind == UVM_READ, 131 local_map); 132 end 133 134 rg.do_predict(reg_item, predict_kind, rw.byte_en); 135 if(reg_item.kind == UVM_WRITE) 136 `uvm_info("REG_PREDICT", {"Observed WRITE transaction to register ", 137 ir.get_full_name(), ": value='h", 138 $sformatf("%0h",reg_item.value[0]), " : updated value = 'h", 139 $sformatf("%0h",ir.get())},UVM_HIGH) 140 else 141 `uvm_info("REG_PREDICT", {"Observed READ transaction to register ", 142 ir.get_full_name(), ": value='h", 143 $sformatf("%0h",reg_item.value[0])},UVM_HIGH) 144 reg_ap.write(reg_item); 145 m_pending.delete(rg); 146 end 147 break; 148 end 149 end 150 if (!found) 151 `uvm_error("REG_PREDICT_INTERNAL",{"Unexpected failed address lookup for register '", 152 rg.get_full_name(),"'"}) 153 end 154 else begin 155 `uvm_info("REG_PREDICT_NOT_FOR_ME", 156 {"Observed transaction does not target a register: ", 157 $sformatf("%p",tr)},UVM_FULL) 158 end 159 endfunction 160 161 162 // Function: check_phase 163 // Checks that no pending register transactions are still queued. 164 virtual function void check_phase(uvm_phase phase); 165 ... 166 endfunction 167 168 endclass
2. 前门读操作提供返回值的两种方式
2.1 auto_predict功能(没有使用monitor的implict prediction)
(1) rm.default_map.set_auto_predict(1);
(2) 能够这样操作的原因在于: 由于总线的特殊性,bus_driver在驱动总线进行读操作时,它也能顺便获取要读的数值,如果它将此值放入从bus_sequencer获得的bus_transaction中,那么bus_transaction中就会有读取的值,此值经过adapter的bus2reg函数的传递,最终被寄存器模型获取;
2.2 依靠monitor与uvm_reg_predictor的非auto_predict功能(使用monitor的explict prediction)
(1) monitor将从总线上收集到的transaction交给寄存器模型,后者更新相应寄存器的值.
3. auto_predict读操作返回值详细分析
(1) 由于总线的特殊性, bus_driver在驱动总线进行读操作时,也能顺便获取要读的数值; 如果将该值放入从bus_sequencer获得的bus_transaction中,那么bus_transaction中就会有读取的值; 此值经过adapter的bus2reg函数的传递,最终被寄存器模型获取,寄存器模型会更新寄存器的镜像值和期望值,这就是寄存器模型的auto predict功能, 如下图虚线所示;
(2) auto predict功能的具体应用
注1:寄存器模型的前门访问操作最终都将由uvm_reg_map完成,因此在connect_phase中,需要将转换器uvm_reg_adapter和bus_sequencer通过set_sequencer函数告知reg_model的default_map,并将default_map设置为自动预测状态;
1 //示例1 2 class base_test extends uvm_test; 3 my_env env; 4 my_vsqr vsqr; 5 reg_model rm; 6 my_adapter reg_sqr_adapter 7 ... 8 endclass 9 10 function void base_test::build_phase(uvm_phase phase); 11 super.build_phase(phase); 12 env=my_env::type_id::create("env",this); 13 vsqr=my_vsqr::type_id::create("vsqr",this); 14 15 rm=reg_model::type_id::create("rm",this); 16 rm.configure(null,""); 17 rm.build(); 18 rm.lock_model(); 19 rm.reset(); 20 21 reg_sqr_adapter=new("reg_sqr_adapter"); 22 env.p_rm=this.rm; 23 endfunction 24 25 function void base_test::connect_phase(uvm_phase phase); 26 super.connect_phase(phase); 27 vsqr.p_my_sqr=env.i_agt.sqr; 28 vsqr.p_bus_sqr=env.bus_agt.sqr; 29 vsqr.p_rm=this.rm; 30 31 rm.default_map.set_sequencer(env.bus_agt.sqr,reg_sqr_adapter); 32 rm.default_map.set_auto_predict(1); 33 34 endfunction
4. 依赖monitor与uvm_reg_predictor的读操作返回值
(1) 该方法的原理如下图中的右图所示; 使用这种方式更新数据,需要设置reg_predictor的adapter和map(只有设置了map,才能将predictor和寄存器模型关联在一起),并且需要将reg_predictor和bus_agt的ap口连接在一起;
(2) 当总线上可能有众多设备发起读写操作时,有一个monitor是非常有必要的;
(3) 使用示例
注1:在该代码示例中,存在着两条更新寄存器模型的路径: 一是左侧虚线所示的自动预测路径,二是经由predictor的途径; 如果要彻底关掉自动预测的路径,需要set_auto_predict(0);
1 //示例1 2 class base_test extends uvm_test; 3 ... 4 reg_model rm; 5 my_adapter reg_sqr_adapter; 6 //adapter for uvm_reg_predictor; 7 my_adapter mon_reg_adapter; 8 uvm_reg_predictor #(bus_transaction) reg_predictor; 9 ... 10 endclass 11 12 function void base_test::build_phase(uvm_phase phase); 13 ... 14 rm=reg_model::type_id::create("rm",this); 15 rm.configure(null,""); 16 rm.build(); 17 rm.lock_model(); 18 rm.reset(); 19 20 reg_sqr_adapter=new("reg_sqr_adapter"); 21 mon_reg_adapter=new("mon_reg_adapter"); 22 reg_predictor=new("reg_predictor",this); 23 env.p_rm=this.rm; 24 endfunction 25 26 function void base_test::connect_phase(uvm_phase phase); 27 ... 28 rm.default_map.set_sequencer(env.bus_agt.sqr,reg_sqr_adapter); 29 //读操作返回值方式1:auto predict; 30 rm.default_map.set_auto_predict(1); 31 //读操作返回值方式2:monitor & uvm_reg_predictor; 32 reg_predictor.map=rm.default_map; 33 reg_predictor.adapter=mon_reg_adapter; 34 env.bus_agt.ap.connect(reg_predictor.bus_in); 35 endfunction
1 //示例2 2 class environment extends uvm_env; 3 typedef uvm_reg_predictor #(master_data) mreg_predictor; 4 mreg_predictor mreg_predict; 5 6 virtual function void build_phase(uvm_phase phase); 7 mreg_predict=mreg_predictor::type_id::create("mreg_predict",this); 8 endfunction 9 10 virtual function void connect_phase(uvm_phase phase); 11 mreg_predict.map=regmodel.get_default_map(); 12 mreg_predict.adapter=adapter; 13 regmodel.default_map.set_auto_predict(0); 14 m_agent.analysis_port.connect(mreg_predict.bus_in); 15 endfunction 16 endclass
5. 两种读操作返回值方法的区别
(1) 当总线上只有一个主设备时,两种读操作返回值方法等价; 如果有多个主设备,自动预测的方法会漏掉某些transaction;
(2) Explicit Prediction相对于Implicit Prediction,除了监视通过寄存器模型API对寄存器的访问操作,还可以覆盖到其他测试序列(sequence)通过总线对寄存器的直接访问,这一点会使它更加通用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?