How do I properly structure UVM sequence flows?

Oct 23, 2026 Last reply: 17 hours ago 4 Replies

First reply

SawdustEcho

The confusion almost always comes from treating the sequence and driver as one conversation when they're acutally two independent things that only meet at the sequencer. Once that clicks, the rest gets easier. Here's the flow in its simplest form. Your sequence doesn't touch…

Read the full reply

I am working thru some FPGA verification coursework and I am trying to get comfortable with UVM environments for my career. The basic concepts of stimulus generation are fine, but the way sequences interact with the driver phase is really confusing me. It feels like there are a dozen ways to handle basic test scenarios, and I keep getting tangled up in the hierarchy of the sequences themselves. I understand the abstract idea of randomized testing, but getting the flow right between the sequencer and the driver without everything becoming a monolithic mess is proving difficult. My current setup seems overly complex for what should be a simple transfer test, and I think I am missing a fundamental rule about phasing.



Can someone point me toward a simple breakdown of the essential UVM flow, focusing only on the absolute must-know steps? I need something that cuts through the academic complexity and gives me a practical framework to build reliable tests.


The confusion almost always comes from treating the sequence and driver as one conversation when they're acutally two independent things that only meet at the sequencer. Once that clicks, the rest gets easier. Here's the flow in its simplest form. Your sequence doesn't touch the driver at all. It calls start_item and finish_item on a transaction, and those calls go into a queue on the sequencer. Meanwhile the driver, in its run_phase, sits in a loop calling get_next_item, drives the pins on the interface, then calls item_done. That four-call handshake is the entire mechanism. If your transfer test feels bloated, it's usually because people wrap extra layers of sequences and virtual sequences around it before they need them. For a simple test, one sequence, one sequencer, one driver is enough.

The other thing that trips people up is phasing. Sequences don't participate in the build or connect phases at all. They only exist during the run phase. Your test or env kicks them off from its run_phase, usually with something like seq.start(sequencer). That's it. If you find yourself trying to start sequences anywhere earlier, that's the sign you've tangled the hierarchy.

My practical rule: write one base sequence that sets up default transactions, then extend it for each scenario rather than building inheritance towers. Keep the driver dumb, it just turns transactions into pin wiggles and knows nothing about the scenario. All the scenario logic lives in sequences. When people end up with a monolithic mess, it's because scenario knowledge leaked into the driver or the test started doing the sequence's job.

For reading, the Siemens Verification Academy material is free with a work or school email and has a good step-by-step env build. The UVM Cookbook is denser but solid once you've got the handshake down. And honestly, writing a tiny two-transaction test from scratch without copy-pasting is the exercise that made it stick for me.

What finally made it click for me was watching the handshake in a waveform viewer. I dumped the transactions and the driver's loop to the log during a run, and followed one transaction from start_item through item_done side by side. Seeing the sequencer sit there waiting while the driver did its thing made the whole two-queues-talking thing obvious in a way no reading did.

Also, I second the two-transaction exercise, but I'd add: break it on purpose. Delete a finish_item call or skip item_done and watch the sim hang. Once you've seen what the hang looks like, debugging real tests gets way less scary.

Been doing this since the days of hand-rolled Verilog testbenches with $monitor and directed test vectors, so take it from me, most of what burns people out in UVM is stuff they won't need for years. Don't touch objections, register callbacks, or config DB plumbing on your first tests. Get the handshake working, then layer things in when a real test case actually demands it. One habit from the old days that still pays off: draw the flow on paper before you code it. Who starts what, who waits on whom. I did it on graph paper back in '98 with plain Verilog tasks and I still do it now. Sounds dumb but the times I skip it are exactly the times I end up confused about where a sequence is actually running.

The testbench I maintain at work has grown to something like 60 sequences over three years and they all hang off one base class, so the extend-don't-build advice is solid. But here's the thing nobody warns you about: your first instinct will be to make the driver 'helpful' by adding retries or error checks inside it. Resist that hard. We had a bug last quarter that took two days to find because someone hid a retry in the driver, so the scoreboard saw transactions the sequence never sent. If the transaction leaves the driver looking different from how it came in, you will regret it eventually. Keep the driver boring enough that you could read it in ten seconds.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required