Uvm reg predict issue

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