In reply to Jonathan_Alvarez:
I wanted to add some write/read register helper functions that in my opinion should be added to UVM. I put it here to let the community to review
//WRITE TASK that performs a range of write accesses defined by address WITH OPTIONS
task pve_write_addr_range(
output uvm_status_e status_q[$], //mandatory queue with the write status of the requested accesses done in the same order they are performed
input uvm_reg_addr_t start_addr, //mandatory start address
input uvm_reg_addr_t end_addr=start_addr, //end address of the range
input uvm_reg_data_t reg_value_q[$], //null values to be written defined always from low to high register address. it must have so many queued values as register addresses to write, if not, then error message,
//if not defined, it will look if another regblock is defined, and if the regblock is not defined then is assumed THIS register block
input uvm_reg_block reg_block_with_values=null, //if reg_value_q is not defined then this register block will be written. If reg_value_q and this regblock is not defined, then it is assumed THIS register block
input string exclude_reg_names[$], //null queue of register INST names to exclude from the address range
input logic[1:0] avoid_ro=0, //0 force write all registers, 1 avoid write whole/full RO registers, 2 "decide random for each register in the addr range"
input logic[1:0] addr_range_write_mode=0, //0 from start addr to end addr, 1 from end addr to start addr, 2 choose a random unique address from the range
input logic[1:0] do_only_changed=0, //0 write always, 1 only write if the new register value is different to the current one, 2 "decide random for each register in the addr range" (important for trigger on write reg)
input logic[1:0] do_dummywrite_before_write=0, //0 write directly the expected value, 1 For each register it will write a random value without constraints first and then the expected value (important to detect counter range problems between register writes.),2random
//2 "decide random for each register in the addr range"
input logic[1:0] do_read_after_write=0, //0 it will not read the register after the write, 1 read after write activated, 2 "decide random for each register in the addr range"
//
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
////////////////////////////////////////////////////////////////////////////////////////////
automatic uvm_reg regs[$]; //queue with all registers inside a map of this block
automatic uvm_reg range_regs[$];//queue with registers inside the range
automatic uvm_reg_map decided_map;
automatic logic test;
if (start_addr>end_addr) begin
`uvm_error( "pve_write_addr_range", $sformatf( "The start address 0x%0x is higher than the end address 0x%0x. That is not allowed. write canceled",start_addr,end_addr));
return;
end
if ((reg_value_q.size()==0)&&(reg_block_with_values!=null)) begin
//defined a dummy reg_block with values to be written
//we write these values
end
//GET REGISTERS
if (map==null) begin
decided_map=this.default_map; //.get_map_by_name("default_map");
decided_map.get_registers(regs, UVM_HIER); //NOW take all registers (regs) inside the default map.
end else begin
automatic string map_name;
automatic uvm_reg_map map_found_by_name;
map_name=map.get_name();//user map_name
map_found_by_name = this.get_map_by_name(map_name);//try to find this map in this block
if (map_found_by_name==null) begin
`uvm_error( "pve_write_addr_range", $sformatf( "The map provided with name:%s does not exist in this register block:%s. Write range cancelled.",map_name,this.get_name()) );
return;
end
decided_map=map;
decided_map.get_registers(regs, UVM_HIER); //NOW take all registers (regs) inside of an specific map of the block, UVM_HIER add recursively
end
//DEFINE REGISERS INSIDE THE RANGE
//foreach(regs[i]) begin
// addr_reg = regs[i].get_address();
// if ((addr_reg>=start_addr)&&(addr_reg<=end_addr)) begin
// range_regs.push_back(regs[i]);//add this register to the selected queue
// end
//end
range_regs = regs.find( item ) with ( ((item.get_address()>=start_addr)&&(item.get_address()<=end_addr)) );
//EXCLUSION INSIDE THE RANGE USING LOOP REVERSE
if (exclude_reg_names.size()!=0) begin
//if there is some register exclusion inside the range
automatic int last_exclude_idx;
last_exclude_idx = range_regs.size()-1;
for (int unsigned i=last_exclude_idx; i>0; i--) begin //loop from last element to the first in the queue so that the exclusion/deletion don't corrupt the indexes
automatic string current_reg_name;
current_reg_name=range_regs[i].get_name();
foreach (exclude_reg_names[j]) begin
if ( current_reg_name==exclude_reg_names[j]) begin
range_regs.delete(i);//delete element i from the queue
end
end //end for j
end //end for i
end
//CHANGE SIZE OF VALUES AND REGISTER RANGE
if ((reg_value_q.size()!=0)&&(reg_value_q.size()!=range_regs.size())) begin
`uvm_error( "pve_write_addr_range", $sformatf( "User specified a queue of values to write. With value.size: %0d elements, however, the number of reg elements to be written inside the address range is:%0d. Please define the correct number. Writes cancelled.",reg_value_q.size(),range_regs.size()));
if (exclude_reg_names.size()!=0) begin
`uvm_error( "pve_write_addr_range", $sformatf( "This error can be caused because of using N=%0d excluded register names that are not excluded from the N=%0d value queues.",exclude_reg_names.size(),reg_value_q.size()));
end
return;
end
//ORDER OF THE REGISTER TO BE WRITTEN
if (addr_range_write_mode==0) begin
//order from low to high address
range_regs.sort with (item.get_address()); //sort ascending by address
end else if (addr_range_write_mode==1) begin
range_regs.rsort with (item.get_address()); //rsort descending by address
if ((reg_value_q.size()!=0)) begin
reg_value_q.reverse(); //values will be aligned to registers order in descending order
end
end else if (addr_range_write_mode==2) begin
//2do insert the reg_value_q inside a new class and shuffle that class, then extract it back
if ((reg_value_q.size()!=0)) begin
`uvm_error( "pve_write_addr_range", $sformatf( "User specified a queue of values to write. however user want to shuffle the register write order. This cannot be done defining the input values. Writes cancelled.",reg_value_q.size(),range_regs.size()));
return;
end
range_regs.shuffle(); //shuffle the order of the whole array
end
test=test;
//////////////////////////////////////
//DO THE WRITE FOR EACH REGISTER
//////////////////////////////////////
foreach (range_regs[i]) begin
//DEFINE VALUES TO WRITE
automatic uvm_reg_data_t value_to_write;
automatic uvm_reg_addr_t current_addr_reg;
automatic logic decide_avoid_ro;
automatic logic decide_do_only_changed;
automatic logic decide_do_read_after_write;
automatic logic decide_do_dummywrite_before_write;
automatic uvm_status_e status_this_wr;
current_addr_reg = range_regs[i].get_address();
//Decide Value
if ((reg_value_q.size()!=0)) begin
//Take values from user specified queue
value_to_write = reg_value_q[i];
end else if (reg_block_with_values!=null) begin
automatic uvm_reg user_register;
//TAKE VALUES FROM specific reg_block_with_values
user_register = reg_block_with_values.get_reg_by_name(range_regs[i].get_name());
value_to_write = user_register.get();//return desired value
end else begin
//values are from THIS REGBLOCK DESIRED
value_to_write = range_regs[i].get();//return desired value
end
//Decide avoid_ro
if (avoid_ro==2) begin
assert(std::randomize(decide_avoid_ro) with {decide_avoid_ro dist {0:=10, 1:=10};});
end else begin
decide_avoid_ro = avoid_ro;
end
if ((decide_avoid_ro==1)) begin
automatic uvm_reg_field m_fields[$];
automatic logic found_one_writable_field=0;
////////////////////////////////////////////
range_regs[i].get_fields(m_fields);
//only update the protections for RW or WO registers
foreach(m_fields[j]) begin
string acc = m_fields[j].get_access(decided_map);
acc = acc.substr(0, 1);
if ((acc == "WO")||(acc == "RW")) begin
found_one_writable_field=1;
break;
end
end
if (found_one_writable_field==0) begin
continue;//go to next register this will not be written
end
end
//Decide do_only_changed
if (do_only_changed==2) begin
assert(std::randomize(decide_do_only_changed) with {decide_do_only_changed dist {0:=10, 1:=10};});
end else begin
decide_do_only_changed = do_only_changed;
end
if ((decide_do_only_changed==1)) begin
automatic uvm_reg_data_t current_mirrored_value_register;
current_mirrored_value_register=range_regs[i].get_mirrored_value();
if (current_mirrored_value_register==value_to_write) begin
continue;//if the same value will be written
end
end
//Decide decide_do_dummywrite_before_write
if (do_dummywrite_before_write==2) begin
assert(std::randomize(decide_do_dummywrite_before_write) with {decide_do_dummywrite_before_write dist {0:=10, 1:=10};});
end else begin
decide_do_dummywrite_before_write = do_dummywrite_before_write;
end
if ((decide_do_dummywrite_before_write==1)) begin
automatic uvm_reg_data_t value_dummy;
automatic uvm_status_e status;
//do dummy write with new random value
//2do UNFORTUNATELY THIS WILL NOT CONSTRAINT ACCORDING TO BLOCK CONSTRAINTS :(, full random
assert(range_regs[i].randomize(* solvefaildebug *) with {
//nothing to constraint for this register
}) else $fatal (0, $sformatf( "decide_do_dummywrite_before_write Randomization of the reg %s failed.",range_regs[i].get_name()) ); //end assert
value_dummy = range_regs[i].get();
range_regs[i].write(status,value_dummy, path, map, parent, prior, extension, fname, lineno);//WRITE the dummy value to DUT
if (status != UVM_IS_OK && status != UVM_HAS_X) begin
`uvm_error("pve_write_addr_range", $sformatf( "The Dummy write before write was not correctly accepted by DUT. Status:%s for REGISTER name:%s",status.name(),range_regs[i].get_name()) );
end else begin
//all OK
end
end
///////////////////////////////////////////////////////
//Do the register write
///////////////////////////////////////////////////////
range_regs[i].write(status_this_wr,value_to_write, path, map, parent, prior, extension, fname, lineno);//WRITE the dummy value to DUT
status_q.push_back(status_this_wr); //include status of this write as output of this function
///////////////////////////////////////////////////////
//Decide decide_do_read_after_write
if (do_read_after_write==2) begin
assert(std::randomize(decide_do_read_after_write) with {decide_do_read_after_write dist {0:=10, 1:=10};});
end else begin
decide_do_read_after_write = do_read_after_write;
end
if (decide_do_read_after_write==1) begin
automatic uvm_status_e status;
//PERFORM READ AFTER WRITE
range_regs[i].read(status,value_to_write, path, map, parent, prior, extension, fname, lineno);//WRITE the dummy value to DUT
if (status != UVM_IS_OK && status != UVM_HAS_X) begin
`uvm_error("pve_write_addr_range", $sformatf("The Read after write was not OK with value:%s, REGISTER name:%s",status.name(),range_regs[i].get_name()) );
end else begin
//all OK
end
end
test=test;
end//for loop range registers
endtask :pve_write_addr_range
//READ TASK that performs a range of read accesses defined by address WITH OPTIONS
task pve_read_addr_range(
output uvm_status_e status_q[$], //mandatory queue with the read status of the requested accesses done in the same order they are performed
output uvm_reg_data_t read_values_q[$], //values read in the same order that the access has been done
output string order_reg_names[$], //queue of register INST names that specifies the order of read that has been performed
input uvm_reg_addr_t start_addr, //mandatory start address
input uvm_reg_addr_t end_addr=start_addr, //end address of the range
//if not defined, it will look if another regblock is defined, and if the regblock is not defined then is assumed THIS register block
input string exclude_reg_names[$], //null queue of register INST names to exclude from the address range
input logic[1:0] addr_range_read_mode=0, //0 read from start addr to end addr, 1 read from end addr to start addr, 2 choose a random unique address to read from the range
//
input uvm_path_e path = UVM_DEFAULT_PATH,
input uvm_reg_map map = null,
input uvm_sequence_base parent = null,
input int prior = -1,
input uvm_object extension = null,
input string fname = "",
input int lineno = 0);
////////////////////////////////////////////////////////////////////////////////////////////
automatic uvm_reg regs[$]; //queue with all registers inside a map of this block
automatic uvm_reg range_regs[$];//queue with registers inside the range
automatic uvm_reg_map decided_map;
automatic logic test;
if (start_addr>end_addr) begin
`uvm_error( "pve_read_addr_range", $sformatf( "The start address 0x%0x is higher than the end address 0x%0x. That is not allowed. read canceled",start_addr,end_addr));
return;
end
//GET REGISTERS
if (map==null) begin
decided_map=this.default_map; //.get_map_by_name("default_map");
decided_map.get_registers(regs, UVM_HIER); //NOW take all registers (regs) inside the default map.
end else begin
automatic string map_name;
automatic uvm_reg_map map_found_by_name;
map_name=map.get_name();//user map_name
map_found_by_name = this.get_map_by_name(map_name);//try to find this map in this block
if (map_found_by_name==null) begin
`uvm_error( "pve_read_addr_range", $sformatf( "The map provided with name:%s does not exist in this register block:%s. read range cancelled.",map_name,this.get_name()) );
return;
end
decided_map=map;
decided_map.get_registers(regs, UVM_HIER); //NOW take all registers (regs) inside of an specific map of the block, UVM_HIER add recursively
end
//DEFINE REGISERS INSIDE THE RANGE
//foreach(regs[i]) begin
// addr_reg = regs[i].get_address();
// if ((addr_reg>=start_addr)&&(addr_reg<=end_addr)) begin
// range_regs.push_back(regs[i]);//add this register to the selected queue
// end
//end
range_regs = regs.find( item ) with ( ((item.get_address()>=start_addr)&&(item.get_address()<=end_addr)) );
//EXCLUSION INSIDE THE RANGE USING LOOP REVERSE
if (exclude_reg_names.size()!=0) begin
//if there is some register exclusion inside the range
automatic int last_exclude_idx;
last_exclude_idx = range_regs.size()-1;
for (int unsigned i=last_exclude_idx; i>0; i--) begin //loop from last element to the first in the queue so that the exclusion/deletion don't corrupt the indexes
automatic string current_reg_name;
current_reg_name=range_regs[i].get_name();
foreach (exclude_reg_names[j]) begin
if ( current_reg_name==exclude_reg_names[j]) begin
range_regs.delete(i);//delete element i from the queue
end
end //end for j
end //end for i
end
//ORDER OF THE REGISTER TO BE READ
if (addr_range_read_mode==0) begin
//order from low to high address
range_regs.sort with (item.get_address()); //sort ascending by address
end else if (addr_range_read_mode==1) begin
range_regs.rsort with (item.get_address()); //rsort descending by address
end else if (addr_range_read_mode==2) begin
range_regs.shuffle(); //shuffle the order of the whole array
end
//WE NOW ALREADY THE ORDER OF THE REGISTERNAMES
test=test;
//////////////////////////////////////
//DO THE read FOR EACH REGISTER
//////////////////////////////////////
foreach (range_regs[i]) begin
//DEFINE VALUES TO read
automatic uvm_reg_data_t value_read;
automatic uvm_reg_addr_t current_addr_reg;
automatic uvm_status_e status_this_rd;
current_addr_reg = range_regs[i].get_address();
///////////////////////////////////////////////////////
//Do the register read
///////////////////////////////////////////////////////
range_regs[i].read(status_this_rd,value_read, path, map, parent, prior, extension, fname, lineno);//WRITE the dummy value to DUT
status_q.push_back(status_this_rd); //include status of this write as output of this function
read_values_q.push_back(value_read); //add the value to the queue of values
order_reg_names.push_back(range_regs[i].get_name()); //include INST name in the order of registers
///////////////////////////////////////////////////////
test=test;
end//for loop range registers
endtask :pve_read_addr_range
//////////////////////////////////////
//CONTROL OVER UVM_CHECK FUNCTIONS
/////////////////////////////////////
//controls if we want to activate/deactivate the check on read for all the maps inside this block
//you can specify a single map.
//UVM default status of all maps is DISABLED UVM_CHECK
//e.g. pve_set_reg_model_check_on_read(1);//activate
virtual function void pve_set_reg_model_check_on_read(bit v, uvm_reg_map specific_map=null);
if (specific_map==null) begin
uvm_reg_map maps[$];
this.get_maps(maps);//Get maps from our register model
foreach(maps[i]) begin
maps[i].set_check_on_read(v);
end
end else begin
//user specified a specific map to set
automatic string map_name;
automatic uvm_reg_map map_found_by_name;
map_name=specific_map.get_name();//user map_name
map_found_by_name = this.get_map_by_name(map_name);//try to find this map in this block
if (map_found_by_name==null) begin
`uvm_error( "pve_set_reg_model_check_on_read", $sformatf( "The map provided with name:%s does not exist in this register block:%s. UVM_CHECK set cancelled.",map_name,this.get_name()) );
return;
end
end
endfunction
//This is to deactivate in ONE specific register the comparison.
//DEFAULT value for Register and field level is set compare ENABLE
virtual function void pve_set_register_check_on_read(uvm_reg register, bit check);
automatic uvm_reg_field m_fields[$]; // Fields in LSB to MSB order
automatic uvm_reg register_ref; // register reference in this block
automatic string reg_name;
reg_name=register.get_name();//user register name
register_ref = this.get_reg_by_name(reg_name);//try to find this map in this block
if (register_ref==null) begin
`uvm_warning( "pve_set_register_check_on_read", $sformatf( "The register provided is not from this block %s.",this.get_name()) );
return;
end
register.get_fields(m_fields);
foreach (m_fields[i]) begin
if (check) begin
m_fields[i].set_compare(UVM_CHECK);
end else begin
m_fields[i].set_compare(UVM_NO_CHECK);
end
end
endfunction
//This is to deactivate only specific fields inside of a REGISTER. Maybe there are some volatile fields in this register that cannot be predicted.
function void pve_set_reg_field_check_on_read(uvm_reg_field reg_field, bit check);
if (check) begin
reg_field.set_compare(UVM_CHECK);
end else begin
reg_field.set_compare(UVM_NO_CHECK);
end
endfunction