Skip to content
VLSI Mentor

UART · Module 16

Sequence Item and Sequence Library

Modelling a UART frame as a transaction, the constraints that keep a random frame legal and the soft ones that let a sequence ask for an illegal frame on purpose, and the distribution that puts stimulus where the failures are.

A sequence item is a description of what should happen, not of what the wires do. Getting that boundary right is most of the work: a transaction that carries tx_o has become a driver, and a transaction that carries a bit period in clock cycles has welded itself to one clock frequency.

This chapter models a UART frame as a transaction, constrains it so a random one is legal, and then — deliberately — provides the mechanism to make it illegal.

1. The Transaction Is the Task's Arguments

Module 14's BFM had this signature:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
task automatic send_frame(input logic [8:0] data,
                          input int   nbits,
                          input int   parity_mode,
                          input int   stop_halves,
                          input real  tbit_ns,
                          input int   err);

Six arguments. A uvm_sequence_item is those six arguments in a class, plus the ability to randomise and print them:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ---------------------------------------------------------------------------
//  uart_frame_item — one UART frame, described the way a TEST thinks about it
//
//  Every field is a parameter of the FRAME, not a signal on the wire. There
//  is no tx_o here, no baud tick and no clock: a transaction says what should
//  appear on the line, and the driver decides how to produce it.
//
//  That boundary is what lets the same item be driven at 9,600 baud on one
//  test and 3 Mbaud on another without touching the class.
// ---------------------------------------------------------------------------
class uart_frame_item extends uvm_sequence_item;

    rand bit [8:0]    data;
    rand int unsigned nbits;         // 5..9 data bits
    rand int unsigned parity_mode;   // 0 none, 1 even, 2 odd, 3 mark
    rand int unsigned stop_halves;   // 2 = 1 bit, 3 = 1.5, 4 = 2
    rand real         tbit_ns;       // THE argument that makes the BFM useful
    rand int unsigned err;           // ERR_NONE .. ERR_BREAK

    // Observed-only: filled in by the monitor, never randomised.
    bit parity_err;
    bit frame_err;

    `uvm_object_utils_begin(uart_frame_item)
        `uvm_field_int (data,        UVM_ALL_ON)
        `uvm_field_int (nbits,       UVM_ALL_ON)
        `uvm_field_int (parity_mode, UVM_ALL_ON)
        `uvm_field_int (stop_halves, UVM_ALL_ON)
        `uvm_field_real(tbit_ns,     UVM_ALL_ON)
        `uvm_field_int (err,         UVM_ALL_ON)
        `uvm_field_int (parity_err,  UVM_ALL_ON | UVM_NOCOMPARE)
        `uvm_field_int (frame_err,   UVM_ALL_ON | UVM_NOCOMPARE)
    `uvm_object_utils_end

    function new(string name = "uart_frame_item");
        super.new(name);
    endfunction
endclass

An unconstrained randomize() on that class produces nbits = 2_147_483_621 and a tbit_ns of whatever the random number generator felt like. Every random frame must be legal unless a test asks otherwise.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    // ---- the frame must be a frame -----------------------------------
    constraint c_format {
        nbits       inside {[5:9]};
        parity_mode inside {[0:3]};
        stop_halves inside {[2:4]};
    }

    // ---- the payload must fit the width ------------------------------
    // Written as a relation, not as `data < 512`. A 5-bit frame carrying
    // 0x1FF is not a legal frame with an unusual value -- it is a request
    // the driver cannot honour, and it will silently truncate.
    constraint c_payload { data < (1 << nbits); }

    // ---- and it must be sent at a rate the receiver can follow --------
    constraint c_baud_legal {
        soft tbit_ns inside {[8160.0 : 9200.0]};   // about +/-6% of nominal
    }

3. Distribution: Put the Stimulus Where the Failures Are

Uniform randomisation spends its time in the middle of every range, and the middle is where nothing happens.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    // Clean traffic must dominate, or the error cases stop being corners
    // and the environment is no longer testing normal operation at all.
    constraint c_err_mix {
        err dist { 0 := 70, [1:5] := 30 };
    }

    // Baud error clusters at the EDGE of the tolerance window, because that
    // is where behaviour changes. Chapter 15.5 measured the edge at +/-6%
    // against a prediction of 5.88%; uniform randomisation across +/-10%
    // would spend most of its samples nowhere near it.
    constraint c_baud_shape {
        soft tbit_ns dist {
            8680.5556                  := 40,     // nominal
            [8160.0 : 8420.0]          := 15,     // about -5%
            [8940.0 : 9200.0]          := 15,     // about +5%
            [7810.0 : 8160.0]          := 5,      // beyond the edge
            [9200.0 : 9550.0]          := 5
        };
    }

    // Data values that are corners for a reason, not because they look odd.
    // 0x00 is the longest run of zeros and produced the false break of
    // Chapter 11.4; 0xFF is the longest run of ones.
    constraint c_data_corners {
        soft data dist { 9'h000 := 10, 9'h0FF := 10, [1:254] := 80 };
    }

Writing that distribution by hand in a directed loop is possible and nobody does it. This is the one place where UVM's machinery genuinely changes what gets tested rather than how it is organised.

4. The Sequence Library

A sequence is a for loop that can be started on any sequencer, nested inside another, and randomised.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Every byte value at the nominal rate -- the coverage floor.
class uart_all_bytes_seq extends uvm_sequence #(uart_frame_item);
    `uvm_object_utils(uart_all_bytes_seq)
    function new(string name = "uart_all_bytes_seq"); super.new(name); endfunction

    task body();
        for (int d = 0; d < 256; d++)
            `uvm_do_with(req, { data == d; nbits == 8; parity_mode == 0;
                                stop_halves == 2; err == 0;
                                tbit_ns == 8680.5556; })
    endtask
endclass

// The full format cross of Chapter 15.3 -- 60 bins, four frames each.
class uart_format_cross_seq extends uvm_sequence #(uart_frame_item);
    `uvm_object_utils(uart_format_cross_seq)
    function new(string name = "uart_format_cross_seq"); super.new(name); endfunction

    task body();
        for (int nb = 5; nb <= 9; nb++)
            for (int pm = 0; pm <= 3; pm++)
                for (int sh = 2; sh <= 4; sh++)
                    repeat (4)
                        `uvm_do_with(req, { nbits == nb; parity_mode == pm;
                                            stop_halves == sh; err == 0; })
    endtask
endclass

// Each injected fault in turn -- Chapter 15.4's detection matrix.
class uart_fault_sweep_seq extends uvm_sequence #(uart_frame_item);
    `uvm_object_utils(uart_fault_sweep_seq)
    function new(string name = "uart_fault_sweep_seq"); super.new(name); endfunction

    task body();
        for (int e = 0; e <= 5; e++)
            repeat (8)
                `uvm_do_with(req, { err == e; nbits == 8; parity_mode == 1; })
    endtask
endclass

// And a virtual sequence: two of the above on the same sequencer, interleaved.
class uart_mixed_vseq extends uvm_sequence #(uart_frame_item);
    `uvm_object_utils(uart_mixed_vseq)
    function new(string name = "uart_mixed_vseq"); super.new(name); endfunction

    task body();
        uart_format_cross_seq cross_seq;
        uart_fault_sweep_seq  fault_seq;
        cross_seq = uart_format_cross_seq::type_id::create("cross_seq");
        fault_seq = uart_fault_sweep_seq ::type_id::create("fault_seq");
        fork
            cross_seq.start(m_sequencer);
            fault_seq.start(m_sequencer);
        join
    endtask
endclass

5. Verification

Randomise legally by default. An unconstrained randomize() on this class produces a nbits in the billions. Every field that has a legal range must have a hard constraint stating it.

Relate the payload to the width. data < (1 << nbits), not data < 512. A 5-bit frame carrying 0x1FF is not an unusual value; it is a request the driver will silently truncate.

Use soft for defaults a test may need to break, and hard for statements about what the transaction is. The test: would violating it produce a frame, or produce nothing?

Exclude observed-only fields from compare(). parity_err and frame_err are filled in by the monitor; comparing them against an unfilled expected item fails on every correctly-detected error.

Shape the distribution towards the edges. Uniform randomisation across a tolerance window spends most of its samples where nothing happens; Chapter 15.5 measured the interesting region at ±6%.

And check randomize()'s return value. A failed randomisation with unchecked status leaves the item at its previous values and drives the same frame repeatedly — a suite that appears to run and tests one thing.

6. Debugging

7. Understanding Check

8. Summary

A sequence item is the arguments of Module 14's send_frame task, in a class. Every field describes the frame; none describes a wire.

Hard constraints state what the transaction is; soft constraints are defaults a test may override. The test is whether violating it produces a frame or produces nothing.

The payload is constrained relative to the width, so no randomised item can request bits the format cannot carry.

Observed-only fields are excluded from compare(), or every correctly-detected error reads as a mismatch.

The distribution is weighted towards the edges — 70/30 clean to faulty, and baud clustered at the ±6% boundary rather than spread uniformly across ±10%.

And a virtual sequence running two sequences on one sequencer is why a sequencer exists — the directed environment of Module 14 could not have expressed it at all.

9. What Comes Next

Chapter 16.3 takes a transaction and puts it on a wire — and builds the sequencer that decides whose transaction goes next, as running code in three languages.

Browse the full path on the UART tutorials index. For the task whose arguments became this class, read back to Chapter 14.2.

Continue learning

Where this fits

Part of the UART curriculum.