Generating sequences based on activity on other interfaces

Hi,

I am new to the UVM. Though I understand how simple sequences can be generated and run, I have a problem where I need to generate sequences based on some activity other interfaces which gets processed and based on this data I need to constrain my active sequences. I am not sure how to pass this information on which is a result of activity on one or more than two interfaces to generate sequences on one or more active agents.

I have two active agents, reactive agent and a passive agent. The transactions on the two active agents need to be constrained based on monitored and(or) processed data from the passive agent. Eg. Something like not generating ID’s which are not in use and freeing them as the packet is seen on the output (passive) agent.

Could anyone tell me what are the ways I can use this functionality ? What is the best method, which is scalable / reusable ? Any example would be appreciated.

Thanks in advance,
Madhu

Hi Madhu and thanks for your question.

This topic comes up a lot, I am considering writing more advice about it.

My quick advice to you for this situation, where you have two or more interfaces to your DUT and you wish to co-ordinate some aspect of verification (e.g. sequence contents) between them, is to create a scoreboard that subscribes to analysis port of your passive agent, and maintains some level of ‘model’ of the behavior that is to be shared with the stimulus on the active agents. (similar to a register model that can be referred to/updated by both stimulus (sequences) and analysis (monitors/scoreboards).

For this I would add a ‘config object’ which is a non-component, populated by the scoreboard based on observations, and referred to by the sequences running on the active agents.
Of course in doing so you are creating some OOP coupling between two or more parts of your testbench by the fact they both need a handle to this config object, but this is quite valid when it is done for verification reasons.
Keep the scoreboard lightweight, keep the ‘model’ data in a lightweight config object, and your solution will scale up for reuse.

You can use the config db as a mechanism for managing config objects:

For a technique to give your sequence access to a config object, read:

In reply to gordon:

Hi Gordon,

Thanks for your response. While I go through your proposed solution, is there any example that you can point me to, which can help me understand more easily?

How about passing the information from “scoreboard” sort of component (through analysis port) to active agent’s sequencer or virtual sequencer to coordinate two or more sequences ?

One obvious advantage I see with config db approach is the information from the scoreboard can be accessed by any other component through config db, while passing through analysis port is a point-to-point.

Thanks,
Madhu

In reply to mseyunni:

As you use the UVM more, you will find the right time to use a point-to-point communication such as analysis port, and the right time to use a ‘stateful’ shared data, such as a model, stored in a shared config object. There are good cases for both. In the case you describe (and because you are thinking ahead about scalability and reuse) the shared config object containing a small model of ‘state’ is useful, whereas using an analysis port means that the ‘state’ has to be maintained in your monitor, or in your sequencer, rather than somewhere independent and shared between them.
Many new users start to add this functionality to the monitor, or sequencer, or driver, and then hit problems when it gets more complex or they have to reuse. It’s good to think about partitioning, and decoupling, and reuse up front.
I don’t have any examples to point to here of this particular arrangement, it would be useful to add one in the future.

In reply to gordon:

Thank you Gordon for the explanation.

Hi Gordon,

For this I would add a ‘config object’ which is a non-component, populated by the scoreboard based on observations, and referred to by the sequences running on the active agents.
Of course in doing so you are creating some OOP coupling between two or more parts of your testbench by the fact they both need a handle to this config object, but this is quite valid when it is done for verification reasons.
Keep the scoreboard lightweight, keep the ‘model’ data in a lightweight config object, and your solution will scale up for reuse.

When you say config object it should be extended from uvm_object, right ? How can the object be populated by the scoreboard as it will not have any tlm ports ? Could you please explain ?

Could you please elaborate on what you meant by, Keep the scoreboard lightweight, keep the ‘model’ data in a lightweight config object, and your solution will scale up for reuse - did you mean the scoreboards just feed in observed transactions to the config object and all the required processing should be done in the config object. I am not very clear on “lightweight config object” here.

Also, I would be very grateful, if you can share an example for this interaction.

Regards,
Madhu

In reply to mseyunni:

Hi Gordon,

I created a “config object” which has an array to track the id’s based on request and responses. I would like to put this object in the config db and retrieve it in both system monitor to set/clear the id’s. Again, in vseq/sequence I would like to retrieve a handle to this object and generate id’s not in the list of active_id’s.

class id_tracker extends uvm_object;

endclass

so I have created this config object in my env and then did something like below:

m_id_tracker = new(“m_id_tracker”);

uvm_config_db #(id_tracker)::set(this,“m_id_tracker”,“id_tracker”,m_id_tracker);

and then in my system monitor trying to retrieve like:

if(!uvm_config_db #(id_tracker)::get(null,“”,“id_tracker”,m_id_tracker)) begin
`uvm_error(“Unable to retrieve id_tracker object”)
end

I get the above error for uanble to retrieve the config object. I don’t seem to understand why the config db set / get failed.

Could you explain?

Thanks,
Madhu

Hi Madhu,

There could be a number of reasons for set()/get() not working, but they boil down to:
(1) make sure the set() happens before the get().
(2) make sure the set() and get() have matching lookup strings
(read the Cookbook page on this topic at UVM Configuration Database | Verification Academy)
(3) make sure the same type (including any parameterization) on both ends

If you still are having unexplained problems in the set / get config there is a debug tool built into UVM you can use, and also debug tools in your simulator (e.g. refer to the Questa UVM debug in your documentation)

To debug set/get, add the following code prior to your first ‘set()’ call:

   uvm_config_db_options::turn_on_tracing();

You mentioned access to the config object from your sequence - read also the Cookbook page at Config/ConfiguringSequences | Verification Academy for some useful techniques.

In reply to gordon:

Thank you Gordon. The debug option was useful.

(1) make sure the set() happens before the get().

Could you elaborate on this ? How can you know set happened before get and was successful.

Thanks,
Madhu

One frequent cause of issues with the config_db is that the set() and get() happen in different threads. In the worst case, they may be executed in the wrong order, so the get() will not pick up a valid value because the set() has not yet happened.

If you have this kind of race condition, the debug options shown above will let you know. It emits a message upon each set() and get() so you can quickly check (1) the order, (2) the matching strings and (3) the type. It gives useful output if there is a type mismatch.

I will also need to do what is described in this topic. I think a section in the cookbook about this topic would be very usfull. About using the config db to share info between different agents: how would you guarantee deterministic simulation behaviour?
I assume the simulator can schedule the access to the config db for the different agents in any order. So if you for example would just use an int or so to put in the config db and use this as shared resource I would think you can get different simulation behaviour depending on in what order the simulator chooses to call the agent threads?

Is this indeed a problem?

If so, how to solve this? Using something uvnm_event based, doing NBA into the config_db (is this possible?), …

In reply to gordon:

Yes Gordon. It was quite useful. I was able to find the issue and fix. Now it works. Thanks a lot.

Regards,
Madhu

Hi Nico, I know what question you are asking as I have had similar situations in my own experience, but the config_db is not the place where this problem is intended to be resolved.
In fact, I recommend you use the config_db just once, to pass a handle to a config object that you define, containing whichever fields and access methods you need (or even containing whole models, register models, any shared ‘state’ that may be referred to by your scoreboards, sequences, other components. You can then deal with any application-specific requirements for synchronization by coding access methods within the config object on a case by case basis.

A lot of the race conditions are avoided though by a synchronous approach: if your monitor is synchronous to the protocol clock, then it will only call write() on the analysis port synchronously, and the scoreboard connected to that analysis port (which refers to and updates a config object, say) will do those accesses synchronously. So other components would have a predefined access model controlled directly from your DUT protocol clock. In my experience this removes the need of 90% of all synchronization worries. The other 10% can use semaphores or other access methods.

So I would say, this is not a problem, and is one other reason to use the config_db with objects, not just discrete ints etc. [if the performance overhead is not enough of a reason already]

Interesting topic, yes we can say more about this in the cookbook.

In reply to gordon:

As you said to my initial post that this is a common problem / requirement and it would be quite useful to have a section in cookbook, coupled with an example will be icing on a cake.
Also it will make people to implement things correctly rather than “it works” like approach.

Regards,
Madhu

Gordon, thanks for your answer. I think that you made an excellent point regarding putting objects with access methods in the conf db instead of a plain int for example.

What I do not get though is how the synchronous approach would remove a lot of non deterministic behavior, just like 2 clocked processes in a design that use blocking assignments to communicate are problematic.
In my uvm setup I have the following: I have 2 agents 1 and 2. Driver1 and driver2 are synchronous to the same clock. Sequence1 should react (decide on the next seq item content) to what happens in agent2. Suppose at a certain clock edge seq items in driver1 is finished, so sequence1 will have to make a new seq item. To decide on the seq item content (content A or B) it will look into the config db for the status in agent2. This status is set by sequence2 when it’s seq item was finished. Suppose this was in the same clock cycle and therefor the seq items in the 2 drivers finish in the same delta cycle.
When a simple shared state X is used I think the following 2 scheduling schemes can happen:

sequence1 checks for state X: X is not set, so a new seq item with content A is made.
sequence2 sets state X

Or

sequence2 sets state X
sequence1 checks for state X: X is set, so a new seq item with content B is made.

In reply to NiLu:

Thinking about your suggestion to put objects with access methods in the config db, would putting an object of following class solve the issue:

class C;
  local int x;
  task set (int i);
    x <= i;             // NBA
  endtask
  function int get;
    return x;
  endfunction
endclass

Now x would be set (agent2) using a non blocking assignment, solving hopefully the non deterministic behavior.
Correct, would this work?

It is common for monitors to update a shared state, and for drivers or sequencers to use that to make decisions about future stimulus.
However, it is not possible for this to happen synchronously on the same clock edge when you go via the testbench. You could only do that by writing real RTL outside the UVM testbench to make that link between 2 interfaces.

The status update from agent2 would happen after a clock edge that sampled it e.g. into agent2’s monitor, and that status would be available to agent1’s sequencer or driver in time to (deterministically) set up the next activity on the next clock edge.

NBA assignments in classes are only allowed in the newest SV-2012 and are not commonly used in UVM testbenches.

Thanks for taking time to look into this.
I am happy that this is a common problem, meaning there must be a simple solution.

Maybe my question was not 100% clear: I do not require sequence1 to react to agent2 on the same clock edge in time. But the driver/monitor of both agents share the same clock. So without artificial delays the activity in the 2 agents would occur at the same moments in time (eg on the neg edges of the shared clock). My concern is that without NBA-like communication between the agents the reaction of sequence1 to agent2 could happen on the next clock edge or one extra clock edge later, depending on in which order the simulator calls the agents.

I.e. I do not understand how the reaction would be deterministically always on the next clock cycle, if we do not know in what order the shared state gets updated (agent2) and evaluated (agent1)?

Maybe the order in which the shared state gets updated and read is known, and I am wrongly assuming this order is not guaranteed? If so, what mechanism sets this order?

Got it. You care about determinism from a monitor report to a sequence/driver activity reacting to that, where both are on the same clock edge.

  1. add some SV code to deal with that, on a case by case basis. In the past I have used the negedge of the clock for this, knowing that all consequences of the previous activity have settled down by then. Call it a ‘setup time’ of sorts.
  2. Manually adding delays is almost always not a good solution.
  3. I don’t think NBA is useful here but we have not had NBA in classes before so a new pattern may emerge. Let us know if you invent something that works for your use case.
  4. Some situations will not be ideal for a shared config object. If you need a ‘communication channel’ use an analysis port on the monitor/scoreboard and have your sequencer subscribe to updates from it, so you know the update was sent before you make a decision based on it.

There is no mechanism in the UVM config provision that can help here (which I think is a good thing. If UVM added one, it would slow down config object access for everyone)

Finally, consider instead orchestrating your stimulus from above (a virtual sequence calling sequences) to achieve your verification objectives without needing this link.

In reply to gordon:

Yes, that is indeed exactly what I care about.

We agree on (2), and I think (1) is indeed a doable and robust solution.
(3) suffers from the same problem I think because the analysis fifo has no evaluate/update, NBA mechanism.

I come from a vhdl, systemc background where channels with evaluate/update (like signals) between processes guarantee deter minism. I think NBA comes closest to this. I think I will give (4) a try, but it might be hard to proof that it is a deterministic solution.