[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_adapterbus_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)通过总线对寄存器的直接访问,这一点会使它更加通用。

 

posted on   知北游。。  阅读(2500)  评论(0编辑  收藏  举报

编辑推荐:
· .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吗?
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示