UVM/UVM Tutorial for Candy Lovers/Register Access Methods
- Sequences
- Analysis Components & Techniques
- UVM Tutorial for Candy Lovers
- Register Access Methods (link)
- Использование Register model
The register abstraction layer (RAL) of UVM provides several methods to access registers. This post will explain how the register-access methods work. In Register Abstraction, we introduced the overview of RAL and explained how to define registers. In this post, we will cover how to access the registers.
Регистр уровень абстракции (RAL) из УВМ предоставляет несколько методов для доступа к регистрам. Это сообщение будет объяснить, как методы регистров доступа работать. Регистрация Абстракция, мы ввели обзор RAL и объяснил, как определить регистры. В этой статье мы рассмотрим, как получить доступ к регистрам.
Содержание |
Properties of uvm_reg_field
Перед рассмотрением методов доступа к регистрам (регистровой модели), необходимо рассмотреть как значение регистра хранится. Как рассмотрено в статье Register Abstraction, Класс поля uvm_reg_field
является наименьшим (в иерархии) слоем в регистровой модели, который представляет биты регистра. Класс uvm_reg_field
использует некоторые свойства для сохранения множества значений поля регистра:
-
m_reset["HARD"]
хранит значение HARD (системного) сброса. Следует заметить, чтоm_reset
является ассоциативным массивом с типом сброса в качестве ключа. -
m_mirrored
хранит значение того, что мы «думаем» в нашем DUT`е. -
m_desired
хранит значение того, что мы «хотим» установить в DUT`е. -
value
хранит значение для учета его в функциональном покрытии, или значение законстрейненное (to be constrained) когда поле рандомизируется.
Заметим что среди этих свойств только свойство value
является public. Остальные свойства являются локальными, The other properties are local, таким образом мы не можем к ним обратиться напрямую вне данного класса. Далее будет рассмотрено как обратиться к этим локальным свойствам используя методы доступа к регистровой модели.
![]() |
---|
Свойства класса uvm_reg_field
|
Метод configure()
Первое что делается после создания класса uvm_reg_field
это его настройка. В статье Register Abstraction настаивалось/конфигурировалось поле flavor
как отмечено ниже. Заметим, что в статье Register Abstraction, задавалось поле flavor
как "WO"
(write-only), но здесь зададим его "RW"
(read/write), чтобы сделать поле более типовым.
flavor = uvm_reg_field::type_id::create( "flavor" );
flavor.configure( .parent ( this ),
.size ( 3 ),
.lsb_pos ( 0 ),
.access ( "RW" ),
.volatile ( 0 ),
.reset ( 0 ),
.has_reset ( 1 ),
.is_rand ( 1 ),
.individually_accessible( 0 ) );
Если аргумент has_reset
равен 1
, то значение аргумента reset
задаётся в как значение "HARD"
сброса. Если же has_reset
равень 0
, то значение аргумента reset
игнорируется. Значение reset
должно совпадать с состоянием DUT. Если же необходимо изменить значение сброса после конфигурации, то можно использовать метод set_reset()
.
flavor.set_reset( .value( 0 ), .kind( "HARD" ) ); // kind == "HARD" by default
![]() |
---|
Как действуют методы configure() и set_reset() |
Метод reset()
The reset()
method resets the properties of a register field, if the m_reset[kind]
exists. The default kind
is "HARD"
. If the m_reset[kind]
does not exist, the reset()
method does nothing. Note that the reset()
method does not reset a register in the DUT. It only resets the properties of a register-field object.
flavor.reset( .kind( "HARD" ) ); // kind == "HARD" by default
![]() |
---|
How the reset() method works |
Метод set()
The set()
method sets the desired value of a register field. The set()
method does not set the value to a register in the DUT. It only sets the value to the m_desired
and the value
properties of a register-field object. To actually set the value to the register in the DUT, use write()
or update()
method. These methods will be explained later.
flavor.set( .value( 1 ) );
![]() |
---|
Как действует метод set() |
Метод get()
The get()
method gets the desired value of a register field. The get()
method does not get the value from a register in the DUT. It only gets the value of the m_desired
property. To actually get the value from the DUT, use read()
or mirror()
methods. These methods will be explained later. Similarly to the get()
method, there are two more getters to access the local properties. The get_reset()
retrieves the value of the m_reset[kind]
property, while the get_mirrored_value()
method retrieves the value of the m_mirrored
property.
uvm_reg_data_t desired_value = flavor.get(); uvm_reg_data_t reset_value = flavor.get_reset( .kind( "HARD" ) ); // kind == "HARD" by default uvm_reg_data_t mirrored_value = flavor.get_mirrored_value();
![]() |
---|
How the get(), get_reset(), and get_mirrored_value() methods work |
Метод randomize()
The randomize()
method is a SystemVerilog method. It randomizes the value
property of a register-field object. After the randomization, the post_randomize()
method copies the value of the value
property to the m_desired
property. Note that the pre_randomize()
method copies the value of the m_desired
to the value
property if the rand_mode
of the value
property is OFF
.
assert( flavor.randomize() );
![]() |
---|
How the randomize() method works |
Метод write()
The write()
method actually writes a value to the DUT.
uvm_status_e status; flavor.write( .status( status ), .value( 1 ) );</pre>
The write()
method involves multiple steps.
- A
uvm_reg_item
object corresponding to the write operation is created. - The
uvm_reg_adapter
converts the write operation to a corresponding bus transaction. - The
uvm_driver
executes the bus transaction to the DUT. - The
uvm_monitor
captures the bus transaction. - The
uvm_reg_predictor
asks theuvm_reg_adapter
to convert the bus transaction to a corresponding register operation. - The register operation is converted to a
uvm_reg_item
. - The
uvm_reg_item
is used to update thevalue
,m_mirrored
, andm_desired
properties.
Note that if the individually_accessible
argument was 0
when the register field was configured, the entire register containing the field is written, because the field is not individually accessible. In this case, the m_mirrored
values are used as the write values for the other fields.
![]() |
---|
How the write() method works |
Метод read()
The read() method actually reads a register value from the DUT.
uvm_status_e status; uvm_reg_data_t value; flavor.read( .status( status ), .value( value ) );
Similarly to the write()
method, the read()
method involves multiple steps.
- A
uvm_reg_item
object corresponding to the read operation is created. - The
uvm_reg_adapter
converts the read operation to a corresponding bus transaction. - The
uvm_driver
executes the bus transaction to the DUT. - The
uvm_reg_apapter
converts the bus transaction with read data to a register operation. - The
read()
method returns the read value to the caller. - In the mean time, the
uvm_monitor
captures the bus transaction. - The
uvm_reg_predictor
asks theuvm_reg_adapter
to convert the bus transaction to a corresponding register operation. - The register operation is converted to a
uvm_reg_item
. - The
uvm_reg_item
is used to update thevalue
,m_mirrored
, andm_desired
properties.
Note that if the individually_accessible
argument was 0
when the register field was configured, the entire register containing the field is read. In this case, the m_mirrored
values are updated for the other fields as well.
![]() |
---|
How the read() method works |
Метод update()
The update()
method actually writes a register value to the DUT. The update()
method belongs to the uvm_reg
class. The uvm_reg_field
class does not have the update()
method.
uvm_status_e status; jb_recipe_reg.update( .status( status ) );
The differences between the write()
method and the update()
method are:
- The
write()
method takes a value as its argument, while theupdate()
method uses the value of them_desired
property as the value to write. - The
update()
method writes the value only if them_mirrored
and them_desired
are not equal.
![]() |
---|
Before the update() is executed |
The update()
method internally calls the write( .value( m_desired ) )
. Because of this, the value of the m_mirrored
will be updated as well, after the update.
![]() |
---|
After the update() is executed |
Метод mirror()
The mirror()
method actually reads a register from the DUT.
uvm_status_e status; flavor.mirror( .status( status ), .check( UVM_CHECK ) );
The differences between the read()
method and the mirror()
method are:
- The
read()
method returns the register value to the caller, while themirror()
method does not return the register value. Themirror()
method only updates the value of them_mirrored
property. - The
mirror()
method compares the read value against them_desired
if the value of thecheck
argument isUVM_CHECK
.Note that the UVM Class Library document states that it compares the read value against the mirrored value, but if you look at the line 2,944 of(Update 4/11/2014: uvm-1.1d code base has corrected this issue. Theuvm_reg.svh
of uvm-1.1c code base, it actually compares against the desired value, not against the mirrored value.mirror()
compares the read value against the mirrored value now. Please see the line 2,951 ofuvm_reg.svh
if you are curious about this fix.) Another caveat about the check is that if you set thevolatile
argument to be1
when you configured the register field, the register field won’t be checked even though you set thecheck
argument to beUVM_CHECK
. This is because we cannot predict the value of the register field deterministically as it might have been changed (volatile) in the DUT.
The mirror()
method internally calls do_read()
method. This is the same method the read()
method internally calls. Because of this, the mirror()
method will update the value
and the m_desired
properties, in addition to the m_mirrored
property.
![]() |
---|
How the mirror() method works |
Метод predict()
The predict()
method updates the mirrored value.
flavor.predict( .value( 1 ) );
The predict()
method also updates the value
and the m_desired
properties.
![]() |
---|
How the predict() method works |
Summary
The table below summarizes how each method updates the properties of the register-field object.
Method | m_reset
["HARD"]
|
value
|
m_desired
|
m_mirrored
|
DUT |
---|---|---|---|---|---|
configure(
.reset(val),
.has_reset(1))
|
set the value of val
|
||||
set_reset(val)
|
set the value of val
|
||||
reset()
|
copy the value of m_reset
["HARD"]
|
copy the value of m_reset
["HARD"]
|
copy the value of m_reset
["HARD"]
|
||
set(val)
|
set the value of val
|
set the value of val
|
|||
get_reset()
|
return the value of m_reset
["HARD"]
|
||||
get()
|
return the value of m_desired
|
||||
get_mirrored_value()
|
return the value of m_mirrored
|
||||
randomize()
|
randomize | copy the value of value
|
|||
write(.value(val))
|
set the value of val
|
set the value of val
|
set the value of val
|
write the value of val
| |
read(.value(val))
|
set the read value | set the read value | set the read value | read the register | |
update()
|
set the value of m_desired
|
set the value of m_desired
|
set the value of m_desired
|
write the value of m_desired
| |
mirror()
|
set the read value | set the read value | set the read value | read the register | |
predict(
.value(val))
|
set the value of val
|
set the value of val
|
set the value of val
|
In this post, we only covered so-called front-door access. We will cover back-door access in a separate post. I hope this tutorial helped you to understand the register access methods.