资讯详情

UVM--寄存器模型的常规方法

1 常规的寄存器模型方法

mirrored、 desired 和 actual value

在应用寄存器模型时, 除使用其寄存器信息外, 还可以利用它来跟踪寄存器的值。 跟踪寄存器的值, 一方面是建立 mirrored value, 另一方面是建立 desired value。寄存器模型中的每一个寄存器都应该有两个值,一个是镜像值(mirrored value),一是期望值 (desired value) 。 。 通常通过模型预测给出镜像值, 在前门访问时,通过观察总线或在后门访问时通过自动预测给出镜像值。 ,例如。 另外, 如果其他访问寄存器的通道修改了寄存器, 也许是因为那条路的总线没有被监控, 寄存器的镜像值没有及时更新。

UVM 提供了两种跟踪寄存器值的方法, 我们将其分为自动预测 (auto prediction)和显式预测 (explicit)。 如果读者想使用自动预测,还需要调用函数 。两种预测方法的显著区别在于, 显式预测对寄存器的数值预测更准确, 通过以下两种模式的分析,我们可以得出具体的原因。

:如果读者在环境中没有独立集成 predictor, 而是利用寄存器的操作自动记录每个寄存器的读写值, 并在后台自动调用 predict()方法, 则这种方式称为自动预测。这种方法简单有效, 但是要注意, 若有其他一些sequence 直接在总线层面操作寄存器(跳过寄存器级别) write()/read()操作), 或者通过其他总线访问寄存器等额外情况, 寄存器的镜像值和预期值无法自动获得。

:一种更可靠的方法是通过监视器在物理总线上捕捉总线事务, 。 如下图, 在集成过程中需要 adapter 与 map 句柄也一起传递 predictor, 同时将 monitor 收集的事务通过 analysis port 接入到 predictor一侧。这种集成关系可以使 monitor一旦有效事务被捕获, 会发送给 predictor, 再由其利用adapter桥接方法, 实现事务信息转换, 并将转换后的寄存器模型相关信息更新到 map 中。 默认情况下,系统将采用显式预测, 这就要求集成到环境中的总线 UVC monitor 需要捕捉事务 对应的功能 analysis port, 以便于同 predictor 连接。

关于 predictor 集成在顶层环境中, 在集成过程中,读者可以通过以下例码片段来掌握几个元素:

class mcdf_bus_env extends uvm_env; mcdf_bus_agent agent; mcdf_rgm rgm;  reg2mcdf_adapter reg2mcdf;  uvm_reg_predictor #(mcdf_bus_trans) mcdf2reg_predictor;  `uvm_component_utils(mcdf_bus_env) ... function void build_phase(uvm_phase phase);  agent=mcdf_bus_agent::type_id::create("agent", this);  if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin `uvm_info("GETRGM", "no top-down RGM handle is assigned", UVM_LOW)  rgm = mcdf_rgm::type_id::create("rgm", this);  `uvm_info("NEWRGM", "created rgm instance locally", UVM_LOW) end  rgm. build() ;  reg2mcdf = reg2mcdf_adapter::type_id::create("reg2mcdf");  mcdf2reg_predictor = uvm_reg_predictor#(mcdf_bus_trans)::type_ id: : create ("mcdf2reg_predcitor", this);  mcdf2reg_predictor.map = rgm.map;  mcdf2reg_predictor.adapter = reg2mcdf;  endfunction  function void connect_phase(uvm_phase phase); rgm.map.set_sequencer(agent.sequencer, reg2mcdf);  agent.monitor.ap.connect(mcdf2reg_predictor.bus_in); endfunction  endclass 

uvm_reg的访问方法

在给出寄存器模型的常见应用模式之前,首先从下表中更全面地了解它uvm_reg_ block、 uvm_reg 和uvm_reg_ field 访问寄存器的方法有三类提供。

uvm_reg_ sequence (均为寄存器对象, 如下表所示,而不是寄存器块或寄存器域。

结合mirrored value、 desired value 和 actual value, 调用时需要了解这四种方法, 变化时序关系的三个值:

? 前门访问 read()和 write(), ?

关于 reset()和 get_reset()用法, 下面还给出了一些例码。 例如,当硬件复位触发时, 复位内部寄存器值, 当寄存器模型捕获复位事件时, 为了与硬件行为保持一致, 也要复位。 这里注意的是,

@(negedge p_sequencer.vif.rstn); rgm. reset(); / / register block reset for mirrored value and desired value

rgm.chnl0_ctrl_reg.reset(); // register level reset rgm.chnl0_ctrl_reg.pkt_len.reset(); // register field reset

// register model reset value get and check rstval = rgm.chnl0_ctrl_reg. get_reset() ; rgm.chnl0_ctrl_reg.read (status, data, UVM_BACKDOOR, .parent(this));

if(rstval != data)

`uvm_error ("RSTERR", "reset value read is not the desired reset value")

mirror()方法与 read()方法相似, 也可选择前门访问或后门访问, 不同的是,。 在修改镜像值之前, 用户还可以选择是否将读回的值与模型中的原镜像值进行比较。 下面的例码在更新镜像值之前, 首先将读回的值与上一次镜像值做了比对, 随后再更新镜像值。 比如, 对于配置寄存器, 可以采用这种方法来检查上一次的配置是否生效, 又或者对于状态寄存器可以选择只更新镜像值不做比较, 这是因为状态寄存器随时可能被硬件内部逻辑修改。

// get register value and check  rgm.chnl0_ctrl_reg.mirror(status, UVM_CHECK, UVM_FRONTDOOR,parent(this));

下面的方法是运用 set()和 update()对寄存器做批量修改。 。这种 set()和 update()的方式较 write()和 poke()的写寄存器方式更为灵活的是,它可以实现随机化寄存器配置值(先随机化寄存器模型,后将随机值结合某些域的指定值写入到寄存器),继而模拟更多不可预知的寄存器应用场景。 另外, update()强大的批量操作寄存器功能使得修改寄存器更为便捷。 

// randomize register model, set register/field value and update to 
// hardware actual value 
void'(rgm. chnlO_ctrl_reg. randomize()); 
rgm.chnlO ctrl reg.pkt len.set('h3); 
rgm.chnlO ctrl reg. update (status, UVM FRONTDOOR, .parent (this)); void'(rgm.chnll_ctrl_reg.randomize()); 
rgm. chnl0_ctrl_reg. set ('h22); 
rgm.update(status, UVM FRONTDOOR, .parent(this)); 

mem与reg的联系和差别 

UVM寄存器模型也可以用来对存储建模。uvm_mem 类可以用来模拟RW (读写)、 RO(只读)和WO(只写)类型的存储,并且可以配置存储模型的数据宽度和地址范围。 uvm_mem不同于uvm_reg 的地方在于,考虑到物理存储一旦映射到 uvm_mem 会带来更大的资源消耗,因此 uvm_mem 并不支待预测和影子存储(shadow storage) 功能, 即没有镜像值和期望值。uvm_mem 可以提供的功能就是利用自带的方法去访问硬件存储, 相比于直接利用硬件总线 UVC进行访问,这么做的好处在于:

•    类似于寄存器模型访问寄存器, •    在访问过程中,。 •    由于 uvm_mem 也同时提供前门访问和后门访问,这使得存储测试可以考虑先通过后门访问预先加载存储内容,而后通过前门访问读取存储内容,继而做数据比对,这样做不但节省时间,同时也在测试方式上保持了一致性。同时这种方式相比于传统前后测试方式(利用系统函数或仿真器函数实现存储加载),要在UVM测试框架中更为统一。

与 uvm_reg 相比, 之所以额外提供这两种方法,。 要实现BURST访问形式, 需要考虑下面这些因素:

•    目前挂载的总线UVC是否支持BURST形式访问,例如APB不能支持BURST访问模式。

•    与 read()、 write()方法相比,burst_read()和 burst_write()的参数列表中的一项uvm_reg_ data _t value[]采用的是数组形式, 不再是单一变量, 即表示用户可以传递多个数据。 而在后台,这些数据首先需要装载到 uvm_reg_item 对象中,装载时 value 数组可以直接写入, 另外两个成员需要分别指定为 element_kind = UVM_MEM , kind = UVM_BURST_READ。

•    在 adapter 实现中, 也需要考虑到存储模型 BURST 访问的情形, 实现 4 种访问类型(uvm _access_ e) 的转换,即UVM_READ、UVM_WRITE、 UVM_BURS_READ 和UVM_BURST_ WRITE。对于 UVM_READ 和 UVM_WRITE 的桥接, 已经在寄存器模型访问中实现, 而 UVM_BURST_READ 和 UVM_BURST_WRITE的转换,, 例如长度是否是 4、 8、 16 或其他。 比如 AHB 总线, 支持连续 4 个、8 个、 16 个数据的读写 (INCR4 、 INCR8、 INCR16), 但是如果数据长度不是这些固定长度时, adapter 还需要自己处理来实现 INCR 的连续访问方式。

•    此外还需要考虑不同总线的其他控制参数, 例如 AHB 支持 WRAP 模式, AXI 支持out-of-order 模式等, 如果想要将更多的总线控制封装在 adapter 的桥接功能里, 需要将更多的配置作为扩展配置, 在调用访问方法时作为扩展信息类,传入到形式参数uvm_object_extension。待传入后, adapter 将可以在桥接方法中抽取出扩展信息类,作为更准确的协议访问的限定依据。

•     对于更为复杂的 BURST 形式, 如果需要实现更多的协议配置要求, 那么笔者推荐直接在总线 UVC 层面去驱动。这样做的灵活性更大, 且更能充分全面的测试存储接口的协议层完备性。因此, 验证师在为存储模型访问实现 adapter 方法时, 需要考虑的是, uvm_mem 层面的方法应该尽员便捷、必要的参数应该少量, 以便于使用和维护; 而另一方面, 如果要首先测试存储接口协议, 则应该在总线 UVC 的层面上完成更充分的验证。

内建sequences

不少有经验的 UVM 用户可能会忽略 UVM 针对寄存器模型内建的(built-in) 一些sequence, 实际上, 将这些自建的序列作为验证项目开始前的健康检查必选项, 对整个项目的平稳运行会有不小的贡献。 这是因为, 在项目的开始阶段, 设计内部的逻辑尚不稳定, 验证师要跟上设计的进度, 可以展开验证的部分无外乎是系统控制信号(时钟、复位、 电源)和寄存器的验证。在项目早期 , 寄存器模型的验证可以为后期各个功能点验证打下良好的基础。 比如, 通过内建的寄存器序列可以实现完善的寄存器复位值检查, 又比如检查读写寄存器的读写功能是否正常等。不过有一些寄存器即便可以测试, 也建议将其作为例外而过滤出去, 例如一些重要的系统控制信号(时钟、复位、电源), 当写入某些值以后, 会使得系统全部或局部复位、时钟也可能被关闭 , 这就可能阻碍寄存器的下一步检查。 所以 UVM 提供了一些特殊域, 用来禁止sequence 检查这些寄存器或存储。接下来,我们从下表来分别浏览整理出的寄存器和存储相关的自建sequence 。

 

 寄存器模型内建序列

 存储器模型内建序列

接下来我们给出一段例码,来演示如何利用内建序列完成MCDF寄存器测试一开始的健康检查。 下面的例码分别添加了 uvm_reg_ hw _reset_ seq、 uvm_reg_ bit_ bash_ seq和 uvm _reg_ access_ seq 来测试寄存器模型, 从代码的整洁性来看, 用户并不需要额外再添加什么, 这种使用方式非常方便, 且又能完成寄存器的大规模集成测试。 

mcdf_rgm rgm; 
`uvm_object_utils(mcdf_example_seq)
`uvm_declare_p_sequencer(mcdf_bus_sequencer)
...
task body(); 
uvm_status_e status;
uvm_reg_data_t data; 
uvm_reg_hw_reset_seq reg_rst_seq = new(); 
uvm_reg_bit_bash_seq reg_bit_bash_seq = new(); 
uvm_reg_access_seq reg_acc_seq = new(); 
if (! uvm_config_db# (mcdf_rgm)::get (null, get_full_name (), "rgm", rgm)) begin 
`uvm_error("GETRGM", "no top-down RGM handle is assigned") 
end 
// wait reset asserted and release 
@(negedge p_sequencer.vif.rstn); 
@(posedge p_sequencer.vif.rstn); 
`uvm_info ("BLTINSEQ", "register reset sequence started", UVM LOW) 
reg_rst_seq.model = rgm; 
reg_rst_seq.start(m_sequencer);
`uvm_info ("BLTINSEQ", "register reset sequence finished", UVM_LOW) 
`uvm_info("BLTINSEQ", "register bit bash sequence started", UVM_LOW)
//reset hardware register and register model 
reg_bit_bash_seq.model = rgm; 
reg_bit_bash_seq.start(m_sequencer); 
`uvm_info("BLTINSEQ", "register bit bash sequence finished", UVM_LOW) 
`uvm_info("BLTINSEQ", "register access sequence started", UVM_LOW) 
//reset hardware register and register model 
reg_acc_seq.model = rgm;
`uvm info ("BLTINSEQ", "register access sequence finished", UVM LOW )
endtask 
endclass 

”。由于 uvm_reg_block 和 uvm_reg 均是 uvm_object 类而不是 uvm_ component 类,。下面的代码摘自 mcdf_rgm::build()方法, 这相当于寄存器模型在自己的建立阶段设定了一些属性。当然, uvm_resource_ db 的配置也可以在更高层指定, 只不过考虑到 uvm_resource_ db 不具备层次化的覆盖属性, 我们建议只在 一个地方进行 “ 禁止域名 ” 的配置。

class mcdf_rgm extends uvm_reg_block;
...
virtual function build(); 
// disable built-in seq attributes 
uvm_resource_db#(bit)::set ({"REG::", this. chnlO stat reg. get full name () } , 
NO REG ACCESS TEST", 1); 
uvm_resource_db# (bit)::set ({ "REG: : ", this. chnll stat reg.get full name () } , 
"NO REG ACCESS TEST", 1); 
uvm_resource_db#(bit):: set ({"REG: : ", this. chnl2 stat reg. get full name () } , 
"NO REG ACCESS TEST", l); 
endfunction
endclass

标签: 连接器poke

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台