[CU]reg model使用篇-uvm_reg常用操作part3(XatomicX/read/write(frontdoor/backdoor))
1. XatomicX
(1) 当要访问互斥的资源时,即只允许一个进程访问,其他进程必须在此进行访问完成之后才能访问,可以使用XatomicX(1) + access the resource +XatomicX(0)实现;
(2) read, write, poke, peek, mirror等操作中,都用类似的方法保证同一时间只有一个进程对互斥资源进行访问;
注1: register model可以由多个线程并行访问,但是register model内部会串行处理这些进程,这样做是为了确保当同一个register field被访问时,其隐式更新的镜像值可预测;
注2: 每一个register内都有一个旗语m_atomic来确保它同时只能被一个进程访问; 如果一个线程在执行过程中,被显式杀掉, 有必要调用uvm_reg::reset()释放掉它所占用的key;
注3:在实际仿真过程中,发现采用寄存器后门读一个uvm_reg_field时,没有立即返回读取值,而是隔了很久,就是因为在XatomicX中获取旗语时hang住;hang住的原因是在另一处调用了前门操作对该uvm_reg_field所在uvm_reg的另一个uvm_reg_field进行写操作;
1 local semaphore m_atomic; 2 local process m_process; 3 4 function void uvm_reg::reset(string kind = "HARD"); 5 foreach (m_fields[i]) 6 m_fields[i].reset(kind); 7 // Put back a key in the semaphore if it is checked out 8 // in case a thread was killed during an operation 9 void'(m_atomic.try_get(1)); 10 m_atomic.put(1); 11 m_process = null; 12 Xset_busyX(0); 13 endfunction: reset 14 15 task uvm_reg::XatomicX(bit on); 16 process m_reg_process; 17 m_reg_process=process::self(); 18 19 if (on) begin 20 if (m_reg_process == m_process) 21 return; 22 m_atomic.get(1); 23 m_process = m_reg_process; 24 end 25 else begin 26 // Maybe a key was put back in by a spurious call to reset() 27 void'(m_atomic.try_get(1)); 28 m_atomic.put(1); 29 m_process = null; 30 end 31 endtask: XatomicX
2. read/write (frontdoor)
1 regmodel.register.read(status,value,UVM_FRONTDOOR,.parent(this)); 2 regmodel.register.write(status,value,UVM_FRONTDOOR,.parent(this));
2.1 read/write (frontdoor)操作的影响
(1) 当使用front-door(path=BFM), 一个或多个实际的transaction会发往DUT进行register的读写;
(2) 无论通过前门访问还是后门访问的方式从DUT中读取或写入寄存器的值,在操作完成后,寄存器模型都会根据读写的结果更新期望值和镜像值(其实是有前提条件的,对于后门访问,会自动更新镜像值和期望值; 对于前门访问,更新镜像值和期望值有两种方法: (a) auto_predict功能需要打开,即在env中调用uvm_reg_map.set_auto_predict(1),然后调用do-predict函数更新寄存器模型的mirror值; (b)关闭auto_predict功能,使用uvm_reg_predictor进行更新);
(3) 如果寄存器map到多个uvm_address_map,在调用uvm_reg的write/read task进行前门访问时,需要指定uvm_reg_map参数;
2.2 write (frontdoor)的源码
(1) uvm_reg::write
(1.1) FRONTDOOR write操作最终会转换为uvm_reg_map的do_write任务;
(1.2) uvm_reg_map的do_write任务会查看系统是否设置了adapter,如果没有设置,就直接启动sequence, 让sequencer发送uvm_reg_item类型的transaction;如果设置了,那就调用do_bus_write任务.
(1.3) uvm_reg_map的do_write完成后,如果auto predict功能打开了, uvm_reg的do_write会根据写入的值更新register model中寄存器的值;
1 task uvm_reg::write(output uvm_status_e status, 2 input uvm_reg_data_t value, 3 input uvm_path_e path = UVM_DEFAULT_PATH, 4 input uvm_reg_map map = null, 5 input uvm_sequence_base parent = null, 6 input int prior = -1, 7 input uvm_object extension = null, 8 input string fname = "", 9 input int lineno = 0); 10 11 // create an abstract transaction for this operation 12 uvm_reg_item rw; 13 14 XatomicX(1); 15 16 set(value); 17 18 rw = uvm_reg_item::type_id::create("write_item",,get_full_name()); 19 rw.element = this; 20 rw.element_kind = UVM_REG; 21 rw.kind = UVM_WRITE; 22 rw.value[0] = value; 23 rw.path = path; 24 rw.map = map; 25 rw.parent = parent; 26 rw.prior = prior; 27 rw.extension = extension; 28 rw.fname = fname; 29 rw.lineno = lineno; 30 31 do_write(rw); 32 33 status = rw.status; 34 35 XatomicX(0); 36 37 endtask
(2) uvm_reg::do_write
1 task uvm_reg::do_write (uvm_reg_item rw); 2 3 uvm_reg_cb_iter cbs = new(this); 4 uvm_reg_map_info map_info; 5 uvm_reg_data_t value; 6 7 m_fname = rw.fname; 8 m_lineno = rw.lineno; 9 10 if (!Xcheck_accessX(rw,map_info,"write()")) 11 return; 12 13 XatomicX(1); 14 15 m_write_in_progress = 1'b1; 16 17 rw.value[0] &= ((1 << m_n_bits)-1); 18 value = rw.value[0]; 19 20 rw.status = UVM_IS_OK; 21 22 // PRE-WRITE CBS - FIELDS 23 begin : pre_write_callbacks 24 ... 25 end 26 rw.element = this; 27 rw.element_kind = UVM_REG; 28 rw.value[0] = value; 29 30 // PRE-WRITE CBS - REG 31 pre_write(rw); 32 for (uvm_reg_cbs cb=cbs.first(); cb!=null; cb=cbs.next()) 33 cb.pre_write(rw); 34 35 if (rw.status != UVM_IS_OK) begin 36 m_write_in_progress = 1'b0; 37 38 XatomicX(0); 39 40 return; 41 end 42 43 // EXECUTE WRITE... 44 case (rw.path) 45 46 // ...VIA USER BACKDOOR 47 UVM_BACKDOOR: begin 48 ... 49 end 50 51 UVM_FRONTDOOR: begin 52 53 uvm_reg_map system_map = rw.local_map.get_root_map(); 54 55 m_is_busy = 1; 56 57 // ...VIA USER FRONTDOOR 58 if (map_info.frontdoor != null) begin 59 uvm_reg_frontdoor fd = map_info.frontdoor; 60 fd.rw_info = rw; 61 if (fd.sequencer == null) 62 fd.sequencer = system_map.get_sequencer(); 63 fd.start(fd.sequencer, rw.parent); 64 end 65 66 // ...VIA BUILT-IN FRONTDOOR 67 else begin : built_in_frontdoor 68 69 rw.local_map.do_write(rw); 70 71 end 72 73 m_is_busy = 0; 74 75 if (system_map.get_auto_predict()) begin 76 uvm_status_e status; 77 if (rw.status != UVM_NOT_OK) begin 78 sample(value, -1, 0, rw.map); 79 m_parent.XsampleX(map_info.offset, 0, rw.map); 80 end 81 82 status = rw.status; // do_predict will override rw.status, so we save it here 83 do_predict(rw, UVM_PREDICT_WRITE); 84 rw.status = status; 85 end 86 end 87 88 endcase 89 90 ... 91 XatomicX(0); 92 93 endtask: do_write
(3) uvm_reg_map.do_write
(3.1) 24到26行把要写的item通过sequencer发送出去,27行调用rw.end_event的wait_on;
(3.2) sequence.finish_item task中会调用sequencer.end_tr,而sequencer.end_tr会在driver调用item_done后结束;
(3.3) sequencer.end_tr内会触发end_event事件;
1 task uvm_reg_map::do_write(uvm_reg_item rw); 2 3 uvm_sequence_base tmp_parent_seq; 4 uvm_reg_map system_map = get_root_map(); 5 uvm_reg_adapter adapter = system_map.get_adapter(); 6 uvm_sequencer_base sequencer = system_map.get_sequencer(); 7 8 if (adapter != null && adapter.parent_sequence != null) begin 9 uvm_object o; 10 uvm_sequence_base seq; 11 o = adapter.parent_sequence.clone(); 12 assert($cast(seq,o)); 13 seq.set_parent_sequence(rw.parent); 14 rw.parent = seq; 15 tmp_parent_seq = seq; 16 end 17 18 if (rw.parent == null) begin 19 rw.parent = new("default_parent_seq"); 20 tmp_parent_seq = rw.parent; 21 end 22 23 if (adapter == null) begin 24 rw.set_sequencer(sequencer); 25 rw.parent.start_item(rw,rw.prior); 26 rw.parent.finish_item(rw); 27 rw.end_event.wait_on(); 28 end 29 else begin 30 do_bus_write(rw, sequencer, adapter); 31 end 32 33 if (tmp_parent_seq != null) 34 sequencer.m_sequence_exiting(tmp_parent_seq); 35 36 endtask
2.3 read (frontdoor)源码
1 task uvm_reg::read(output uvm_status_e status, 2 output uvm_reg_data_t value, 3 input uvm_path_e path = UVM_DEFAULT_PATH, 4 input uvm_reg_map map = null, 5 input uvm_sequence_base parent = null, 6 input int prior = -1, 7 input uvm_object extension = null, 8 input string fname = "", 9 input int lineno = 0); 10 XatomicX(1); 11 XreadX(status, value, path, map, parent, prior, extension, fname, lineno); 12 XatomicX(0); 13 endtask: read
1 task uvm_reg::XreadX(output uvm_status_e status, 2 output uvm_reg_data_t value, 3 input uvm_path_e path, 4 input uvm_reg_map map, 5 input uvm_sequence_base parent = null, 6 input int prior = -1, 7 input uvm_object extension = null, 8 input string fname = "", 9 input int lineno = 0); 10 11 // create an abstract transaction for this operation 12 uvm_reg_item rw; 13 rw = uvm_reg_item::type_id::create("read_item",,get_full_name()); 14 rw.element = this; 15 rw.element_kind = UVM_REG; 16 rw.kind = UVM_READ; 17 rw.value[0] = 0; 18 rw.path = path; 19 rw.map = map; 20 rw.parent = parent; 21 rw.prior = prior; 22 rw.extension = extension; 23 rw.fname = fname; 24 rw.lineno = lineno; 25 26 do_read(rw); 27 28 status = rw.status; 29 value = rw.value[0]; 30 31 endtask: XreadX
3. read/write (backdoor)
(1) 当使用back-door(path=BACKDOOR)时, 会通过uvm_hdl_read/uvm_hdl_deposit函数获取或修改DUT register值,而不会通过物理上的interface;
(2) 但是back-door的访问方式会尽量模拟前门访问的行为,比如如果对一个只读寄存器进行后门写操作,由于要模拟DUT的只读行为,所以是写不进去的;
(3) 无论通过前门访问还是后门访问的方式,从DUT中读取或写入寄存器的值,在操作完成后,寄存器模型都会根据读写的结果更新期望值和镜像值(对于前门访问而言,前提条件是system_map.get_auto_predict为1);
【推荐】国内首个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吗?