前言
我们在使用UVM的寄存器模型过程中,有时候需要在寄存器配置配置前准备一些数据,或者在寄存器配置之后触发一些操作,这个时候就可以用到UVM寄存器模型中自带的callback,本文主要介绍如何使用UVM中寄存器模型自带的callback。
一、底层UVM的介绍
在uvm-1.2\src\reg\uvm_reg_cbs.svh中,分别为读写提供了pre和post的回调函数,以及post_predict的回调函数等如下所示。
// Task: pre_write
//
// Called before a write operation.
//
// All registered ~pre_write~ callback methods are invoked after the
// invocation of the ~pre_write~ method of associated object (<uvm_reg>,
// <uvm_reg_field>, <uvm_mem>, or <uvm_reg_backdoor>). If the element being
// written is a <uvm_reg>, all ~pre_write~ callback methods are invoked
// before the contained <uvm_reg_fields>.
//
// Backdoor - <uvm_reg_backdoor::pre_write>,
// <uvm_reg_cbs::pre_write> cbs for backdoor.
//
// Register - <uvm_reg::pre_write>,
// <uvm_reg_cbs::pre_write> cbs for reg,
// then foreach field:
// <uvm_reg_field::pre_write>,
// <uvm_reg_cbs::pre_write> cbs for field
//
// RegField - <uvm_reg_field::pre_write>,
// <uvm_reg_cbs::pre_write> cbs for field
//
// Memory - <uvm_mem::pre_write>,
// <uvm_reg_cbs::pre_write> cbs for mem
//
// The ~rw~ argument holds information about the operation.
//
// - Modifying the ~value~ modifies the actual value written.
//
// - For memories, modifying the ~offset~ modifies the offset
// used in the operation.
//
// - For non-backdoor operations, modifying the access ~path~ or
// address ~map~ modifies the actual path or map used in the
// operation.
//
// If the ~rw.status~ is modified to anything other than <UVM_IS_OK>,
// the operation is aborted.
//
// See <uvm_reg_item> for details on ~rw~ information.
//
virtual task pre_write(uvm_reg_item rw); endtask
// Task: post_write
//
// Called after a write operation.
//
// All registered ~post_write~ callback methods are invoked before the
// invocation of the ~post_write~ method of the associated object (<uvm_reg>,
// <uvm_reg_field>, <uvm_mem>, or <uvm_reg_backdoor>). If the element being
// written is a <uvm_reg>, all ~post_write~ callback methods are invoked
// before the contained <uvm_reg_fields>.
//
// Summary of callback order:
//
// Backdoor - <uvm_reg_cbs::post_write> cbs for backdoor,
// <uvm_reg_backdoor::post_write>
//
// Register - <uvm_reg_cbs::post_write> cbs for reg,
// <uvm_reg::post_write>,
// then foreach field:
// <uvm_reg_cbs::post_write> cbs for field,
// <uvm_reg_field::post_read>
//
// RegField - <uvm_reg_cbs::post_write> cbs for field,
// <uvm_reg_field::post_write>
//
// Memory - <uvm_reg_cbs::post_write> cbs for mem,
// <uvm_mem::post_write>
//
// The ~rw~ argument holds information about the operation.
//
// - Modifying the ~status~ member modifies the returned status.
//
// - Modifying the ~value~ or ~offset~ members has no effect, as
// the operation has already completed.
//
// See <uvm_reg_item> for details on ~rw~ information.
//
virtual task post_write(uvm_reg_item rw); endtask
// Task: pre_read
//
// Callback called before a read operation.
//
// All registered ~pre_read~ callback methods are invoked after the
// invocation of the ~pre_read~ method of associated object (<uvm_reg>,
// <uvm_reg_field>, <uvm_mem>, or <uvm_reg_backdoor>). If the element being
// read is a <uvm_reg>, all ~pre_read~ callback methods are invoked before
// the contained <uvm_reg_fields>.
//
// Backdoor - <uvm_reg_backdoor::pre_read>,
// <uvm_reg_cbs::pre_read> cbs for backdoor
//
// Register - <uvm_reg::pre_read>,
// <uvm_reg_cbs::pre_read> cbs for reg,
// then foreach field:
// <uvm_reg_field::pre_read>,
// <uvm_reg_cbs::pre_read> cbs for field
//
// RegField - <uvm_reg_field::pre_read>,
// <uvm_reg_cbs::pre_read> cbs for field
//
// Memory - <uvm_mem::pre_read>,
// <uvm_reg_cbs::pre_read> cbs for mem
//
// The ~rw~ argument holds information about the operation.
//
// - The ~value~ member of ~rw~ is not used has no effect if modified.
//
// - For memories, modifying the ~offset~ modifies the offset
// used in the operation.
//
// - For non-backdoor operations, modifying the access ~path~ or
// address ~map~ modifies the actual path or map used in the
// operation.
//
// If the ~rw.status~ is modified to anything other than <UVM_IS_OK>,
// the operation is aborted.
//
// See <uvm_reg_item> for details on ~rw~ information.
//
virtual task pre_read(uvm_reg_item rw); endtask
// Task: post_read
//
// Callback called after a read operation.
//
// All registered ~post_read~ callback methods are invoked before the
// invocation of the ~post_read~ method of the associated object (<uvm_reg>,
// <uvm_reg_field>, <uvm_mem>, or <uvm_reg_backdoor>). If the element being read
// is a <uvm_reg>, all ~post_read~ callback methods are invoked before the
// contained <uvm_reg_fields>.
//
// Backdoor - <uvm_reg_cbs::post_read> cbs for backdoor,
// <uvm_reg_backdoor::post_read>
//
// Register - <uvm_reg_cbs::post_read> cbs for reg,
// <uvm_reg::post_read>,
// then foreach field:
// <uvm_reg_cbs::post_read> cbs for field,
// <uvm_reg_field::post_read>
//
// RegField - <uvm_reg_cbs::post_read> cbs for field,
// <uvm_reg_field::post_read>
//
// Memory - <uvm_reg_cbs::post_read> cbs for mem,
// <uvm_mem::post_read>
//
// The ~rw~ argument holds information about the operation.
//
// - Modifying the readback ~value~ or ~status~ modifies the actual
// returned value and status.
//
// - Modifying the ~value~ or ~offset~ members has no effect, as
// the operation has already completed.
//
// See <uvm_reg_item> for details on ~rw~ information.
//
virtual task post_read(uvm_reg_item rw); endtask
// Task: post_predict
//
// Called by the <uvm_reg_field::predict()> method
// after a successful UVM_PREDICT_READ or UVM_PREDICT_WRITE prediction.
//
// ~previous~ is the previous value in the mirror and
// ~value~ is the latest predicted value. Any change to ~value~ will
// modify the predicted mirror value.
//
virtual function void post_predict(input uvm_reg_field fld,
input uvm_reg_data_t previous,
inout uvm_reg_data_t value,
input uvm_predict_e kind,
input uvm_path_e path,
input uvm_reg_map map);
endfunction
// Function: encode
//
// Data encoder
//
// The registered callback methods are invoked in order of registration
// after all the ~pre_write~ methods have been called.
// The encoded data is passed through each invocation in sequence.
// This allows the ~pre_write~ methods to deal with clear-text data.
//
// By default, the data is not modified.
//
virtual function void encode(ref uvm_reg_data_t data[]);
endfunction
// Function: decode
//
// Data decode
//
// The registered callback methods are invoked in ~reverse order~
// of registration before all the ~post_read~ methods are called.
// The decoded data is passed through each invocation in sequence.
// This allows the ~post_read~ methods to deal with clear-text data.
//
// The reversal of the invocation order is to allow the decoding
// of the data to be performed in the opposite order of the encoding
// with both operations specified in the same callback extension.
//
// By default, the data is not modified.
//
virtual function void decode(ref uvm_reg_data_t data[]);
endfunction
在uvm-1.2\src\reg\uvm_reg_field.svh或者uvm-1.2\src\reg\uvm_reg.svh中,write和read的task会去分别调用do_write和do_read,在do_write中,写之前会去调用uvm_reg_cbs中的pre_write,写之后会去调用post_write,do_read与do_write类似。
所以,我们可以利用UVM中寄存器自带的这个callback,先基于这个uvm_reg_cbs,在其继承后子类的pre/post中,去实现我们自定义的callback功能,再将我们实现的子类加到对应寄存器的callback中。
二、UVM寄存器模型callback的使用
使用uvm寄存器模型的callback可以分为两步:
1)拓展。基于uvm_reg_cbs拓展出一个子类,在子类的pre/post中实现需要的功能;
2)绑定。将拓展出来的callback子类add到对应的寄存器上。
1、拓展
先基于uvm_reg_cbs拓展出一个子类reg0_callback,在子类pre/post中,实现我们需要的功能。
class reg0_callback extends uvm_reg_cbs;
int a;
`uvm_object_utils(reg0_callback)
function new(string name = "reg0_callback");
super.new(name);
endfunction : new
virtual task post_write(uvm_reg_item rw);
// user function write here
...
endtask : post_write
endclass : reg0_callback
2、绑定
在环境中,首先声明并创建我们自定义的回调class,然后reg利用uvm_reg_cb::add这个静态函数,field利用uvm_reg_field_cb::add这个静态函数,将回调class绑定到对应的寄存器上,这样在这个寄存器访问的时候,就会自动触发回调class的调用,从而实现uvm寄存器模型的callback。
class top_env extends uvm_env;
`uvm_object_utils(top_env)
function new(string name = "top_env");
super.new(name);
endfunction : new
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
reg0_callback reg0_cb;
reg0_cb = reg0_callback::type_id::create("reg0_cb");
// bind here
uvm_reg_cb::add(reg_block.reg0, reg0_cb);
endfunction
endclass : top_env
其中,uvm_reg_cb/uvm_reg_field_cb这两个类型的定义在uvm_reg_cbs中,实质上还是用的uvm_callback,只是传递了不同的参数类型,源码如下:
// Type: uvm_reg_cb
//
// Convenience callback type declaration for registers
//
// Use this declaration to register the register callbacks rather than
// the more verbose parameterized class
//
typedef uvm_callbacks#(uvm_reg, uvm_reg_cbs) uvm_reg_cb;
// Type: uvm_reg_field_cb
//
// Convenience callback type declaration for fields
//
// Use this declaration to register field callbacks rather than
// the more verbose parameterized class
//
typedef uvm_callbacks#(uvm_reg_field, uvm_reg_cbs) uvm_reg_field_cb;
总结
本文主要记录一下,如何利用UVM寄存器模型自带的callback,实现用户自定义的功能。