Welcome To UVM World

Thursday, 14 November 2024

System Verilog Assertion State transitions

 Hello All,  today we will understand a unique concept from System Verilog Assertions. when we write down any assertion and run the simulation by asserting the property, it goes through multiple states internally.

we will understand this concept by taking one simple assertion example shown below.

property example();

@posedge(clk) disable iff (rst) a ##1 b |=> c##2 d;

endproperty

Label: assert property (example);

In above property for every positive clock edge we check for 'A' to be 1 followed by 'B' to be 1, this is antecedent. then we have implication operator for next cycle. then 'D' should be 1 , followed by 'D' should be 1 after 2 cycles. this is consequent. Assertion is disabled if rst is enabled.

Below is the diagram for SVA state transition.



A : Inactive to Active: At the beginning assertion will be in inactive state. it looks for first enabling condition to be true. In other sense antecedents first condition should be true to transition to next state which is active. 

In our example assertion, A should be high then assertion changes state from Inactive to Active. 

B: Active to Inactive: When in active state if assertion is incomplete, in other terms if first enabling condition becomes false then state transition from active to Inactive.

In our example assertion, if A goes back to 0 then assertion changes back to Inactive.

C: Active to Pass: When in active state if assertion is disabled state transitions from active to pass. this is also termed as vacuous success.  Another case in which this happens is when simulation completes before all enabling conditions are met.

In our example assertion, when rst is enabled it disables assertion and this state transition occurs. Also when simulation ends without A going to 1.

D: Active to Enable: When all the enabling condition is satisfied or when antecedent is true, then state transitions from active to enable. this is the when first half of the assertion is completed.

In our example assertion, when both a and b are as per the condition , then state transitions from active to enable.

E: Enable to Pass: When consequent is true after implication operator, then state moves from enable to pass.

In our example, when both c and d are true after the implication followed as per the assertion statement, then state transition happens from enable to pass.

F: Enable to Fail: When consequent is false / fails after implication operator, state moves from enable to fail.

In our example, when c is not 1 after implication or when d is not 1 after 2 cycles, state moves from enable to fail

G, H: Both these transition happens directly without any condition. After pass / fail, assertion automatically changes state to Inactive.

Above transition occur independently for each assertion instance. that was about the SVA state change concept, will come up with new topic next time till then keep exploring.






Sunday, 10 November 2024

Driver and Sequencer handshake mechansim in UVM

Hello everyone. Here in this post we will learn how driver and sequencer communicate with each other in UVM in a simple way.

Driver is a component which is used to provide stimulus for DUT. Sequencer is a component which gets the sequence item from sequence and provide it to driver.

We now will understand how driver communicate with sequencer in simple points.
  • Driver class should be derived from uvm_driver base class and should be parameterized with uvm_sequence_item.
  • sequencer class should be derived from uvm_sequencer base class which is also parameterized with uvm_sequence_item.
  • Sequencer has number of methods defined to get sequence items from sequence level. Below is the list.
  1. get_next_item(output REQ req) : This is a virtual task which is a blocking method. Until sequencer gets sequence item from sequence this will be blocked.This must be associated with Item_done.
  2. try_next_item(output REQ req): This is a virtual task and same as get_next_item(). It will return immediately and it is non-blocking method. Only successful getting of sequence item must be associated with item_done().
  3. get(output REQ req): This is a virtual task which is blocking until sequencer gets sequence item. item_done will be implicitly called. explicit item_done shall not be called.
  4. item_done(input RSP rsp=null): It is a function which does not require any argument by default. successful try_next_item or get_next_item must call this method at the end of complete driver action.  
  5. put(input RSP rsp) : This is a virtual task which is used to give response to sequence. This will be used along with get method. This will internally call put_response method.
  6. peek(output REQ req): This method will be internally called by get_next_item, try_next_item and get method. This is the base for all these methods.
  • We are now familiar with the sequencer methods. But now this method should be called in our driver to get the sequence items and to send response back to the sequence.
  • This is done with the help of special TLM connection. uvm_driver base class have a handle of uvm_seq_item_pull_port known as "seq_item_port".
  • Same way uvm_sequencer base class have a handle of uvm_sequence_item_pull_imp known as "seq_item_export". 
  • These port/export is constructed in uvm driver/ uvm_sequencer base class by default.
  • In the agent, which instantiates sequencer and driver these TLM's should be connected. (seq_item_port.connect(seq_item_export)).

       
        


       


  • Now we will go little in depth to understand how driver can access sequencer's method. 
  • below you can see seq_item_pull_port and seq_item_pull_imp class has UVM_SEQ_ITEM_PULL_IMP macros which is common.
      
   

  • We will see what that macro defines
        
  • All the methods required to access sequence items in driver are defined here.
  • So when we call get_next_item from our driver class, the above defined method will be called which will call implementations methods.
  • Implementation is the sequencer which has all the required methods.
This way driver and sequencer communicate with each other. One more point is that it is also possible to access all these methods with sequencer handle.
Hope this post helped in understanding uvm_driver communication in easy, better and detailed way. Stay tuned for more posts on UVM...!! we will also have posts from System Verilog.

Wednesday, 14 February 2024

Difference between p_sequencer and m_sequencer in UVM

The main difference between m_seqeuncer and p_sequencer in one line is the parent and child class handle types. m_sequencer is of parent type and p_sequencer is of child type.

More detailed idea on these sequencers as given below.

m_seqeuncer is the handle of a base class type “uvm_sequencer_base” , which is parent class of "uvm_sequencer".

m_sequencer handle is declared inside “uvm_seqeunce_item” class from which we extend user defined sequence item class.

No alt text provided for this image

whenever we create sequence items for UVM TB (TestBench), m_sequencer gets declared even before creating any user specific sequences.

Whereas p_sequencer handle not even present in out TB until we declare it manually.

When we call sequence.start(sequencer) method, internally set_sequencer method called, which basically points m_sequencer to the user specific “sequencer” handle passed in start method. This is done by static casting as child class can be assigned to parent.

No alt text provided for this image

For p_sequencer handle to declare we call macro inside sequence like this, `uvm_declare_p_sequencer(sequencer).

No alt text provided for this image

This macro declares p_sequncer handle of type sequencer which is passed and dynamically casts p_sequencer to m_sequencer. If we don’t call this macro p_sequencer does not exist. We use p_sequencers inside our sequences to access the methods or properties of user defined sequencer if needed.