> There’s never time to do it right, but always time to do it twice.
![[puppy-driver.JPG]]
## How to (not) mess up your driver
When learning a new technology, especially on your own, it’s very easy to become confused by new concepts. One can misunderstand something in an infinitely large amount of ways, while the set of correct interpretations is rarely greater than a singleton. One such misconception I see in people who only start their UVM journey is about a responsibility division between a driver and a monitor and, sometimes, a sequence.
Let’s start with a monitor and a driver. The idea is very simple: driver drives and monitor monitors, what can be easier? Then, what happens when you have an interface with a handshake?
| Signal | Direction |
| ------ | --------- |
| data | IN |
| valid | IN |
| ready | OUT |
The `data` should be stable while `valid` is HIGH until `ready` is also HIGH. Now driver’s behavior depends on the DUT's responses. Who shall check the `ready` signal if the driver drives and the monitor monitors? The correct answer is the driver. It may be obvious to most of you, but it's not for those who do the very first steps in verification.
How come it doesn't violate the [single responsibility principle](https://en.wikipedia.org/wiki/Single_responsibility_principle)? Because the initial definition of driver and monitor was oversimplified. Let's rectify it and include sequences for the wholeness of the picture.
> The sequence is an object that decides what to drive on the interface. The driver is a component that converts class-based transactions to the *interface activity*. The monitor is a component that converts valid *interface activity* to class-based transactions.
So, if reading the interface to produce correct *interface activity* is needed, then it's part of the driver's responsibility.
Now let's imagine for a moment that we decided to go with the initial idea and to read signals only through the monitor. Then the driver shall get this information somehow. An obvious solution would be to have a monitor handle inside the driver and to implement some API in the monitor. The next logical question is why we even need to separate the driver from the monitor if it only brings additional complex interactions. Let's lump everything together! By doing this we arrive at something called BFM (bus functional model), a single entity handling both driving and monitoring. This approach can be very lucrative as we can get rid of all UVM complexities and finally do our work unimpeded... At the cost of having to swim in a sea of spaghetti for the rest of our lives. The BFM solution is bulky, unstable and the code is hard to read and modify.
## Arguments against
Of course, the case above is very simple and that real life is more complicated. So one may argue that having a driver-monitor connection is necessary to handle complex situations. Let's imagine what the arguments can be.
### We shall avoid code duplication
Most of the time the condition that the driver expects to complete driving of a transaction is fairly simple and the complexity that always accompanies code reuse far outweighs any pros. A common configuration object can encapsulate whatever constants and operations modes you have in the protocol.
But sometimes the response can be really complex. If there's a wide bus parts of which encode the type of message, then the decoding logic can be quite complex and shall not be duplicated. However, if DUT's response is so complex it can be seen as a transaction, it shall be treated as such and the right approach would be to implement a *reactive agent pattern* instead of handling everything in a driver. More on this later.
### My stimulus heavily depends on DUT responses
That's a slightly different scenario from the handshake problem. Here the next interface activity isn’t just blocked by, it’s chosen based on DUT responses. So there may be a temptation to put this deciding logic in the driver, whether you observe responses in the driver or with some ungodly monitor/driver connection. But following the single responsibility principle and the definition above, the decision on what to drive next shall be made by the same entity, that decides what to drive in the first place: by the sequence. The driver shall not have its own opinion, it simply drives whatever told to. Once again this is neatly handled by the *reactive agent pattern*.
### I have a slave agent
Here the agent does not initiate interface activity, it only consumes DUT requests and responds to them. A straightforward solution here is to abandon sequence as unnecessary and just make a BFM, but you guess what? Reactive agent.
## Reactive agent
Smarter people than me wrote about this pattern, so I will just outline the general idea and leave the links. Maybe I’ll write a complete example someday in the future.
Below is a diagram of a reactive master agent (although you can implement a slave in this manner too, if you want to). Unlike the one-directional scenario of the sequencer sending one request after another to the driver, here the driver returns responses and the sequence can use them to select appropriate further requests.
![[Reactive master.drawio.png]]
This is a good approach to use if the flow of requests depends on the responses from DUT. It allows us to keep decision-making contained in the sequence and the driver just handles the interface. Creating new test scenarios is just writing new sequences, as usual.
You can find a good guide on reactive agents in \[[[#Further reading|1]]\].
Now let's take a look at the reactive slave agent. In this scenario, the DUT is a requester and the agent is a responder.
![[Reactive slave.drawio.png]]
As in the previous approach, all decision-making is encapsulated in a sequence. Compared to BFM, this pattern makes every component simpler, new scenarios can be added without touching the agent.
This concept is described in detail in \[[[#Further reading|2]]\].
## I don't care, I just need a driver-monitor connection
If that's something you really want, I suggest using a shared communication object, created in the build phase of the agent and set to the driver and the monitor.
![[shared object.drawio.png]]
The monitor uses it to send a notification that a certain condition has happened. The driver waits for the notification from the shared object before continuing its work. Compared to the BFM or "the driver has a handle to the monitor" solutions it keeps components as separate as possible and makes all communication channels between them clear. In addition, this shared object is not limited to the single agent, it can be used anywhere in the testbench. Take care, however, as it opens another spaghetti floodgate.
The internal implementation of the shared object is up to you. It can be based on SystemVerilog `event`, `uvm_event`, queues and mailboxes or whatever. UVM Cookbook suggests implementing it inside the configuration object, however, having a separate object is fine. See *UVM Cookbook* \[[[#Further reading|3]]\] **Wait for a Signal** for more on this topic.
## Conclusion
- Driver is not forbidden from reading an interface.
- Driver and monitor shall be kept as independent as possible.
- Use reactive agent pattern for proper communication.
- Use a shared communication object for dirty things.
## Further reading
1. Clifford E. Cummings, Heath Chambers, Stephen D'Onofrio: [UVM Reactive Stimulus Techniques](http://www.sunburst-design.com/papers/CummingsDVCon2020_UVM_ReactiveStimulus.pdf)
2. Mark Litterick, Jeff Montesano, Taruna Reddy: [Mastering Reactive Slaves In UVM](https://assets-global.website-files.com/63f4bb21bd5303fe472ad00e/649575fcbf99287ba25f20e8_litterick_uvm_slaves2_paper.pdf)
3. [UVM Cookbook](https://verificationacademy.com/cookbook/uvm-universal-verification-methodology/)