UVM Sequences and Sequencers

In my previous post, I put together the least lines of code to write a “Hello, World!” program in SystemVerilog using UVM. That’s cute, but not really helpful. I want to work towards a skeleton code that turns into something useful, while still staying close to the basics.

This post is a pretty basic introduction to Sequences and Sequencers. I am going to print “Hello, World” from the sequencer(s) and the sequence(s). I am continuing to stay in the “programming world” so as to speak; there is no interaction with a DUT/RTL of any kind, no signals or clocks just yet.

Sequences

Let’s talk about Sequences first. “Sequences” are exactly what they say they are. A sequence is a series of steps that is to be executed. UVM sequences have a “body” method that gets executed when the sequence is called.

Sequences are started using sequence.start() task. The start() task (as defined in uvm_sequence_base.sv) allows me to provide a sequencer for this sequence to run on. For now think of the sequencers as an arbiter for the sequences. I can run multiple sequences on a sequencer; the start() task also accepts a priority number for the sequences. If you don’t provide a priority, the default priority is assumed.

A Basic Sequence

class hw_seq extends uvm_sequence;
  task body();
    $display("Hello, World! from sequence");
  endtask
endclass

Rest of the “Hello, World!” code

class my_uvm_hw_test extends uvm_test;
  `uvm_component_utils(my_uvm_hw_test)

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
  task run_phase(uvm_phase phase);
    hw_seq hw_seq_h;  
    hw_seq_h = new();

    $display("Starting HW sequence");
    hw_seq_h.start(null); //Null if no sequencer.
    
  endtask
endclass

module my_uvm_hw;
  initial
    run_test("my_uvm_hw_test");
endmodule

Expected Output

>>Starting HW sequence
>>Hello, World! from sequence

Please note that I have no time consuming events in the above example. If the sequence did involve time consuming events, UVM excepts to raise and drop exceptions.

Sequencers

The simplest way to think of a sequencer is as an arbiter for the sequences. I can start a bunch of sequences on a sequencer, and the sequencer will arbitrate between them. I can have the sequencer do a lot of fancy stuff and I intend to demonstrate this with an example; and will do this in a later post once I have discussed “sequence_items.”

A Basic Sequencer

class hw_seqr extends uvm_sequencer;
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction
  
//The run_phase isn't required.
//It's just to display the Hello World.
  task run_phase(uvm_phase phase);
    $display("Hello, World! from the sequencer");
  endtask
  
endclass

Running sequences on a Sequencer

...
...
hw_seq_h.start(hw_seqr_h); //Passing sequencer instead of null.
...
...

Do note that if I am running just one sequence, I don’t really have to use a sequencer. Using a sequencer provides other benefits; being a “uvm_component” it makes it easier to communicate with other components (the most common usage is for the sequencer to communicate with the driver). A sequencer is usually associated with a parameterized sequence_item.

That’s it for sequences and sequencers. I now have a UVM program of sorts that uses a test, a uvm_sequence and a uvm_sequencer. It’s still a cute piece of code that doesn’t do much with respect to driving actual signals to a DUT. We need a couple more pieces for that to happen and I plan to discuss those in the next posts.

References and useful links