Jump to content

jeremyr

Members
  • Posts

    2
  • Joined

  • Last visited

jeremyr's Achievements

Member

Member (1/2)

0

Reputation

  1. Hi, I'm facing a class inheritance problem that I don't quite understand related to accessing parent class typedef when that parent is passed as type via opaque class type extension. I'm using this for base classing in the UVM library, but it is not specific to UVM. In the following example, I can extend the uvm_report_catcher and directly implement the abstract catch function without needing to alias the enumeration type. virtual class uvm_callback extends uvm_object; protected bit m_enabled = 1; ... endclass virtual class uvm_report_catcher extends uvm_callback; typedef enum { UNKNOWN_ACTION, THROW, CAUGHT} action_e; pure virtual function action_e catch(); endclass class my_catcher extends uvm_report_catcher; virtual function action_e catch(); if(m_enabled) `uvm_info("MYCTCHR", "Saw msg when enabled", UVM_LOW) return THROW; endfunction endclass Even though action_e is declared/defined in scope of uvm_report_catcher, since my_catcher is an extension I have no issue accessing then same type for the the catch() return value. Furthermore, I, of course, have no issue accessing the uvm_callback::m_enabled protected class member. However, I have inserted a middling type-parameterized opaque class between. virtual class my_object_param#(type T = int) extends T; // middling class function new(string name = ""); super.new(name); endfunction // some other stuff common in all my object instantiations endfunction virtual class my_catcher extends my_object_param#(uvm_report_catcher); function new(string name = "my_catcher"); super.new(name); endfunction virtual function action_e catch(); // compile error if(m_enabled) `uvm_info("MYCTCHR", "Saw msg when enabled", UVM_LOW) return THROW; endfunction endclass Here, I get a compiler error on action_e indicating it is not a valid type (i.e., unknown type). I would think that, because the resolved inheritance hierarchy is: uvm_report_catcher +- my_object_param +- my_catcher that action_e would be in-scope for both my_object_param and my_catcher. I can workaround this by specializing to my own report catcher first, then into my_catcher. virtual class my_report_catcher extends my_object_param#(uvm_report_catcher); typedef uvm_report_catcher::action_e action_e; endclass class my_catcher extends my_report_catcher; virtual function action_e catch(); // OK now if(m_enabled) `uvm_info("MYCTCHR", "Saw msg when enabled", UVM_LOW) return THROW; endfunction endclass Notice that in order to access action_e type I had to alias this into my_catcher's parent class, but m_enabled was always accessible. That seems strange to me. It seems incongruous to simply resolve the m_enabled binding while requiring the alias on the typedef. Nonetheless, this is clearly understood spec behavior -- the compiler error seen and the non-issue on m_enabled is not unique to a specific simulator. Am I missing something on the opaque extension that would be more appropriate? It's not a big deal to alias the typedef in this case since the UVM library isn't changing all that often. However, I do wish to avoid needing to alias each new typedef as base classes evolve. Thoughts? Is there another/better forum to direct this question? Thanks, Jeremy Ridgeway
  2. Hi, I am facing a threading issue when a single register instance is accessed multiple times simultaneously. Given two separate threads scheduled at the same time and each calling the uvm_reg::write() task, I see the first one to execute the call does correctly take the semaphore on uvm_reg::write().XatomicX(1) task call, and the second thread correctly blocks when it attempts uvm_reg::write().XatomicX(1) task. The first thread, in the uvm_reg::do_write() task safely immediately returns on its uvm-reg::do_write().XatomicX(1) task call and correctly executes the bus transaction. The problem arises when the uvm_reg::do_write() task returns the semaphore in its XatomicX(0) call. This: 1 - immediately wakes Thread2 which takes the semaphore and assigns its process to the local m_process variable, then continues into the uvm_reg::do_write() and correctly executes its bus transaction; 2 - once Thread2 blocks while its bus transaction takes place, Thread1 returns from uvm_reg::do_write() and hits its uvm_reg::write().XatomicX(0) task call. At this point in time, Thread2 correctly owns the semaphore and has its process reference stored in uvm_reg::m_process. However, when Thread1 calls XatomicX(0) just before returning from uvm_reg::write(), there is no check on why the semaphore is owned. From UVM-1.2 uvm_reg.svh: task uvm_reg::XatomicX(bit on); process m_reg_process; m_reg_process=process::self(); if (on) begin // we don't care about this branch right now end else begin // Maybe a key was put back in by a spurious call to reset() void'(m_atomic.try_get(1)); m_atomic.put(1); m_process = null; end endtask: XatomicX Because Thread2 correctly owns the semaphore AND there no checking done on ownership when bit on == 0, Thread1 is able to incorrectly put back the semaphore and invalidate the m_process reference. (The spurious reset comment does not apply). Because of my testbench setup, my test has to immediate write this same register with another value. So, Thread1 preforms a second write() call on the same register. This causes warnings to be emitted from the field because the register is currently busy (in the bus transaction): function void uvm_reg_field::set(...); ... if (m_parent.is_busy()) begin `uvm_warning("UVM/FLD/SET/BSY", $sformatf("Setting the value of field \"%s\" while containing register \"%s\" is being accessed may result in loss of desired field value. A race condition between threads concurrently accessing the register model is the likely cause of the problem.", get_name(), m_parent.get_full_name())) end ... endfunction and it clobbers the register value in the RAL model. Because Thread2 is currently busy on bus transaction, when it returns it may perform the do_predict call while Thread1 is busy on its bus transaction for the same register. I'm not sure what will happen here. I am thinking that the XatomicX should have a check on the process when bit on == 0, similar to when the bit on == 1, to ensure the clearer of the semaphore is the same owner who claimed it. However, I am not sure how this would affect other testbenches. For example, is it possible to split the write() task into separate threads when the driver returns responses separately (i.e., the uvm_reg_adapater.provides_responses == 1). Basically, is it possible to start a register transaction in one thread (taking the semaphore) and end it in another transaction? Also, I see that there is a similar, though different solution than I'm suggesting, already posted (with an approved but not fixed mantis bug): I also see that in my release of UVM-IEEE, the uvm_reg class has the exact same XatomicX locking mechanism implemented. The only current solution I see that handles this outside of UVM and enables multi-threading access of the same register is with, yet another, semaphore that makes the internal uvm_reg semaphore all but moot. However, this is not desirable since I need to enforce a guideline on my team rather than have it built-in. Am I missing something? Better approaches? Thanks for your feedback! Jeremy Ridgeway
×
×
  • Create New...