Logger Posted October 27, 2012 Report Share Posted October 27, 2012 Using uvm_reg_sequece the completion model of the interface is abstracted. How do I retain control of deciding whether the register access is executed in a blocking vs non-blocking manner? I want the ability to write a uvm_reg_sequence that can do both types of accesses under user control. -Ryan Quote Link to comment Share on other sites More sharing options...
uwes Posted October 28, 2012 Report Share Posted October 28, 2012 i think the recommended path is to make an extension container object and add there all additional properties for your reg2bus translation process. pass the object in the extension parameter of your register access. you could for instance add a field there "is_blocking" and use that field in your register adapter to make the downstream bus access blocking/non-blocking. Quote Link to comment Share on other sites More sharing options...
Logger Posted October 30, 2012 Author Report Share Posted October 30, 2012 That seems like a reasonable idea, however there is a more basic problem. RAL does not seem to support non-blocking protocol at all. Here is the snip from uvm_reg_map::do_bus_write() where it executes the uvm_sequence_item. Notice the last line. ================================================== rw.parent.start_item(bus_req,rw.prior); if (rw.parent != null && i == 0) rw.parent.mid_do(rw); rw.parent.finish_item(bus_req); bus_req.end_event.wait_on(); //// <- This prevents non-blocking behavior. ================================================== Even if the driver calls item_done() before the transaction has completed, thus enabling non-blocking behavior. RAL is then calling end_event.wait_on(), forcing blocking behavior again. -Ryan Quote Link to comment Share on other sites More sharing options...
janick Posted October 30, 2012 Report Share Posted October 30, 2012 RAL is blocking because it needs to know the response of a transaction so it can predict its effect in auto-predict mode. Why not simply fork the call to reg.read()/reg.write()? Quote Link to comment Share on other sites More sharing options...
Logger Posted October 30, 2012 Author Report Share Posted October 30, 2012 What I'd like to write is something like this: ========================================= virtual task body(); uvm_status_e src_status, mask_status; logic [31:0] src_value, mask_value, src_sample; uvm_reg r; src_value = 'ha5; write_reg(model.blk.int_src, src_status, src_value); mask_value = 'hb4; write_reg(model.blk.int_mask, mask_status, mask_value); // somehow wait for the last write to complete? // model.blk.int_mask.updated_event.wait_on(); read_reg(model.blk.int_src, src_status, src_sample); endtask ========================================= However, using your advice I am able to achieve the desired stimulus by writing this: ========================================= virtual task body(); uvm_status_e src_status, mask_status; logic [31:0] src_value, mask_value, src_sample; uvm_reg r; fork begin src_value = 'ha5; write_reg(model.blk.int_src, src_status, src_value); end begin mask_value = 'hb4; write_reg(model.blk.int_mask, mask_status, mask_value); end join read_reg(model.blk.int_src, src_status, src_sample); endtask ========================================= It works, but unfortunately there is a race in that code. There's no gaurantee what order the forked threads will execute. Which means I have to further add more synchronization code to enforce the thread ordering. With a lot of registers to set, this becomes quite cumbersome. It would seem that RAL should provide blocking and non-blocking read and write functions. If non-blocking is used, RAL would fork a thread to get the response. The non-blocking read could return some kind of event object to wait on. ========================================= virtual task body(); uvm_status_e src_status, mask_status; logic [31:0] src_value, mask_value, src_sample; uvm_reg r; src_value = 'ha5; write_reg_nb(model.blk.int_src, src_status, src_value); mask_value = 'hb4; write_reg(model.blk.int_mask, mask_status, mask_value); read_cmpl_event = read_reg_nb(model.blk.int_src, src_status, src_value); // non_blocking so can be a function // src_status, and src_value are invalid until after the following event fires read_cmpl_event.wait_on(); // src_status and src_value are now valid. endtask ========================================= -Ryan Quote Link to comment Share on other sites More sharing options...
Logger Posted October 31, 2012 Author Report Share Posted October 31, 2012 I have created an extension to uvm_reg_sequence that hides the forking and thread ordering I was complaining about. However, am I being overly pessimistic about thread ordering? The LRM is explicit that "At any time while evaluating a procedural statement, the simulator may suspend execution and place the partially completed event as a pending event in the event region. The effect of this is to allow the interleaving of process execution, although the order of interleaved execution is nondeterministic and not under control of the user." But in practice, none of the simulators switch from one process to another willy-nilly. They typically don't switch unless the active process hits a blocking statement. The LRM really has too much flexibility here. ========================================================== class bu_reg_sequence #(type BASE=uvm_sequence #(uvm_reg_item)) extends uvm_reg_sequence #(BASE); local int next_thread_id = 0; local mailbox#(int) mb = new; local event mb_pop; virtual function uvm_event read_reg_nb( input uvm_reg rg, ref uvm_status_e status, ref uvm_reg_data_t value, input uvm_path_e path = UVM_DEFAULT_PATH, input uvm_reg_map map = null, input int prior = -1, input uvm_object extension = null, input string fname = "", input int lineno = 0 ); int thread_id = next_thread_id++; uvm_event done_event = new; fork begin int selected_thread_id; mb.put(thread_id); mb.peek(selected_thread_id); // gaurantee super.read_reg is called in the order read_reg_nb was called. while (selected_thread_id != thread_id) begin @(mb_pop); mb.peek(selected_thread_id); end fork super.read_reg(rg,status,value,path,map,prior,extension,fname,lineno); begin #0; // gaurantee call to super.read_reg happens before popping the thread id. mb.get(selected_thread_id); ->mb_pop; end join done_event.trigger(); end join_none return done_event; endfunction // read_reg_nb virtual task read_reg( input uvm_reg rg, output uvm_status_e status, output uvm_reg_data_t value, input uvm_path_e path = UVM_DEFAULT_PATH, input uvm_reg_map map = null, input int prior = -1, input uvm_object extension = null, input string fname = "", input int lineno = 0 ); // read_reg needs to be redefined to use the ordering mechanism provided by read_reg_nb uvm_event done_event = read_reg_nb(rg,status,value,path,map,prior,extension,fname,lineno); done_event.wait_on(); endtask // read_reg virtual function uvm_event write_reg_nb( input uvm_reg rg, ref 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 int prior = -1, input uvm_object extension = null, input string fname = "", input int lineno = 0 ); int thread_id = next_thread_id++; uvm_event done_event = new; fork begin int selected_thread_id; mb.put(thread_id); mb.peek(selected_thread_id); // gaurantee super.write_reg is called in the order write_reg_nb was called. while (selected_thread_id != thread_id) begin @(mb_pop); mb.peek(selected_thread_id); end fork super.write_reg(rg,status,value,path,map,prior,extension,fname,lineno); begin #0; // gaurantee call to super.read_reg happens before popping the thread id. mb.get(selected_thread_id); ->mb_pop; end join done_event.trigger(); end join_none return done_event; endfunction // write_reg_nb virtual task write_reg( input uvm_reg rg, 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 int prior = -1, input uvm_object extension = null, input string fname = "", input int lineno = 0 ); // write_reg needs to be redefined to use the ordering mechanism provided by write_reg_nb uvm_event done_event = write_reg_nb(rg,status,value,path,map,prior,extension,fname,lineno); done_event.wait_on(); endtask // write_reg endclass // bu_reg_sequence =============================================================== That allows me to write the following in my sequence body. =============================================================== // stack up two back to back AXI write transactions src_value = 'ha5; write_reg_nb(model.blk.int_src, src_status, src_value); mask_value = 'hb4; write_reg(model.blk.int_mask, mask_status, mask_value); // last write was blocking, so the following code waits for that last write to complete. // stack up two back to back AXI read transactions read_reg_nb(model.blk.int_src, src_status, src_sample); ev = read_reg_nb(model.blk.int_mask, mask_status, mask_sample); ev.wait_on(); =============================================================== Seems like a nice use model that could be built into uvm_reg_sequence. -Ryan 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.