ddayan14 Posted March 2, 2016 Report Share Posted March 2, 2016 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. Quote Link to comment Share on other sites More sharing options...
uwes Posted March 3, 2016 Report Share Posted March 3, 2016 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 Quote Link to comment Share on other sites More sharing options...
ddayan14 Posted March 3, 2016 Author Report Share Posted March 3, 2016 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. Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted March 6, 2016 Report Share Posted March 6, 2016 The "doesn't sound like a good idea in terms of memory usage" line seems like premature optimization. Did you try adding a different instance of the frontdoor to each register and see that memory is an issue? Quote Link to comment Share on other sites More sharing options...
ddayan14 Posted March 6, 2016 Author Report Share Posted March 6, 2016 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 Quote Link to comment Share on other sites More sharing options...
tudor.timi Posted March 7, 2016 Report Share Posted March 7, 2016 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). Quote Link to comment Share on other sites More sharing options...
ddayan14 Posted March 9, 2016 Author Report Share Posted March 9, 2016 Hmm, sound nice. I'll check into this. Thanks! Quote Link to comment Share on other sites More sharing options...
dipling Posted March 21, 2018 Report Share Posted March 21, 2018 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? Quote Link to comment Share on other sites More sharing options...
dipling Posted March 21, 2018 Report Share Posted March 21, 2018 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. Quote Link to comment Share on other sites More sharing options...
ddayan14 Posted March 25, 2018 Author Report Share Posted March 25, 2018 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 Quote Link to comment Share on other sites More sharing options...
salman Posted August 3, 2018 Report Share Posted August 3, 2018 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 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.