Changing sequemcer handle in reg_seqs still triggers register writes

,

HEllo ALl,

I am facing a unique problem. I have a RAL implemented and register sequence which works on I2C and have I2C driver, transaction and sequencer respectively.
Now once i made a typo while declaring the sequencer for my reg sequence as follows


reg_seqence_A_h.start(i2c_seqencer) //usual I2C seqence writes regA
reg_seqence_B_h.start(another_sequencer) //another reg_seq but the seqencer handle passed was not correct
                                         //Writes regB

After this simulation i was able to see the desired register writes on both the register’s, how is this possible as i am not passing the proper sequencer how does my 2nd seq knows what writes to do and where? How adapter is getting information of transaction here as both the seqencers are with different transaction. I am puzzled

In reply to msuthar:

Did you previously call the uvm_reg_map function set_sequencer() ? That would set the sequencer and the adapter for whatever reg_map you are utilizing.

In reply to dhserfer:

Yes i have declared in my environment class

reg_block.regmap.set_sequencer(i3c_seqr)

In reply to msuthar:

Your Piece of Code Shows you are accessing the Registers through two different Interfaces, i.e. I2C and something else.
Is this really the case and your intention?

In reply to chr_sue:

That second line had a mistake, It was intended for I2C interface but due to typo it picked up another interface. But still the register writes were going through for second equence whhich is really not expected.

In reply to msuthar:

I understood what you have explained. My question is if the sequencer you are using in the second line is existing?

In reply to chr_sue:
Yes it is existing…but not related to RAL

In reply to msuthar:

Unfortunately, uvm_reg_sequence ignores the sequencer you pass to start(). Which IMHO is a bug. Try passing null, it’ll work just fine.

In your sequence, if you access the read, write, mirror, and update tasks directly from the RAL model there’s really nothing UVM can do about that, because the context is lost and there’s no way for it to determine which sequencer/map should be used anyway.

However, if you use uvm_reg_sequence::read/write/update/mirror to indirectly access the register model, it really should but doesn’t use the sequencer provided to start().

I use my own replacement for uvm_reg_sequence that does the right thing, since UVM can’t seem to bother.

In reply to warnerrs:

Each access of the RAL has one or more dedicated interfaces. In your case it is just 1 functional interface/agent. There is only one sequencer belonging to this interface.
If it is a bug or not is not clear to me. It makes the Setup more easy and gives us freedom.
In the OVM it was differently. You had to define explicitly which sequencer should be used for executing reg sequences.

In reply to warnerrs:

In reply to msuthar:
In your sequence, if you access the read, write, mirror, and update tasks directly from the RAL model there’s really nothing UVM can do about that, because the context is lost and there’s no way for it to determine which sequencer/map should be used anyway.
However, if you use uvm_reg_sequence::read/write/update/mirror to indirectly access the register model, it really should but doesn’t use the sequencer provided to start().

Thanks,
Can you please elaborate further on this?

In reply to msuthar:

Let’s look at the API involved. First, the write task in uvm_reg.

task uvm_reg::write(output uvm_status_e      status,
                    input  uvm_reg_data_t    value,
                    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);

Usually you’ll find authors are lazy and only provide the minimum parameters required. In other words, status and value. You’ll supply path if you want to change it to a backdoor transaction, or you’ll supply map if you want to pick between one of multiple maps ( and by extension sequencers ).

What happens if you go with the default parameters? Then the default map ( the first map registered ) will be selected and used.

The parent parameter is interesting, as it suggests you can pass context to the register model. In theory, this context could be used to determine which sequencer your sequence is running on and by extension which map to use. So let’s dig deeper to see what happens.

uvm_reg::write calls uvm_reg::do_write. Unless you supplied a map when you called ::write(), as we enter this task, rw.map and rw.local_map are null.

task uvm_reg::do_write (uvm_reg_item rw);

   uvm_reg_cb_iter  cbs = new(this);
   uvm_reg_map_info map_info;
   uvm_reg_data_t   value; 

   m_fname  = rw.fname;
   m_lineno = rw.lineno;

   if (!Xcheck_accessX(rw,map_info,"write()"))
     return;


And unless you specified to use a backdoor the Xcheck_accessX() method does the following.

   if (rw.path != UVM_BACKDOOR) begin

     rw.local_map = get_local_map(rw.map,caller);

And if rw.map is null here, rw.local_map is set to the the default map. This brings us back to ::do_write() to execute your transaction.

// EXECUTE WRITE...
   case (rw.path)
      
      // ...VIA USER BACKDOOR
      UVM_BACKDOOR: begin
         //...
      end

      UVM_FRONTDOOR: begin

         uvm_reg_map system_map = rw.local_map.get_root_map();

         m_is_busy = 1;

         // ...VIA USER FRONTDOOR
         if (map_info.frontdoor != null) begin
            //...
         end

         // ...VIA BUILT-IN FRONTDOOR
         else begin : built_in_frontdoor

            rw.local_map.do_write(rw);

         end

Despite the fact that if parent != null, this information is available, nowhere in this call stack, is the sequencer your sequence is running on consulted. Your register transaction is executed using either the map you supplied or the default map. So if you don’t supply one, you’ll use the default.

So, in your case I’m assuming you are not supplying the map parameter to ::write(). Or if you are, you are supplying the same map regardless of which sequencer you’re running on.

If you supply the parent parameter to uvm_reg::write(), or use the uvm_req_sequence::write_reg() helper task which does that for you, there is sufficient context information for UVM to figure all this out for you. But it doesn’t. :(