Jump to content

User defines frontdoor sequence can't support simultaneous accesses


Recommended Posts

Hi

We're trying to use a used defined frontdoor sequence for our register model. Most of our registers are accessed through an indirection mechanism, so creating a custom frontdoor sequence looks like the way to go.

Everything works well, except when parallel threads are trying to simultaneously access the model (*NOT* to the same register of course) we're getting into some trouble - the frontdoor sequence is started again causing a UVM_FATAL error. Here's the code from uvm_reg:

         // ...VIA USER FRONTDOOR
         if (map_info.frontdoor != null) begin
            uvm_reg_frontdoor fd = map_info.frontdoor;
            fd.rw_info = rw;
            if (fd.sequencer == null)
              fd.sequencer = system_map.get_sequencer();
            fd.start(fd.sequencer, rw.parent);
         end

Now either we can set a unique instance of the frontdoor sequence to each register (doesn't sound like a good idea in terms of memory usage), or we need to have some sort of a semaphore for the whole map (not sure how exactly.

We can also try to "clone" the sequence instead of just starting it, but that involves changing the code of UVM...

 

What do you guys propose?

 

Thanks.

Link to comment
Share on other sites

hi,

 

 

why dont you just use grab/lock from the frontdoor sequence on the sequencer running your parallel sequences ? obviously each thread should run an own instance of the frontdoor sequence in order that each instance can perform the necessary access sequence in an atomic fashion.

 

essentially the lock/grab scheme allows you to block other sequences from executing items while your sequence is in an atomic/critical section.

 

/uwe

Link to comment
Share on other sites

Locking the sequencer will not work since the FATAL is caused even before it enters the arbitration queue. The sequence is in a "Running" phase, and trying to start it again will cause the error. Locking the reg_adapter sequencer can work only when no frontdoor sequence is involved.

How can I specify a different sequence to each thread? They don't even use it directly, they simply do something like:

reg_model.reg_a.write(...)

Trying to synchronize all threads that are accessing the reg_model is pointing me to somehow have a semaphore on the reg_map. But then again, I'm not really sure how...

 

Thanks for your comments.

Link to comment
Share on other sites

Hi Tudor,

 

Not yet, it's on our fallback options. But just counting the number of registers, we're talking about create additional tens of thousands of sequences. I'm sure you can agree even before trying that there are much better options.

I'm now thinking about overriding the start task of the frontdoor sequence on do something like this:

virtual task start (uvm_sequencer_base sequencer,
                    uvm_sequence_base parent_sequence = null,
                    int this_priority = -1,
                    bit call_pre_post = 1);
   semaphore.get();
   super.start(sequencer, parent_sequence, this_priority, call_pre_post);  
   semaphore.put();
endtask

unfortunately there is something I'm not sure how to solve. When the sequence is started (during uvm_reg::do_write for example), the uvm_reg_item that is required by the frontdoor sequence is not passed as an argument to the function. It is rather assigned directly to the sequence. This means the parallel thread will override the previous item that the current running thread is using (since both threads have a handle to the same sequence instance). This is specially problematic for register reads, since the item is used to return the read data.

Does anyone have a nicer solution, or should we go with the dirty "sequence per register" approach?

Thanks

 

Thanks

Link to comment
Share on other sites

If you have that many registers you need to customize the way you access, why don't you just create your own sub-class of uvm_reg_map that can handle this? You'll just need to override the do_read/write(...) tasks. The best part is that you'll only have one object handling this (the map to which you add the registers) as opposed to many (the frontdoors you would add to the registers).

Link to comment
Share on other sites

  • 2 years later...

Unaware of the existence of this thread I ended up with exactly the same "solution" (start override and semaphore) and side effect :-)

On 9.3.2016 at 11:45 AM, ddayan14 said:

Hmm, sound nice. I'll check into this.

Have you reached a usable solution? Can you post it here?

Link to comment
Share on other sites

My take:

Wrapper object to contain a mutex

class ral_mutex_t extends uvm_object;
    `uvm_object_utils(ral_mutex_t)

    semaphore FrontDoorMutex = new(1);
   
    // constructor ...
endclass

  
class uvm_reg_mutex extends uvm_reg;
    `uvm_object_utils(uvm_reg_mutex)

    ral_mutex_t  ral_mutex;

    function new(string names="");
       super.new(names, 32, UVM_NO_COVERAGE);
    endfunction : new

    task write( ... ); // 
        if(!uvm_config_db #(ral_mutex_t)::get(null, "", "ral_mutex", ral_mutex))
            `uvm_fatal(get_type_name(), "Error fetching ral_mutex")

        ral_mutex.FrontDoorMutex.get(1);
        super.write(...);
        ral_mutex.FrontDoorMutex.put(1);
    endtask
endclass

class my_ral_reg extends uvm_reg_mutex;
    `uvm_object_utils(my_ral_reg)
      // define fields
    function new(string names = "register_info");
        super.new(names);
    endfunction
    virtual function void build();
	//	build register
    endfunction
endclass
      
          

Would have been great to have a UVM semaphore pool just like the event pool... but I can live with wrapper objects and SV semaphores.

Link to comment
Share on other sites

Yeah, I followed Tudor's suggestion:

typedef class indirect_reg_map;

class my_reg_block extends uvm_reg_block;

   semaphore blk_sema;

   ... new, build with indirect map ...

endclass

class indirect_reg_map extends uvm_reg_map;
   my_reg_block model;

   // We override the do_read/write tasks and implement the indirection
   task do_read(uvm_reg_item rw);
      model.blk_sema.get();

      ...

      model.blk_sema.put();
   endtask

   ...

endclas

 

Link to comment
Share on other sites

  • 4 months later...

Hi

A colleague and I came up with the following solution which seems simple enough and works well. We simply overrode the start() task of our frontdoor class like so :

class my_frontdoor extends uvm_reg_frontdoor.......


  task start(uvm_sequencer_base sequencer,
                     uvm_sequence_base parent_sequence = null,
                     int this_priority = -1,
                     bit call_pre_post = 1);
        
    my_sema.get();
    super.start(sequencer,parent_sequence, this_priority,call_pre_post);
    my_sema.put();
  endtask;

Ideally I would have liked to override the pre/post_start() hooks but suprisingly the pre_start is already called too late ( after the UVM_FATAL already fires).

Salman

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...