-
A register will have multiple register fields:
- Step: inside register build() function construct reg field m_reg_field_a --> m_reg_field_a.configure()
-
reg block will contains registers, and address maps block:
-
We can have multiple address map blocks inside a register block.
-
A register can be added to these address map blocks, meaning each register can have multiple address, and each address map has different access requirements
-
Step: inside register block build() function construct a register m_rega = ::type_id::create(...)--> m_rega.configure() // add the hdl path as argument here // this is built-in func inside uvm_reg class --> m_rega.build() construct a memory m_mema = ::type_id::create(...) construct a reg map m_map_apb = create_map("")--> m_map_apb.add_reg(m_rega, ...) --> m_map_apb.add_mem(m_mema, ...) --> default_map = m_map_apb --> lock_model()
-
-
Group block of registers:
-
We can have a group block of registers, contains a address map, and several register blocks using that address map
-
Step: inside group block build() function: construct a register block m_reg_block_a, m_reg_block_b, then --> m_reg_block_.configure() --> m_reg_block_.build() construct a address map m_map_ahb = create_map() --> m_map_ahb.add_submap(m_reg_block_a.default_map, 'h0000); --> m_map_ahb.add_submap(m_reg_block_b.default_map, 'h1000); --> lock_model();
-
-
Multiple address mapping:
- In the build() function of reg block (or group regblock), create as many address map as needed, then add the required registers/mem to that map using add_reg()/add_mem() function
-
The Adapter:
- Used to translate a register transaction to bus request transaction and vice versa
- Will be put in an address map of a register block
- Adapter is constructed outside a regblock, then assigned its handle to the needed addr map inside a reg block
- Each adapter is specific for one bus agent (i.e: adapter for ahb agent, adapter for axi agent)
- Related:
- Generic register item (uvm_reg_bus_op): a struct contains important info for a operation on a register: addr, data, kind(READ/WRITE), num of xfer bits, byte enable, sts.
- uvm_reg_adapter: base class of the adapter, contains 2 methods: reg2bus() and bus2reg(), 2 properties: supports_byte_enable, provides_responses. extend this base class, then modify these 2 methods to translate the generic register item above to the needed bus transaction item.
- The address map of a reg block will have the set_sequencer() function, where we will pass the needed bus agent and the related adapter to module_regblock.apb_map.set_sequencer(m_apb_agent.m_sequencer, m_reg2apb_adapter);
-
Predict:
- Predict is the action in which the mirror content of DUT state is updated. The mirror contents is in a reg model.
- For Backdoor: automatically call predict() method after each backdoor access.
- For Frontdoor: there are three models:
-
Auto Prediction: after the front door accesses method are executed (using agent and send bus transaction to DUT), the predict() method is called automatically, using read back data or write data send to the agent only update mirror value if the ENV create the FRONT DOOR trans to DUT) SHOULD NOT USE
-
Explicit Prediction: Create a predictor component, which receives bus transaction from the bus agent, then convert and update the mirror value using predict() method of the register. ALL trans happen on the bus interface will be updated in the mirror value. RECOMMENDED
-
Passive Prediction: Similar to Explicit Prediction, but do not create simulus sequence to the DUT. The stimulus sequence is not generated from the register model, meaning the adaptor to gen reg2bus is not used
-
-
Implement process:
- Creating:
- Bus agent, and the adapter, predictor.
- Check the address map, creates all the needed registers and address map
- Build register model in the test, passing its handle down the testbench via configuration obj.
- Implement Adaption Layer:
- The sequence stimulus generate part:
- Create in build_phase(), mapping in connect_phase() of env
- m_cfg.m_regmodel.m_map.set_sequencer(m_agent.m_sequencer, m_adapter)
- The monitor/update register model part.
- Create in build_phase(), mapping in connect_phase() of env
- m_predictor.map = m_reg_block.default_map
- m_predictor.adapter = m_adapter // this also the adapter assign to reg model's map // when calling m_cfg.m_regmodel.m_map.set_sequencer(...)
- m_agent.ap.connect(m_predictor.bus_in); // connect ap port of monitor of agent to predictor
- The sequence stimulus generate part:
- Creating:
-
Quirky Registers:
- uvm reg support these, if none of these matches your reg filed access attribute, create your own Quirky Register:
- set_access() will modify the access policies. The effect of a read operation are applied after the current value of the field is sampled ”RO” W: no effect, R: no effect ”RW” W: as-is, R: no effect ”RC” W: no effect, R: clears all bits ”RS” W: no effect, R: sets all bits ”WRC” W: as-is, R: clears all bits ”WRS” W: as-is, R: sets all bits ”WC” W: clears all bits, R: no effect ”WS” W: sets all bits, R: no effect ”WSRC” W: sets all bits, R: clears all bits ”WCRS” W: clears all bits, R: sets all bits ”W1C” W: 1/0 clears/no effect on matching bit, R: no effect ”W1S” W: 1/0 sets/no effect on matching bit, R: no effect ”W1T” W: 1/0 toggles/no effect on matching bit, R: no effect ”W0C” W: 1/0 no effect on/clears matching bit, R: no effect ”W0S” W: 1/0 no effect on/sets matching bit, R: no effect ”W0T” W: 1/0 no effect on/toggles matching bit, R: no effect ”W1SRC” W: 1/0 sets/no effect on matching bit, R: clears all bits ”W1CRS” W: 1/0 clears/no effect on matching bit, R: sets all bits ”W0SRC” W: 1/0 no effect on/sets matching bit, R: clears all bits ”W0CRS” W: 1/0 no effect on/clears matching bit, R: sets all bits ”WO” W: as-is, R: error ”WOC” W: clears all bits, R: error ”WOS” W: sets all bits, R: error ”W1” W: first one after HARD reset is as-is, other W have no effects, R: no effect ”WO1” W: first one after HARD reset is as-is, other W have no effects, R: error
-
Backdoor:
- Add hdl path using the m_reg.configure() function
- Or use the add_hdl_path()
- Using different path for RTL/GATE/ or SOC:
- add_hdl_path("DUT","RTL");
- add_hdl_path("DUT", "GATE");
- NEED TO BE UPDATED
-
Tracking reg value:
- Reg model will have these value for each register:
- The desired value represents a state that the register model is going to use to update the hardware, but has not done so.
- The mirrored value represents the current known state of the hardware register. This is updated at:
- The end of front bus read and write cycles either based on the data value seen by the register model (auto-prediction).
- Based on bus traffic observed by a monitor and sent to predictor that updates the register model content (recommended approach for integrating the register model).
- Backdoor accesses update the register model automatically.
- This could be wrong if the reg fields are volatile.
- Register access methods:
-
read() and write():
- read(): read from hw, update mirrorred/desired value of reg model at the end of bus read cycle
- write(): write to hw, update mirrorred/desired value of reg model at the end of bus read cycle
-
set() and get() and update():
-
Operate on register model, do not impact HW.
-
set() will assign an internal "expected value" to a register of a reg model. This "expected value" is the calculation result of access policy, value argument assign to this set() func, and current mirrored value This "expected value" will finally update to the desired value of reg after call set()
-
call get() after set will return the calculated desired value.
-
used for generate the desired value in testing special reg field: W1C, WC,...
-
update() will applied the desired value on the HW (via bus transaction) if mirror and desired value is different.
-
i.e:
- for RO: set(0xaaa) will not affect the mirrored, desired value, no value on bus when update()
- for W1C: write 1 clear, current mirror value is 0x11ff, set(0x1111) then the desired value will be 0x00ee, when update, 0x00ee will be seen in bus
-
set()/randomize() will update desired value, then update() will update the HW, at the last cycle of the bus transaction the mirrored will be updated by predictor to match desired value
-
The advantage of using set/update over write is that you can batch update multiple registers with one method call instead of writing each register individually.
-
-
get_mirrored_value() returns the current mirrored value of the reg model.
-
peek() and poke():
- backdoor access methods.
- peek() read backdoor the HW reg and update desired and mirrored value.
- poke() force thw HW reg to a expecte value, also update desired and mirrored value.
-
randomize():
- random the register value.
- uvm_reg will post_randoize() to update desired/mirrored value.
- update() after randomize() will update the HW value.
-
mirror():
- HW read (frontdoor) or peek (backdoor) the HW value, then mirrored value will be updated
- There are option to: select either backdoor/frontdoor, or compare mirrored value against the old value.
- Call mirror() on a block register make all the registers in this block to be mirrored
-
reset():
- set desired/mirrored value to pre-defined register reset value.
-
get_reset():
- return pre-defined reset value, do not update desired/mirrored value.
-