UVM: unified verification methodology. UVM created in 2009. It's just a bunch of sv files with lot of tasks, functions, classes, etc already written, so that you don't have to write it. You just have to learn how to call them.
---
NOTE: In sv, classes are created at runtime, as opposed to verilog/vhdl, where all instances are elaborated before runtime.



We can use irun/xrun with -uvm option which then allows us to access cadence uvm library. Top level design units that show while compiling are uvm_pkgs (uvm_pkg.sv cdns_uvm_pkg.sv cdns_uvmapi cdns_assert2uvm_pkg) and tb.sv

uvm library files are usually installed in these paths for Cadence simulator:

ies: /apps/cds/incisiv/14.20.01/tools/methodology/UVM/CDNS-1.1d/additions/sv/ => for uvm 1.1 (all files are system verilog files)

xcelium: /home/.../cadence/xcelium/19.01.v001/tools/methodology/UVM/CDNS-1.2/sv/ => for uvm 2.0

Within this "sv" dir are multiple dir. Source code is in dir = src

src/uvm_pkg.sv => It's a package, with bunch of include files (*.svh) in it

src/uvm_macros.svh => It has bunch of include files for macro defn (*_defines.svh) in it. This is not a package, just a regular file with include files which have various macro defines (i.e `define uvm_delay(TIME) #(TIME);)

There are bunch of more dir in "src" that has all of these include files, and all other files needed by uvm (i.e src/base/uvm_root.svh has run_test task, ..).

sample tb testbench: => no test being called in this
---------------
module tb;

import uvm_pkg::*; // uvm package imported, these 2 lines needed
`include "uvm_macros.svh" //uvm macro included (to get all defn)

initial begin
  `uvm_info("TEST", "Hello!!!", UVM_LOW) //NOTE: no semicolon at end since it's replaced by the macro defn found in uvm_macros.svh
end

endmodule : tb

cmd:
---
irun -uvm tb.sv => -uvm compiles uvm pkgs (uvm_pkg.sv cdns_uvm_pkg.sv). Then module tb is compiled. Then ncsim is run, which causes "initial" block in tb to run. It prints "HELLO!!!" from that block

screen o/p:
-----------
SVSEED default: 1                                                               
ncsim> source /apps/cds/incisiv/14.20.017p1/tools/inca/files/ncsimrc                      
ncsim> source /apps/cds/incisiv/14.20.017p1/tools/methodology/UVM/CDNS-1.1d/additions/sv/files/tcl/uvm_sim.tcl    => This has bunch of tcl proc which parse various uvm cmds for option correctness, before calling the uvm package (i.e uvm_version proc finally calls uvm_pkg::uvm_version)
UVM_INFO tb.sv(12) @ 0: reporter [TEST] Hello!!!
ncsim: *W,RNQUIE: Simulation is complete.       
ncsim> exit    

----------------------------------------------
Now we modify tb above to include a test to run  
----------------------------------------------
sample tb testbench: =>  test being called in this
---------------
module tb;

// uvm package/macro, these 2 lines below needed
import uvm_pkg::*;
`include "uvm_macros.svh"

 wire a, b,..; //nets, reg etc defined which are used here

 digtop u_digtop (.A(wire1), ...); //inst digtop
 pullup(GPIO1); //inst other modules
 i2c_bfm u_i2c_bfm (.SCL(SCL), ...);

 always @(a) begin .. end //for capturing events

 //simple interface for interfacing internal nets => we always connect dut signals to interface, and then interface interacts with our uvc. This is to make it resuable. We use virtual interface (which then needs to be connected to real interface). config database is used to set these virtual i/f.
 gpio_interface gpio_if (.pclk(`TB.u_dig_top.clk_16m),.data(..),...);
 adc_interface adc_if (....);

//ex of interface: (see pg 300 of UVM lecture notes from cadence)
interface (input clk, rst);
 logic [7:0] in_data;
 logic ...;
endinterface

 //configuring interface ..
initial begin
 //components can be configured using uvm_config_db.  uvm_config_db is a class that has set and get function defined in it. We can supply the name of field, and value to be set or get using this uvm_config_db
 uvm_config_db #(virtual gpio_interface)::set(null, "*", "gpio_vif", gpio_if); //gpio_vif is set to value gpio_if
 uvm_config_db #(virtual adc_interface)::set(null, "*", "adc_vif", adc_vif);
end

initial begin
  `uvm_info("TEST", "Hello!!!", UVM_LOW)
   run_test(); // calling test to run. run_test is a task in .../CDNS-1.1d/sv/src/base/uvm_root.svh  [task uvm_root::run_test(string test_name=""); ... endtask] which calls run_phases [fork ... uvm_phase::m_run_phases(); join_none] and finally does $finish, once all done. All uvm tasks are in base dir. i.e base/uvm_phase.svh, etc. So, no explicit $finish needed in our testcase.
end

initial begin
  #10ms;
  $finish; //for sim timeout (there's already a $finish in uvm run_test call)
end

//code below differs for separate testcases. So, we can keep below code in separate files  so that each test has it's own tc.sv file. similar to how we do our regular testcases in non-uvm env. In this ex, testcase is part of same file as tb.

/////////////// testcase start = base_test.sv /////////////
NOTE: classes are same as modules. They have local signals, function, etc
`define testname base_test

class `testname extends uvm_test; //uvm_test is built in uvm class for tests.
  `uvm_component_utils(`testname)

  byte mask[]; //dynamic byte array of undefined length

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);        
    `uvm_info("TEST", "Running base Test", UVM_LOW)
  endfunction : build_phase
   
  //more func and task (everything below optional and not included for irun)
  virtual function void end_of_elaboration_phase(uvm_phase phase);
     super.end_of_elaboration_phase(phase);        
    `uvm_info("TEST", "End of elab: Sample Test", UVM_LOW)
  endfunction : end_of_elaboration_phase
   
  function init_test_seq(test_seq_base test_seq);
     test_seq.env = this.env; ...
  endfunction

  //tasks
  task run_phase(uvm_phase phase); //NOTE: we can also use task "main_phase" instead of "run_phase"
    phase.raise_objection(this, "base_test"); //raise obj, uvm keeps track of how many obj raised
    ... // all tests done here
    phase.drop_objection(this, `teststr);      // drop obj, only when all obj dropped, $finish called by uvm
  endtask:

   //more tasks
   task i2c_read(input bit [6:0] addr, input byte reg output byte data[]);
    ...
   endtask

endclass : base_test
/////////////////////// testcase end /////////////
   
endmodule : tb

cmd:
---
irun -uvm tb.sv => calls default test (base_test) to run.
irun -uvm tb.sv +UVM_TESTNAME=base_test => or we can provide test name explicitly if there are many tests. same as above if only 1 test.
If base_test is in separate file (other than tb.sv), then we need to give name of that file too so that irun can compile/elaborate it, i.e
irun -uvm tb.sv base_test.sv +UVM_TESTNAME=base_test

screen o/p:
----------
UVM_INFO tb.sv(12) @ 0: reporter [TEST] Hello!!!
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO tb.sv(29) @ 0: uvm_test_top [TEST] Running Base Test  => prints "Running Base test" from function above      

--- UVM Report catcher Summary ---
Number of demoted UVM_FATAL reports  :    0
Number of demoted UVM_ERROR reports  :    0
Number of demoted UVM_WARNING reports:    0
Number of caught UVM_FATAL reports   :    0
Number of caught UVM_ERROR reports   :    0
Number of caught UVM_WARNING reports :    0

--- UVM Report Summary ---

** Report counts by severity
UVM_INFO :    3             
UVM_WARNING :    0          
UVM_ERROR :    0            
UVM_FATAL :    0            
** Report counts by id      
[RNTST]     1               
[TEST]     2                
Simulation complete via $finish(1) at time 0 FS + 179 => NOTE: here sim finishes via $finish
/apps/cds/incisiv/14.20.017p1/tools/methodology/UVM/CDNS-1.1d/sv/src/base/uvm_root.svh:457     $finish;
ncsim> exit                           

-----------------------------------
we can create another testcase called sample_test which can extend base_test (in sample_test.sv)
//////////////////////// sample_test.sv => testcase start /////////////

`define testname sample_test
class `testname extends base_test;
  `uvm_component_utils(`testname)

  byte mask[]; //dynamic byte array of undefined length

 //these function defn below may not be needed
 extern function new(string name = `teststr, uvm_component parent = null); //defined as external function, so compiler looks for these function outside the class
 extern function void build_phase(uvm_phase phase);
 extern task run_phase(uvm_phase phase);
 extern task main_phase(uvm_phase phase);

endclass

  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);        
    `uvm_info("TEST", "Running sample Test", UVM_LOW)
  endfunction : build_phase
   
  //more functions
  function check_val (byte data1, int i);
     ....
  endfunction

  //main task
  task `testname::main_phase(uvm_phase phase);
   int a,b;
   super.main_phase(phase); //super fn always called for phases before doing anything else
   phase.raise_objection(this, "sample_test"); //raise obj
     $display(`testname:: main_phase");
     #10; addr = ... //write like normal task
     #1ms;
   phase.drop_objection(this, `teststr);      // drop obj
  endtask: main_phase
/////////////////////// testcase end /////////////

-------------------------------
see UVM lect from Cadence 5 day workshop:

tb consists of "reusable verification component (UVC)" + DUT. It also has sv i/f to link VC to DUT.
testcases can be put in separate files or within tb.

UVC consists of
 - sequencer = to create high level data (as do_read), either random or under control of test data (has TX transmitter)
 - driver (BFM=Bus functional Model) = to drive i/f signals to DUT (from high level to low level signals)
 - Monitor = to capture activity on i/f for coverage, checking and analysis (has RX receiver)

Top level of UVC is "Env". Env contains one or more agents. Each agent contains inst of Sequencer, driver, monitor and config. config indicates if it'a active agent (i.e TX => all seq, driver, monitor implemented) or passive agent (i.e RX => only monitor implemented)
All above components are created using class.
class sequencer extends base ... endclass
class driver extends base ... endclass
class monitor extends base ... task monitor(bit[3:0] ..) forever begin ... end endtask endclass
interface pds_if(input ...) .. endinterface

//PDS UVC => top level uvc which inst all of the above components
class pds_vc extends base; ... endclass

On top of this, scorboard UVC is also added to tb, so that i/p and o/p to/from DUT can be compared and errors flagged.  
UVM phases: (pg 177 of UVM doc from cadence) These are predefined phases. User defined phases can also be added. Phases start running in the order below when global task run_test() is called.
NOTE: to use phases below, we simply declare *_phase function in your class, and add reqd functionality. We should not call *_phase directly. It's automatically executed by simulator during that phase.
 - build_phase => building testbench. All hier should be built using build_phase rather than constructors. build_phase executes top down, all other phases execute bottom up.
 - connect_phase => making connections. Any connections b/w components should be made using connect_phase rather than constructors
 - end_of_elaboration_phase => for any pre run activity
 - run_phase => simulation of design. run_phase is implemented as a task, as it can consume time. Rest all phases are implemented as functions, as they run in 0 time. run_phase has many subphases and were added in UVM1.0. These sub-phases run in parallel with run_phase.
 - check_phase, report_phase => checking results and generating reports
 
----------------------------
Built in classes (chapter 4 in uvm book)
---------
UVM lib has many inbuilt classes from which we extend to create our classes. Ex:
1. uvm_object: this is the base class for all (data and component) UVM classes. It has built in methods for copy(), compare(), clone(), print(). Most of the time, we derive our data item from uvm_sequence_tem which itself extends from uvm_transaction which extends from uvm_object.
 ex:
class apb_transfer extends uvm_object; //or "extends uvm_sequence_item"  can also be used
      ...
  `uvm_objects_utils_begin(apb_transfer) //uvm automation macros => used to declare common operaations
     `uvm_field_int(addr, UVM_DEFAULT)
  `uvm_objects_utils_end
  function new (string name = "apb xfer"); //uvm construction new() - not mandatory
   super.new(name);
  endfunction
endclass

2. uvm_component: All infrastructure components as testbench comp, UVCs, tests are derived directly or indirectly from uvm_component. Typically we derive our user classes from methodology classes (uvm_agent, uvm_driver, uvm_sequencer, uvm_monitor, uvm_scoreboard, uvm_test and so on), which are themselves extension of uvm_component. Phases and configuration methods are functionality provided by uvm_component base class.
ex:
class testbench_comp extends uvm_component; //testcase
  function new(string name, uvm_component parent)
   super.new(name,parent);
  endfunction
  function void build_phase(uvm_phase phase);
   super.build_phase(phase);
   my_uvc = if_comp::type_id::create("my_uvc", this);
  endfunction
  //similarly other function for end_of_elaboration_phase, etc can be added here
  task run_phase (uvm_phase phase); //this is where the testcase is coded and run
   ...
  endtask
endclass

class if_comp extends uvm_component; //this is some other class that has many uvm_phases
 function new ... endfunction
 function void build_phase ... endfunction
 task run_phase ... endtask
endclass

module test; //testbench to run above testcase
 testbench_comp tb;
 initial begin
  tb = testbench_comp::type_id::create("testbench", null);
  run_test(); //this call starts the uvm phases for all the classes above
 end
endmodule

-----------------
contraints:
-------
In any class, we can have constraints defined for any field.
ex: constraint c_addr {addr[1:0] == 2'b01; }

we can overwrite any constraint in subclass, by redeclaring. Parent class contraint can be removed in subclass by leaving it empty.
ex: constarint c_addr {} => constraint c_addr is removed for this subclass, even though it's present in parent class.

-----------
coverage in uvm: see system verilog notes also:
----------

Metric driven verification
1. functional coverage
2. assertions (static formal verification and sims)
3. directed tests
4. code coverage

Most coverage constructs are placed in monitors.

1. functional coverage of 2 types:
A. assertions for control oriented coverage => cannot be in class
ex:
property req_gnt (cyc);
 @(posedge clk) req |=> ##[cyc] gnt;
endproperty

cover property (req_gnt(3));
cover property (req_gnt(4));

B. covergroups for data-oriented coverage => can be declared as class member
ex:
covergroup cg @(posedge clk);
 len: coverpoint pkt.length { ...}
 addr: cross pkt.addr, len;
endgroup

cg cg1 = new();

--------------