Systemverilog or UVM?

In reply to Farhad:
I struggled with that question when testing small models for my assertions.
I came up with a solution that integrates the concepts of UVM, but without the interface, vif, and UVM classes, the environment, and tests. I use UVM for displays.
**** NOTE: ****
**** I show 2 variations for testing this counter, one simple one, the other, a bit more details and more a la UVM with tasks for the drivers once I detect the “kind” of transaction needed. Anyway, that should give a flavor of my approach.
Using classes will allow you to more easily transition to a full UVM.
Below are the models to verify a counter with strange requirements.
The counter model is at the end of the code. On the testbench I use the following:

  1. counter_pkg: for the enumeration type
  2. class data_item: Items to be randomized
  3. class transaction extends data_item; Other items to be randomized.
    Constraints are defined here.
    The extension is used as a demo, but is nice to have if needed
  4. class simple_random_sequence; task to randomize the items, It executes nothing, just randomizes per the constraints.
  5. module counter_max_tb_using_class; the testbench

The testbench includes:

  1. variables, DUT instantiation
  2. simple_random_sequence class instantiation
  3. test, includes reset, loop to randomize the variable, NBA assign the randomized data

// test using simple_random_sequence
always @ (posedge clk) begin : random1
rst_n <= 1'b1;
repeat(3) @ (negedge clk) rst_n <= 1'b0;
repeat(3) @ (negedge clk)rst_n <= 1'b1;
// dada and load
for (int i=0; i<40; i++) begin : for1
simple_rand_sequence_ld_mostly_hi.run();
data_in <= simple_rand_sequence_ld_mostly_hi.tx.data;
ld <= simple_rand_sequence_ld_mostly_hi.tx.ld;
@ (posedge clk);
end : for1
$display("End of test");
$finish;
end : random1

// **************** Counter pACKAGE
package counter_pkg;
  timeunit 1ns; timeprecision 100ps;
  `define TOP counter_tb
  typedef enum {CT_LOAD, CT_RESET, CT_WAIT, CT_DONE} ct_scen_e;
endpackage : counter_pkg

//************** Testbench *****************************
import uvm_pkg::*;
`include "uvm_macros.svh"
 
class data_item;
    rand logic[3:0] data;
endclass : data_item

class transaction extends data_item;
    rand logic ld;
    // 1'b0 80% of time, 1'b1 20% of the time
    constraint values { ld dist {1'b0 := 80, 1'b1 := 20}; } 
endclass : transaction

class simple_random_sequence;
    transaction tx=new();
    // randomize 
    virtual task run();
    	if (!randomize(tx))  `uvm_error("MYERR", "This is a randomize error"); 
    	// if(!randomize(tx))  $error("randomization failure"); 
    endtask : run
endclass : simple_random_sequence

module counter_max_tb_using_class;
    parameter MAX_COUNT=9, MIN_COUNT=2; 
    logic[3:0] data_in=0, data=0;
    logic ld=0;
    logic[3:0] counter, counter2;
    logic clk=1'b1, rst_n=1'b1;
    simple_random_sequence simple_rand_sequence_ld_mostly_hi=new();
    counter_max counter_max1(.*); 
    // counter_max counter_max2(.counter(counter2), .*); 
    initial forever #10 clk=!clk; 
    
    // test using simple_random_sequence
    always @ (posedge clk) begin : random1
        rst_n <= 1'b1; 
        repeat(3) @ (negedge clk) rst_n <= 1'b0; 
        repeat(3) @ (negedge clk)rst_n <= 1'b1;
        // dada and load 
        for (int i=0; i<40; i++) begin : for1
            simple_rand_sequence_ld_mostly_hi.run(); 
            data_in <= simple_rand_sequence_ld_mostly_hi.tx.data;
            ld <= simple_rand_sequence_ld_mostly_hi.tx.ld; 
            @ (posedge clk); 
        end : for1
        $display("End of test"); 
        $finish;
    end : random1		
endmodule : counter_max_tb_using_class

You can go a bit more fancy on the drivers by doing something like more a la UVM


import counter_pkg::*;
class data_item;
    rand logic[3:0] data;
endclass : data_item

class transaction extends data_item;
    rand logic ld=0;
    int reset_cycles, idle_cycles;
    rand ct_scen_e kind;
    // constraint values { ld dist {1'b0 := 80, 1'b1 := 20}; } // 0 80% of time
    constraint data_range { data > 2 && data <=9; } 
    constraint cst_xact_kind {
        kind dist {
            CT_LOAD := 5, 
            CT_RESET := 2, 
            CT_WAIT := 97,
            CT_DONE := 1
        };
    } // cst_xact_kind
endclass : transaction

class simple_random_sequence;
    transaction tx=new();
    // randomize 
    virtual task run();
        ap_rand_seq: assert(randomize(tx)); 
    endtask : run
endclass : simple_random_sequence

 

module counter_max_tb_simple;
    parameter MAX_COUNT_TB=6, MIN_COUNT_TB=3; 
    // defparam counter_max1.MIN_COUNT=3; 
    // illegal because have parameter port list
    logic[3:0] data_in=0, data=0;
    logic ld=0;
    logic[3:0] counter, counter2;
    logic clk=1'b1, rst_n=1'b1;
    simple_random_sequence seq=new();
    counter_max #(.MAX_COUNT(MAX_COUNT_TB), 
        .MIN_COUNT(MIN_COUNT_TB)) counter_max1(.*); 
    // counter_max counter_max2(.counter(counter2), .*); 
    initial forever #10 clk=!clk; 
    
    // test using simple_random_sequence
    always @ (negedge clk) begin : random1
        rst_n <= 1'b1; ld <=1'b0; 
        repeat(3) @ (negedge clk) rst_n <= 1'b0; 
        repeat(3) @ (negedge clk)rst_n <= 1'b1;
        // dada and load 
        for (int i=0; i<100; i++) begin : for1
        	@ (negedge clk);
            seq.run(); 
            case (seq.tx.kind)
                CT_LOAD : begin 
                    // $display("PUSH"); 
                    //`ovm_info({get_type_name(),":drive_dut"},{"rsp_txn - pre",rsp_txn.convert2string()},OVM_LOW)
                    load_task(seq.tx.data); 
                end
                CT_RESET : begin 
                    // $display("POP"); 
                    //`ovm_info({get_type_name(),":drive_dut"},{"rsp_txn - pre",rsp_txn.convert2string()},OVM_LOW)
                    reset_task(); 
                end 
                CT_WAIT : begin 
                    //$display("PUSH_POP"); 
                    //`ovm_info({get_type_name(),":drive_dut"},{"rsp_txn -pre",rsp_txn.convert2string()},OVM_LOW)
                    idle_task(seq.tx.data); 
                end
                CT_DONE : begin 
                    // $display("IDLE");
                    //`ovm_info({get_type_name(),":drive_dut"},{"rsp_txn -pre",rsp_txn.convert2string()},OVM_LOW)
                    done_task(); 
                end // seq.tx.idle_cycles);
            endcase
            // @ (negedge clk); 
        end : for1
        $display("End of test"); 
        $display("counter_max1.cover_count=%d, counter_max1.fail_count=%d", 
            counter_max1.cover_count, counter_max1.fail_count);
        $finish;
    end : random1
    
    // load counter
    task load_task(int data);
        data_in <= data;
        ld <= 1'b1; 
        @ (negedge clk)ld <= 1'b0; 
        
    endtask : load_task
    
    // reset
    task reset_task();
        ld <= 1'b0; 
        repeat(1) rst_n <= 1'b0; 
        @ (negedge clk)rst_n <= 1'b1;
    endtask : reset_task
    
    // IDLE
    task idle_task(int data);// for now, make idle for 4 cycles 
        automatic logic[3:0] v=data; 
        for (int i=0; i<4; i++) begin
            ld <= 1'b0; 
            assert(randomize(v)); 
            data_in <= v;
            @ (negedge clk); 
        end
    endtask : idle_task
    
    // DONE, for now, a do nothing 
    task done_task();
        @ (negedge clk); 
        
    endtask : done_task
endmodule : counter_max_tb_simple

Ben Cohen
http://www.systemverilog.us/ ben@systemverilog.us
** SVA Handbook 4th Edition, 2016 ISBN 978-1518681448

  1. SVA Package: Dynamic and range delays and repeats SVA: Package for dynamic and range delays and repeats | Verification Academy
  2. Free books: Component Design by Example FREE BOOK: Component Design by Example … A Step-by-Step Process Using VHDL with UART as Vehicle | Verification Academy
    Real Chip Design and Verification Using Verilog and VHDL($3) Amazon.com
  3. Papers:

Udemy courses by Srinivasan Venkataramanan (http://cvcblr.com/home.html)
https://www.udemy.com/course/sva-basic/
https://www.udemy.com/course/sv-pre-uvm/