Uvm reg predict issue

Hi there,

I have a scenario where there are only two registers e.g 0xA and 0xB

  • registers 0xA are readonly and
    • one of its bit is set when I write to 0xB.
    • that bit is cleared upon read access.
  • registers 0xB allows read and write accesses.

The register block model is defined with:
dut_rm.bus_map.set_check_on_read(1);

On the driver, I’ve ensured that everytime when there is a write access on 0xB, a regA.predict(value) is executed. Hence m_mirrored would be updated with the expected value, which would then be used to compare against the DUT frontdoor read value (via a monitor).

The problem I’m encountering is that when there is a write access on 0xB followed by read access on 0xA, I’m unable to clear the m_mirrored of 0xA. This is because during a read access of 0xA, the is_busy is active and any regA.predict(0) will be ignored and hence m_mirrored is not cleared.

Is there a way to clear the m_mirrored of regA after a read access on regA (not that there maybe consecutive read access of regA)?

Hi Chitlesh,
I have encountered this type of issue earlier and followed the below approach to successfully predict the register 0xA.

//Step1: Write operation on register 0xB
reg_model.reg_B.write(status,<some_value>);

//Step2: wait by continuously polling the “is_busy” of 0xA
while(1) begin
if(reg_model.reg_A.is_busy == 1) begin
continue;
end
else begin
reg_model.reg_A.predict(<some_value>);
break;
end
end

//Step3: Now, check the contents of register 0xA in DUT with that of Register Model.
reg_model.reg_A.mirror(status,UVM_CHECK);

NOTE
Please be careful when you use this kind of implementation, if you are using this kind of logic in reference model and many drivers are connected and based on the testcase random stimuli related to these registers is being continuously driven onto the DUT then, in some corner case the prediction might not happen and the test fails.

Thanks,
Neith

In reply to UVM Beginner:

When you call Predict function, it calls do_predict(). Do predict checks if the register is busy. If the register is found to be busy, do_predict() will not perform the predict operation.

So, user has to check the return value of the predict() function to check if the operation was done successfully

Predict returns 1 for SUCCESS and 0 for UN_SUCCESSFUL operation.

Solution provided by Neith works; but you may need to add a “#1” delay before “continue” statement.

You may like to make it simple with one line code

while (! (REGISTER.predict(<value>)));

In reply to suniljasthi:

Lets assume you have to update two registers with predict in a function.
For completeness, lets assume that the prediction of those two registers must be done for any register access in a reg_block.


   virtual function void custom_field_predict(uvm_reg_field m_field=null, uvm_reg_data_t value);
            if ((m_field!=null)) begin
                fork
                    automatic uvm_reg_field    m_field_fork=m_field;
                    automatic uvm_reg_data_t   value_fork  =value;
                    begin
                        automatic uvm_reg    m_reg_fork=m_field_fork.get_parent();
                        if  (m_reg_fork.is_busy()) begin
                            wait(!m_reg_fork.m_is_busy);
                        end
                        assert( m_field_fork.predict(value_fork))    else `uvm_error( "pve_reg_predict", $sformatf( "The register with name:%s and field %s cannot be  predicted correctly.",m_reg_fork.get_name(),m_field_fork.get_name() ) );
                    end
                join_none
            end
        endfunction


As you can see, we use regblock to access the register we want to predict and we use register item to identify if we are going to have collision by predicting a register that is currently being accessed in reg.

I use “wait” instead of a “while” construct because we don´t have to put a fixed #1 time delay.

Notice that i use a fork inside a function so that we can use this function inside other functions. e.g. do_predict() overwrite in a custom register class.

Another elegant solution is just to hack in the busy functionality of UVM.
If you are interested to do the direct prediction ignoring the busy flag, then you can just force the busy flag to go to ZERO.
This is achieved calling ‘Xset_busyX(0)’ before doing the prediction. This has the advantage that you will not have the warning from the busy check. However, ignoring the busy flag means that you may have mismatches between frontdoor DUT output and your predicted value. This depends on the time of the update and the taken value for the frontdoor READ/WRITE. Therefore, disabling the busy flag is only thought for volatile register that are predicted using a backdoor/spysignal access, so that the register value is always aligned to the intern value of the DUT.

I hope it helps
Best regards,
Jonathan

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
        


In reply to Jonathan_Alvarez:

Hi Jonathan_Alvarez,

Without going to deep into the code these helper tasks seems interesting, but I was under the impression that the UVM register layer was intended in a higher abstraction level in which you don’t deal with addresses but with register names, maybe I’m wrong, but anyways just my two cents about your intention, and why maybe a task that takes the map and a list of names would be more suitable for the UVM RAL.

Cheers,

-Ronald

In reply to rgarcia07:

Hi rgarcia07,
Thanks for your feedback. I think you are right.
Therefore i have changed both functions to accept start and end register names and from there extract the addresses.

I don’t want to put the whole code again, but the difference would be something like:


 virtual task pve_write_reg_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  string            start_reg_name,               //mandatory start register name
            input  string            end_reg_name=start_reg_name,  //end register name to be written
            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;
            automatic uvm_reg_addr_t start_addr,end_addr;
            automatic logic found_start_register=0, found_end_register=0;

            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_reg_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
            
            
            
            foreach (regs[i]) begin                
                automatic string current_reg_name;
                automatic uvm_reg_addr_t current_reg_address;
                current_reg_name=regs[i].get_name();
                current_reg_address=regs[i].get_address(decided_map);
                if ( current_reg_name==start_reg_name) begin
                    found_start_register=1;
                    start_addr=current_reg_address;
                end
                if ( current_reg_name==end_reg_name) begin
                    found_end_register=1;
                    end_addr=current_reg_address;
                end    
                if ((found_start_register ==1)&&(found_end_register==1)) begin
                    break;
                end
            end
            
            if ((found_start_register ==0)) begin
                `uvm_error( "pve_write_reg_range", $sformatf( "The name of the START register provided %s has not been found in the map:%s. Write cancelled.",start_reg_name,decided_map.get_name()));
                return;
            end
            if ((found_end_register==0)) begin
                `uvm_error( "pve_write_reg_range", $sformatf( "The name of the END register provided %s has not been found in the map:%s. Write cancelled.",end_reg_name,decided_map.get_name()));
                return;
            end
        
            if (start_addr>end_addr) begin
                `uvm_error( "pve_write_reg_range", $sformatf( "The start address 0x%0x of START register:%s is higher than the end address 0x%0x of the END register:%s. That is not allowed. write canceled",start_addr,start_reg_name,end_addr,end_reg_name));
                return;
            end


*************HERE IT CONTINUES AS BEFORE

I hope it would be useful for verifiers, if you have any other tip, would be welcome.
Best regards,
Jonathan

Regarding avoiding to predict while a register is being accessed through the frontdoor, i have found a problem with the previous solutions.

The problem is the following.
If the same register is accessed two times using two different threads/forks. Or even if you write two times sequentially the same register but you monitor and wait for the m_is_busy inside a fork_none (on each register), then you could see that the m_is_busy variable will go to ZERO for a delta time and then again due to the second register access to ONE. That means that one of the threads will unblock its wait statement and do the prediction when busy is 1.
Moreover, it can happens that the second register prediction runs before the first prediction and the order of request is violated. Take in mind that the way parallel forks are executed is not defined the same for all EDA tools, so they run in parallel and some lines of code will run before the others.

To avoid this problem systemverilog has semaphores which internally keep the request ordering with a FIFO, First request input to the resource, is the First served output See 15.3.3 Get() section on the IEEESystemverilog 1800-2012 documentation.

Lets assume that we extend the uvm_reg register type to a type called pve_reg_2#(uvm_reg) which has one semaphore to handle the busy protocol for this register.


        //////////////////////////////////////
        //PREDICT
        /////////////////////////////////////
        //eg. usage
        //top_regm.pve_field_predict( top_regm.inst_HMAC_SHA256_SUM_0.SUM_0,0);
        virtual function void pve_field_predict(uvm_reg_field m_field=null,
            uvm_reg_data_t value,
            logic force_now=0       //means it will predict the value even if frontdoor is being use, it will be overwritten by frontdoor.
            );
            if ((m_field!=null)) begin
                if (force_now==0) begin
                    //FORK
                    fork
                        automatic uvm_reg_field    m_field_fork=m_field;
                        automatic uvm_reg_data_t   value_fork  =value;
                        automatic logic            force_now_fork=force_now;
                        begin
                            pve_reg_2#(uvm_reg)    m_reg_fork;
                            uvm_reg                m_reg_uvm_fork;
                            automatic logic        has_been_locked=0;
                            m_reg_uvm_fork = m_field_fork.get_parent();
                            if ($cast(m_reg_fork, m_reg_uvm_fork)==0) begin
                                `uvm_error( "pve_field_predict", $sformatf( "CASTING ERROR The register with name:%s and field %s  cannot be  casted correctly to pve_reg_2.", m_reg_uvm_fork.get_name(),m_field_fork.get_name() ) );
                            end
                            if  (m_reg_fork.is_busy()) begin
                                //The semaphore waiting queue is first-in first-out (FIFO).
                                m_reg_fork.reg_busy_semph.get(1);//lock because it was busy
                                has_been_locked=1;
                                do begin
                                    if (m_reg_fork.m_is_busy) begin
                                        @(m_reg_fork.m_is_busy);//wait until busy change
                                    end
                                    #0ps;
                                end while (m_reg_fork.is_busy());

                            end
                            assert( m_field_fork.predict(value_fork))    else begin
                                `uvm_error( "pve_field_predict", $sformatf( "The register with name:%s and field %s and REG:%s cannot be  predicted correctly. With value 0x%0x",
                                        m_reg_fork.get_name(),m_field_fork.get_name() ,m_reg_fork.get_name(),value_fork ) );

                            end
                            if  (has_been_locked==1) begin
                                m_reg_fork.reg_busy_semph.put(1);//unlock because it was busy
                            end
                        end
                    join_none
                end else begin
                    pve_reg_2#(uvm_reg)    m_reg_fork;
                    uvm_reg                m_reg_uvm_fork;
                    m_reg_uvm_fork = m_field.get_parent();
                    if ($cast(m_reg_fork, m_reg_uvm_fork)==0) begin
                        `uvm_error( "pve_field_predict", $sformatf( "CASTING ERROR The register with name:%s and field %s  cannot be  casted correctly to pve_reg_2.", m_reg_uvm_fork.get_name(),m_field.get_name() ) );
                    end
                    //FORCE NOW
                    `uvm_info( "pve_field_predict", $sformatf("\nFORCE NOW UNSTABLE PREDICTION since ''force_now'' flag to predict value:0x%0x.\n\
FORCE NOW The FIELD: %s and REG:%s is currently being accessed, therefore FRONTDOOR value/access will be kept and overwrite your predicted value.\n\
Busy is enabled once request is sent to sequencer, however due to arbitration the real reg access can come later on.",
                            value,m_field.get_name(), m_reg_fork.get_name() ),UVM_MEDIUM );

                    assert( m_field.predict(value))    else begin
                        //
                    end
                end
            end else begin
                `uvm_error( "pve_field_predict", $sformatf( "The field specified doesnt exist.") );
            end

        endfunction : pve_field_predict


I would like to have this functionality added to the UVM register library so that we don’t have to extend from the uvm_reg to have this semaphore.