In T* DRC deck, it is based on the voltage recognition CAD layer and net connection to calculate the voltage difference between two neighboring nets by the following formula:

\[ \Delta V = \max(V_H(\text{net1})-V_L(\text{net2}), V_H(\text{net2})-V_L(\text{net1})) \]

where \[ V_H(\text{netx}) = \max(V(\text{netx})) \] and \[ V_L(\text{netx}) = \min(V(\text{netx})) \]

  • The \(\Delta V\) will be 0 if two nets are connected as same potential
  • If \(V_L \gt V_H\) on a net, DRC will report warning on this net

Voltage recognition CAD Layer

Two method

  1. voltage text layer

    You place specific voltage text on specific drawing layer

  2. voltage marker layer

    Each voltage marker layer represent different voltage for specific drawing layer

voltage text layer has higher priority than voltage marker layer and is recommended

voltage text layer

For example M3

Process Layer CAD Layer# Voltage High Voltage High Top
(highest priority)
Voltage Low Voltage Low Top
(highest priority)
M3 63 110 112 111 113

where 63 is layer number, 110 ~ 113 is datatype

voltage marker layer

Different data type represent different voltage, like

DataType 100 101 102 ... 109
Voltage 0.0 0.1 0.2 0.3 0.9

Example

image-20220503171006936

reference

Automate those voltage-dependent DRC checks! - siemens

Generation

image-20220404125201390

1
reg_verifier -domain uvmreg -top yapp_router_regs.xml -dut_name yapp_router_regs -out_file yapp_router_regs -cov -quicktest -pkg yapp_router_reg_pkg

image-20220404125541442

image-20220404125421201

generated file description
yapp_router_regs_config.dat Configuration information
yapp_router_regs_hdlpaths.dat Path information for backddoor access
yapp_router_regs_rdb.sv Register Model
cdns_uvmreg_utils_pkg.sv Cadence utility package
quicktest.sv UVM test to verify model

Sanity Check

1
make run_test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class yapp_router_regs_t extends cdns_uvm_reg_block;
uvm_reg_map default_map;
uvm_reg_map router;
rand yapp_regs_c router_yapp_regs;
rand yapp_mem_c router_yapp_mem;
rand yapp_pkt_mem_c router_yapp_pkt_mem;
...
virtual function void build();
router = create_map("router", `UVM_REG_ADDR_WIDTH'h0, 1, UVM_LITTLE_ENDIAN, 1);
default_map = router;
...
//Mapping router map
router.add_submap(router_yapp_regs.default_map, `UVM_REG_ADDR_WIDTH'h1000)
router.add_mem(router_yapp_mem, `UVM_REG_ADDR_WIDTH'h1100);
router.add_mem(router_yapp_pkt_mem, `UVM_REG_ADDR_WIDTH'h1010);
...
endfunction

endclass


class qt_test extends uvm_test;
yapp_router_regs_t model;
...
task run_phase(uvm_phase phase);
...
model.print();
model.default_map.print();
model.router_yapp_regs.default_map.print();
endtask
...
endclass

yapp_mem_c

1
2
3
4
5
6
7
class yapp_mem_c extends uvm_mem;                                                                                                                                                                                                                                
`uvm_object_utils(yapp_mem_c)

function new(input string name="router_yapp_mem");
super.new(name, 'h100, 8, "RW", UVM_NO_COVERAGE);
endfunction
endclass

yapp_pkt_mem_c

1
2
3
4
5
6
7
class yapp_pkt_mem_c extends uvm_mem;                                                                                                                                                                                                                                      
`uvm_object_utils(yapp_pkt_mem_c)
function new(input string name="router_yapp_pkt_mem");
super.new(name, 'h40, 8, "RO", UVM_NO_COVERAGE);
endfunction

endclass

yapp_regs_c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class yapp_regs_c extends cdns_uvm_reg_block;
`uvm_object_utils(yapp_regs_c)
rand addr0_cnt_reg_c addr0_cnt_reg;
rand addr1_cnt_reg_c addr1_cnt_reg;
rand addr2_cnt_reg_c addr2_cnt_reg;
rand illegal_addr_cnt_reg_c addr3_cnt_reg;
rand ctrl_reg_c ctrl_reg;
rand en_reg_c en_reg;
rand mem_size_reg_c mem_size_reg;
rand oversized_pkt_cnt_reg_c oversized_pkt_cnt_reg;
rand parity_err_cnt_reg_c parity_err_cnt_reg;
virtual function void build();
uvm_reg reg_set[$];
default_map = create_map(get_name(), `UVM_REG_ADDR_WIDTH'h1000, 1, UVM_LITTLE_ENDIAN, 1);
begin
uvm_reg_config_ta ta = get_reg_config("yapp_router_regs.router_yapp_regs");
build_uvm_regs(default_map, this, null, ta, reg_se);
end
if(! $cast(addr0_cnt_reg, reg_set[0]))
`uvm_error("UVM_REG", "addr0_cnt_reg register casting error")
if(! $cast(addr1_cnt_reg, reg_set[1]))
`uvm_error("UVM_REG", "addr1_cnt_reg register casting error")
if(! $cast(addr2_cnt_reg, reg_set[2]))
`uvm_error("UVM_REG", "addr2_cnt_reg register casting error")
if(! $cast(addr3_cnt_reg, reg_set[3]))
`uvm_error("UVM_REG", "addr3_cnt_reg register casting error")
if(! $cast(ctrl_reg, reg_set[4]))
`uvm_error("UVM_REG", "ctrl_reg register casting error")
if(! $cast(en_reg, reg_set[5]))
`uvm_error("UVM_REG", "en_reg register casting error")
if(! $cast(mem_size_reg, reg_set[6]))
`uvm_error("UVM_REG", "mem_size_reg register casting error")
if(! $cast(oversized_pkt_cnt_reg, reg_set[7]))
`uvm_error("UVM_REG", "oversized_pkt_cnt_reg register casting error")
if(! $cast(parity_err_cnt_reg, reg_set[8]))
`uvm_error("UVM_REG", "parity_err_cnt_reg register casting error")
endfunction

function new(input string name="router_yapp_regs");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class uvm_reg_map extends uvm_object;
// Function: add_submap
//
// Add an address map
//
// Add the specified address map instance to this address map.
// The address map is located at the specified base address.
// The number of consecutive physical addresses occupied by the submap
// depends on the number of bytes in the physical interface
// that corresponds to the submap,
// the number of addresses used in the submap and
// the number of bytes in the
// physical interface corresponding to this address map.
//
// An address map may be added to multiple address maps
// if it is accessible from multiple physical interfaces.
// An address map may only be added to an address map
// in the grand-parent block of the address submap.
//
extern virtual function void add_submap (uvm_reg_map child_map,
uvm_reg_addr_t offset);


// Function: add_mem
//
// Add a memory
//
// Add the specified memory instance to this address map.
// The memory is located at the specified base address and has the
// specified access rights ("RW", "RO" or "WO").
// The number of consecutive physical addresses occupied by the memory
// depends on the width and size of the memory and the number of bytes in the
// physical interface corresponding to this address map.
//
// If ~unmapped~ is TRUE, the memory does not occupy any
// physical addresses and the base address is ignored.
// Unmapped memories require a user-defined ~frontdoor~ to be specified.
//
// A memory may be added to multiple address maps
// if it is accessible from multiple physical interfaces.
// A memory may only be added to an address map whose parent block
// is the same as the memory's parent block.
//
extern virtual function void add_mem (uvm_mem mem,
uvm_reg_addr_t offset,
string rights = "RW",
bit unmapped=0,
uvm_reg_frontdoor frontdoor=null);


endclass: uvm_reg_map

register model print

// test run_phase

model.default_map.print();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
----------------------------------------------------------------------------------------------------------------------------            
Name Type Size Value
----------------------------------------------------------------------------------------------------------------------------
model yapp_router_regs_t - @3724
router_yapp_regs yapp_regs_c - @3792
addr0_cnt_reg addr0_cnt_reg_c - @3827
addr0_cnt_reg_fld uvm_reg_field ... RO addr0_cnt_reg[7:0]=8'h00
addr1_cnt_reg addr1_cnt_reg_c - @3839
addr1_cnt_reg_fld uvm_reg_field ... RO addr1_cnt_reg[7:0]=8'h00
addr2_cnt_reg addr2_cnt_reg_c - @3857
addr2_cnt_reg_fld uvm_reg_field ... RO addr2_cnt_reg[7:0]=8'h00
addr3_cnt_reg illegal_addr_cnt_reg_c - @3874
addr3_cnt_reg_fld uvm_reg_field ... RO addr3_cnt_reg[7:0]=8'h00
ctrl_reg ctrl_reg_c - @3891
plen uvm_reg_field ... RW ctrl_reg[5:0]=6'h3f
rsvd_0 uvm_reg_field ... RW ctrl_reg[7:6]=2'h0
en_reg en_reg_c - @3908
router_en uvm_reg_field ... RW en_reg[0:0]=1'h1
parity_err_cnt_en uvm_reg_field ... RW en_reg[1:1]=1'h0
oversized_pkt_cnt_en uvm_reg_field ... RW en_reg[2:2]=1'h0
rsvd_0 uvm_reg_field ... RW en_reg[3:3]=1'h0
addr0_cnt_en uvm_reg_field ... RW en_reg[4:4]=1'h0
addr1_cnt_en uvm_reg_field ... RW en_reg[5:5]=1'h0
addr2_cnt_en uvm_reg_field ... RW en_reg[6:6]=1'h0
addr3_cnt_en uvm_reg_field ... RW en_reg[7:7]=1'h0
mem_size_reg mem_size_reg_c - @3928
mem_size_reg_fld uvm_reg_field ... RO mem_size_reg[7:0]=8'h00
oversized_pkt_cnt_reg oversized_pkt_cnt_reg_c - @3966
oversized_pkt_cnt_reg_fld uvm_reg_field ... RO oversized_pkt_cnt_reg[7:0]=8'h00
parity_err_cnt_reg parity_err_cnt_reg_c - @3983
parity_err_cnt_reg_fld uvm_reg_field ... RO parity_err_cnt_reg[7:0]=8'h00
router_yapp_mem yapp_mem_c - @3757
n_bits integral 32 'd8
size integral 32 'd256
router_yapp_pkt_mem yapp_pkt_mem_c - @3779
n_bits integral 32 'd8
size integral 32 'd64
router uvm_reg_map 0 id=@3737 seqr=HI0.sqr offset=0x0 size=0x1200 baseaddr=0x0
router_yapp_regs uvm_reg_map 0 id=@3816 seqr=HI0.sqr offset=0x1000 size=0x5 baseaddr=0x1000
----------------------------------------------------------------------------------------------------------------------------

address map print

// test run_phase

model.router_yapp_regs.default_map.print();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
---------------------------------------------------------------------------
Name Type Size Value
---------------------------------------------------------------------------
router uvm_reg_map - @3737
endian ... UVM_LITTLE_ENDIAN
effective sequencer uvm_sequencer ... HI0.sqr
router_yapp_mem yapp_mem_c ... @3757 +'h1100
router_yapp_pkt_mem yapp_pkt_mem_c ... @3779 +'h1010
router_yapp_regs uvm_reg_map - @3816
endian ... UVM_LITTLE_ENDIAN
effective sequencer uvm_sequencer ... HI0.sqr
addr0_cnt_reg addr0_cnt_reg_c ... @3827 +'h1009
addr1_cnt_reg addr1_cnt_reg_c ... @3839 +'h100a
addr2_cnt_reg addr2_cnt_reg_c ... @3857 +'h100b
addr3_cnt_reg illegal_addr_cnt_reg_c ... @3874 +'h1006
ctrl_reg ctrl_reg_c ... @3891 +'h1000
en_reg en_reg_c ... @3908 +'h1001
mem_size_reg mem_size_reg_c ... @3928 +'h100d
oversized_pkt_cnt_reg oversized_pkt_cnt_reg_c ... @3966 +'h1005
parity_err_cnt_reg parity_err_cnt_reg_c ... @3983 +'h1004
---------------------------------------------------------------------------

submap print

// test run_phase

model.router_yapp_regs.default_map.print();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-------------------------------------------------------------------------
Name Type Size Value
-------------------------------------------------------------------------
router_yapp_regs uvm_reg_map - @3814
endian ... UVM_LITTLE_ENDIAN
effective sequencer uvm_sequencer ... HI0.sqr
addr0_cnt_reg addr0_cnt_reg_c ... @3825 +'h1009
addr1_cnt_reg addr1_cnt_reg_c ... @3837 +'h100a
addr2_cnt_reg addr2_cnt_reg_c ... @3855 +'h100b
addr3_cnt_reg illegal_addr_cnt_reg_c ... @3872 +'h1006
ctrl_reg ctrl_reg_c ... @3889 +'h1000
en_reg en_reg_c ... @3906 +'h1001
mem_size_reg mem_size_reg_c ... @3926 +'h100d
oversized_pkt_cnt_reg oversized_pkt_cnt_reg_c ... @3964 +'h1005
parity_err_cnt_reg parity_err_cnt_reg_c ... @3981 +'h1004
-------------------------------------------------------------------------

cdns_uvmreg_utils_pkg

uvm_reg_field_config_t

1
2
3
4
5
6
7
8
9
10
11
typedef struct  {                                                                         
string name;
int unsigned size;
int unsigned lsb_pos;
string access;
bit volatile;
uvm_reg_data_t reset;
bit has_reset;
bit is_rand;
bit individually_accessible;
} uvm_reg_field_config_t;

uvm_reg_config_t

1
2
3
4
5
6
7
8
9
10
11
typedef struct  {
string reg_type_name;
string configUID;
string name;
uvm_reg_addr_t offset;
int unsigned size_bytes;
int unsigned addr_bits;
string access;
int is_array;
string range;
} uvm_reg_config_t;

uvm_blk_config_t

1
2
3
4
5
6
typedef struct {                             
uvm_reg_addr_t base_addr;
int n_bytes;
uvm_endianness_e endianess;
bit byte_addr_t;
} uvm_blk_config_t;

uvm_reg_field_config_ta

1
typedef uvm_reg_field_config_t uvm_reg_field_config_ta[];

uvm_reg_field_config_tq

1
typedef uvm_reg_field_config_ta uvm_reg_field_config_tq[string];

build_uvm_regs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function automatic void build_uvm_regs(uvm_reg_map map, uvm_reg_block pblock, uvm_reg_file pfile, ref uvm_reg_config_t f_props[], output uvm_reg out[$]);
foreach(f_props[idx]) begin
uvm_reg_config_t f= f_props[idx];
if(f.is_array == 0) begin
cdns_uvm_reg reg_t;
uvm_object obj = factory.create(f.reg_type_name, pfile!=null ? pfile.get_full_name(): "",f.name);
assert($cast(reg_t,obj));
reg_t.configure(pblock, pfile, "");
reg_t.setconfigUID(f.configUID);
reg_t.build();
// !!! f.offset !!!
map.add_reg(reg_t, f.offset, f.access);
out.push_back(reg_t);
// !!! print the offset
`uvm_info("[build_uvm_regs]", $sformatf("### %s offset: %0x", f.reg_type_name, f.offset), UVM_LOW)
end

else if (f.is_array == 1) begin
int unsigned array_offset;
string range_str = f.range;
int indx, arr_idx = 0;
int range_arr [];
int arr_size = 1;
  • dynamic array
    • need new[size]
  • queue
    • DONT need new
1
2
3
4
5
6
7
8
9
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### addr0_cnt_reg_c offset: 9                                 
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### addr1_cnt_reg_c offset: a
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### addr2_cnt_reg_c offset: b
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### illegal_addr_cnt_reg_c offset: 6
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### ctrl_reg_c offset: 0
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### en_reg_c offset: 1
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### mem_size_reg_c offset: d
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### oversized_pkt_cnt_reg_c offset: 5
UVM_INFO ./cdns_uvmreg_utils_pkg.sv(322) @ 0: reporter [[build_uvm_regs]] ### parity_err_cnt_reg_c offset: 4

In submap, offset is 0-based, router.add_submap(router_yapp_regs.default_map, ``UVM_REG_ADDR_WIDTH'h1000) configure base addr for submap

IP-XACT XML : yapp_router_regs.xml

image-20220404194550165

memory

yapp_pkt_mem

image-20220404194628354

yapp_mem

image-20220404194721416

register

image-20220404194941613

yapp_regs

ctrl_reg

image-20220404195052310

en_reg

image-20220404195156776

Coverage Options

  • Explicit (user defined)
    • Fully and clearly expressed within sources
      • Sequence and temporal coverage using assertions
      • Data-oriented coverage using covergroup
    • Planned and defined by the verification team
  • Implicit
    • Derived or computed from source
      • Code coverage measured by the simulator
    • May be defined outside the verification team
    • May be implied by the verification interface
      • e.g., an industry standard protocol

Explicit Coverage in Systemverilog

Assertions for control-oriented coverage

  • Defined as procedural statements
  • CANNOT be defined in a class
1
2
3
4
5
6
7
8
property req_gnt (cyc);
@(posedge clk)
$rose(req) ##0 (req && !gnt)[cyc] ##1 gnt;
endproperty

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

Control-oriented coverage uses SystemVerilog Assertion (SVA) syntax and the cover directive. It is used to cover sequences of signal values over time. Assertions cannot be declared or "covered" in a class declaration, so their use in an UVM verification environment is restricted to interface only

Covergroup for data-oriented coverage

  • CAN be declared as a class member and created in class constructor
  • Used in interface and module UVCs
1
2
3
4
5
6
7
8
9
10
covergroup cg @(posedge clk);
len: coverpoint pkt.lenght {
illegal_bins zero = {0};
bins sml = {[1:10]}
...
}
addrxlen : cross pkt.addr, len;
endgroup

cg cg1 = new();

Data-oriented coverage uses the covergroup construct. Covergroups can be declared and created in classes, therefore they are essential for explicit coverage in a UVM verification environment.

Interface Monitor Coverage

covergroup new constructor is called directly off the declaration of the covergroup, it does not have a separate instance name

1
collected_pkts_cq = new();

The covergroup instance must be created in the class constructor, not a UVM build_phase or other phase method

image-20220403210225837

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class yapp_tx_monitor extends uvm_monitor;
// constructor

covergroup collected_pkts_cg;
...
endgroup

yapp_packet packet_collected;

task run_phase(uvm_phase phase);
collect_packet();
...
endtask

task collect_packet();
// read RTL signals via interface
// reconstruct packet
collected_pkts_cg.sample();
...
endtask

...
endclass

Module UVC Coverage

Typical module UVC coverage:

  • Routing - packets flow from input ports to all legal outputs
  • latency - all packets received within speicied delay

The module UVC monitor receives data from the interface monitors via analysis ports, so the implementation of the analysis write function is a convenient place to put coverage code

image-20220403212529981

image-20220403094350570

image-20220403094404891

  • Blocking methods are defined with get() or put() tasks to allow them to consume time
  • Non-blocking methods are defined with try_get() or try_put() functions as they execute in zero time

Uni-Directional TLM Methods Reference

Method Description Syntax
put() Blocking put virtual task put(iput TR t);
try_put() Nonblocking put
-return 1 if successful
-return 0 if not
virtual function bit try_put(input TR t);
can_put() Nonblocking test put
-return 1 if put would be successful
-return 0 if not
virtual function bit can_put();
get() Blocking get virtual task get(output TR t);
try_get() Nonblocking get
-return 1 if successful
-return 0 if not
virtual function bit try_get(output TR t);
can_get() Nonblocking test get
-return 1 if get would be successful
-return 0 if not
virtual function bit can_get();
peek() Blocking peek virtual task peek(output TR t);
try_peek() Nonblocking peek
-return 1 if successful
-return 0 if not
virtual function bit try_peek(output TR t);
can_peek Nonblocking test peek
-return 1 if peek would be successful
-return 0 if not
virtual function bit can_peek();

The peek() methods are similarly to the get() methods, but copy the transaction instead of removing it. The transaction is not consumed, and a subsequent get or peek operation will return the same transaction

Selected Connector and Method Options

put try_put can_put get try_get can_get peek try_peed can_peek
uvm_put_*
uvm_blocking_put_*
uvm_nonblocking_put_*
uvm_get_*
uvm_blocking_get_*
uvm_nonblocking_get_*
uvm_get_peek_*
uvm_blocking_get_peek_*
uvm_nonblocking_get_peek_*

in the connectors above, * can be replaced by port, imp, or export

All the methods for a specific connector type MUST be implemented. If you define an uvm_put connection between two compoents, then the component with the uvm_put_imp object must provide implementations of ALL three put methods, put, try_put and can_put, even if these methods are not explicitly called

TLM FIFO

The TLM FIFO is a FIFO component wrapped in get and put imp connectors. This has the benefit of data storage as well as providing implementations of the communication methods. Components connected to the TLM FIFO are in control of data transfer and can simply defined port connectors to initiate read and write operations on the FIFO

uvm_tlm_fifo

image-20220403141222126

The TLM FIFO object is effectively a FIFO component instantiated between and connected to two components. The FIFO contains imp connectors for the standard TLM put and get/peek interfaces, therefore the user does not have to defineimp ports or communication methods and the FIRO takes care of data storage

The advantages are:

  • The user does not need to define communication methods or imp connectors
  • The FIFO provides data storage between the write (put) and read (get/peek) components
  • There are a number of built-in methods for checking FIFO status

The disadvantages are:

  • The user must now initiate both sides of the transfer (both get/peek and put) to complete the transaction
  • Two connections must be made (both sides of the FIFO) rather than one

The put_export and get_peek_export connection objects have alternatives which provide subsets of the full connector. For example, blocking_put_export and nonblocking_put_export can replace put_export. blocking_get_export, nonblocking_get_export and get_export (as well as others) can replace get_peek_export.

built-in methods

Method Description Syntax
new Standard component constructor with an additional third argument, size, which sets the maximum FIFO size. Default size is 1. A size of 0 is an unbounded FIFO function new(string name, uvm_component parent=null, int size=1);
size Return size of FIFO. 0 indicates unbounded FIFO virtual function int size()
used Return number of entries written to the FIFO virtual function int used();
is_empty Return 1 if used() is 0, otherwise 0 virtual function bit is empty();
is_full Return 1 if used() is equal to size, otherwise 0 virtual function bit is_full()
flush Delete all entries from the FIFO, upon which used() is 0 and is_empty() is 1 virtual funciton void flush();
1
2
3
class uvm_tlm_fifo #(type T=int) extends uvm_tlm_fifo_base #(T);
...
endclass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
virtual class uvm_tlm_fifo_base #(type T=int) extends uvm_component;
uvm_put_imp #(T, this_type) put_export;

uvm_get_peek_imp #(T, this_type) get_peek_export;

uvm_analysis_port #(T) put_ap;

uvm_analysis_port #(T) get_ap;

// The following are aliases to the above put_export
uvm_put_imp #(T, this_type) blocking_put_export;
uvm_put_imp #(T, this_type) nonblocking_put_export;

// The following are all aliased to the above get_peek_export, which provides
// the superset of these interfaces.
uvm_get_peek_imp #(T, this_type) blocking_get_export;
uvm_get_peek_imp #(T, this_type) nonblocking_get_export;
uvm_get_peek_imp #(T, this_type) get_export;

uvm_get_peek_imp #(T, this_type) blocking_peek_export;
uvm_get_peek_imp #(T, this_type) nonblocking_peek_export;
uvm_get_peek_imp #(T, this_type) peek_export;

uvm_get_peek_imp #(T, this_type) blocking_get_peek_export;
uvm_get_peek_imp #(T, this_type) nonblocking_get_peek_export;

function new(string name, uvm_component parent = null);
super.new(name, parent);

put_export = new("put_export", this);
blocking_put_export = put_export;
nonblocking_put_export = put_export;

get_peek_export = new("get_peek_export", this);
blocking_get_peek_export = get_peek_export;
nonblocking_get_peek_export = get_peek_export;
blocking_get_export = get_peek_export;
nonblocking_get_export = get_peek_export;
get_export = get_peek_export;
blocking_peek_export = get_peek_export;
nonblocking_peek_export = get_peek_export;
peek_export = get_peek_export;

put_ap = new("put_ap", this);
get_ap = new("get_ap", this);

endfunction

Analysis FIFO

uvm_tlm_analysis_fifo

image-20220403144951278

uvm_tlm_analysis_fifo is a specialization of uvm_tlm_fifo

  • Intended to buffer write transactions between the UVC monitor analysis port and scoreboard

It has the following characteristics:

  • Unbounded (size=0)
  • analysis_export connector replaces put_export
    • Support the analysis write method

image-20220403150326750

By declaring the FIFO in the scoreboard, we can get directly from the FIFO output.

However the write side connection of the FIFO to the interface UVC monitor analysis port must be made (usually) in the testbench which has visibility of both UVC and scoreboard components. The connection is made using a connect method call inside the connect phase method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class uvm_tlm_analysis_fifo #(type T = int) extends uvm_tlm_fifo #(T);

// Port: analysis_export #(T)
//
// The analysis_export provides the write method to all connected analysis
// ports and parent exports:
//
//| function void write (T t)
//
// Access via ports bound to this export is the normal mechanism for writing
// to an analysis FIFO.
// See write method of <uvm_tlm_if_base #(T1,T2)> for more information.

uvm_analysis_imp #(T, uvm_tlm_analysis_fifo #(T)) analysis_export;


// Function: new
//
// This is the standard uvm_component constructor. ~name~ is the local name
// of this component. The ~parent~ should be left unspecified when this
// component is instantiated in statically elaborated constructs and must be
// specified when this component is a child of another UVM component.

function new(string name , uvm_component parent = null);
super.new(name, parent, 0); // analysis fifo must be unbounded
analysis_export = new("analysis_export", this);
endfunction

const static string type_name = "uvm_tlm_analysis_fifo #(T)";

virtual function string get_type_name();
return type_name;
endfunction

function void write(input T t);
void'(this.try_put(t)); // unbounded => must succeed
endfunction

endclass

Analysis Port Broadcast

image-20220403152922101

Analysis ports can (uniquely) be connected to any number of imp connectors, including zero

image-20220403153148158

The analysis port in component yapp_monitor will be connected to both monitor_one and monitor_two components. Each of the receiving components has an analysis imp object and a write communication method declared. The write method must have the same signature - i.e., they must be void functions called write with a single input argument of type yapp_packet, but their implementations can be completely different.

When the send_yapp port is connected to both mone_in and mtwo_in imps, then a single write call from yapp_monitor executes both write implementations in monitor_one and monitor_two

image-20220403153742706

For non-analysis connections

  • the TLM object must be connected to a single destination only, i.e., each non-analysis object has exactly one connect call. An object may be used multiple times as the argument to a connect, but exactly once as the caller. This is called many-to-one connection. For example, many ports can be connected to a one imp connector

For analysis connections

  • a TLM object can be connected to any number of destinations i.e., one analysis object can call connect many times. This is called one-to-many connection. For example, one port can be connected to many imp connectors. one-to-many is only allowed for analysis connections. An analysis connection can also NOT call connect. An unconnected TLM object is also only allowed for analysis connections

Bi-Directional TLM Transport Connection

image-20220403160248099

Connector syntax

1
2
3
uvm_blocking_transport_XXX #(type REA, type RSP)
uvm_nonblocking_transport_XXX #(type REQ, type RSP)
uvm_transport_XXX #(type REQ, type RSP)

Communication methods

1
2
task transport(REQ request, output RSP response)
function bit nb_transport(REQ request, output RSP response)

Gotchas

FIFO/analysis FIFOs do not perform any cloning on input transactions. Therefore, you will need to check that the UVC monitors collect every transaction into a different instance to avoid overwriting data in the FIFOs

imp and port connectors are sufficient for modeling most connections, but there is third connector, export, which is used exclusively in hierarchical connections.

Normally hierarchical routing is not required. A port on an UVC monitor can be connected directly to an imp in a scoreboard by specifying full hierarchical pathname (e.g., env.agent.monitor.analysis_port). The structure of an UVC is fixed and so the user knows to look in the monitor component for the analysis ports.

However the internal hierarchy of a module UVC is more arbitrary, and it may be convenient to route all the module UVC connectors to the top level to allow use without knowledge of the internal structure.

On the initiator side

  • ports are routed up the hierarchy via other port instances

On the target side

  • only the component which defines the communication method is allowed to have an imp instance. So we need a third object to route connections up the target side hierarchy - export

The hierarchical route must be connected at each level in the direction of control flow:

1
initiator.connect(target)

Connection rules are as follows:

  • port initiators can be connected to port, export or imp targets
  • export initiators can be connected to export or imp targets
  • imp cannot be connection initiator. imp is target only and is always the last connection object on a route

uvm_analysis_port

1
2
3
class uvm_analysis_port # (
type T = int
) extends uvm_port_base # (uvm_tlm_if_base #(T,T))

uvm_analysis_port

1
2
3
class uvm_analysis_port # (
type T = int
) extends uvm_port_base # (uvm_tlm_if_base #(T,T))

uvm_analysis_imp

1
2
3
4
class uvm_analysis_imp #(
type T = int,
type IMP = int
) extends uvm_port_base #(uvm_tlm_if_base #(T,T))

QA

  1. What are the three distinct functions of a scoreboard?

    Reference model, expected data storage and comparison

  2. To how many consumer components can an analysis port be connected?

    Any number, including zero

  3. What are the names of the two declarations made available by the following macro:

    1
    `uvm_analysis_imp_decl(_possible)

    Subclass uvm_analysis_imp_possible

    Method write_possible

  4. Why should a scoreboard clone the data received by a write method?

    The write method only sends a reference, therefore if the sending component uses the same reference for every data item, overwriting of data in the storage function of the scoreboard is possible without cloning.

topo.drawio

compile

  • -incdir for all UVC's `include
  • all pkg.sv
  • all if.sv
  • all RTL DUT
  • uvm top module

uvm top.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
module tb_top;

// import the UVM library
import uvm_pkg::*;

// include the UVM macros
`include "uvm_macros.svh"

// import the yapp UVC
import yapp_pkg::*;
import hbus_pkg::*;
import channel_pkg::*;
import clock_and_reset_pkg::*;

// include the test library file
`include "router_tb.sv"
`include "router_test_lib.sv"

initial begin
yapp_vif_config::set(null, "uvm_test_top.tb.yapp.tx_agent.*", "vif", hw_top.in0);
hbus_vif_config::set(null, "uvm_test_top.tb.hbus.*", "vif", hw_top.hif );
channel_vif_config::set(null, "uvm_test_top.tb.chan0.*", "vif", hw_top.ch0);
channel_vif_config::set(null, "uvm_test_top.tb.chan1.*", "vif", hw_top.ch1);
channel_vif_config::set(null, "uvm_test_top.tb.chan2.*", "vif", hw_top.ch2);
clock_and_reset_vif_config::set(null, "uvm_test_top.tb.clock_and_reset.*", "vif", hw_top.clk_rst_if);
run_test();
end


endmodule : tb_top

RTL top

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
module hw_top;

// Clock and reset signals
logic [31:0] clock_period;
logic run_clock;
logic clock;
logic reset;

// YAPP Interface to the DUT
yapp_if in0(clock, reset);
// reset will now be generated by the Clock and Reset UVC
// input: clock; output: reset, run_clock, clock_period
clock_and_reset_if clk_rst_if(.clock(clock), .reset(reset), .run_clock(run_clock), .clock_period(clock_period));
hbus_if hif(.clock(clock), .reset(reset));

channel_if ch0(.clock(clock), .reset(reset));
channel_if ch1(.clock(clock), .reset(reset));
channel_if ch2(.clock(clock), .reset(reset));

// CLKGEN module generates clock
clkgen clkgen (
.clock(clock),
.run_clock(run_clock),
.clock_period(clock_period)
);

yapp_router dut(
...


endmodule

pkg.sv

`include all related uvm class in pgk.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package yapp_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"

typedef uvm_config_db#(virtual yapp_if) yapp_vif_config;

`include "yapp_packet.sv"
`include "yapp_tx_monitor.sv"
`include "yapp_tx_sequencer.sv"
`include "yapp_tx_seqs.sv"
`include "yapp_tx_driver.sv"
`include "yapp_tx_agent.sv"
`include "yapp_env.sv"

endpackage

if.sv

1
2
3
4
5
6
7
8
9
10
11
interface yapp_if (input clock, input reset );
timeunit 1ns;
timeprecision 100ps;

import uvm_pkg::*;
`include "uvm_macros.svh"

import yapp_pkg::*;
...

endinterface : yapp_if

import and compile order

project

yapp_pkg.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package yapp_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"

typedef uvm_config_db#(virtual yapp_if) yapp_vif_config;

`include "yapp_packet.sv"
`include "yapp_tx_monitor.sv"
`include "yapp_tx_sequencer.sv"
`include "yapp_tx_seqs.sv"
`include "yapp_tx_driver.sv"
`include "yapp_tx_agent.sv"
`include "yapp_env.sv"

endpackage

yapp_tx_monitor.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class yapp_tx_monitor extends uvm_monitor;

// Collected Data handle
yapp_packet pkt;

// Count packets collected
int num_pkt_col;

// component macro
`uvm_component_utils_begin(yapp_tx_monitor)
`uvm_field_int(num_pkt_col, UVM_ALL_ON + UVM_NOCOMPARE)
`uvm_component_utils_end
/////////////////// virtual interface ////////////////////
virtual interface yapp_if vif;
/////////////////////////////////////////////////////////

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

endclass : yapp_tx_monitor

yapp_tx_driver.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class yapp_tx_driver extends uvm_driver #(yapp_packet);

////////////////// virtual interface ////////////////////
virtual interface yapp_if vif;
/////////////////////////////////////////////////////

int num_sent;

// component macro
`uvm_component_utils_begin(yapp_tx_driver)
`uvm_field_int(num_sent, UVM_ALL_ON + UVM_NOCOMPARE)
`uvm_component_utils_end

// Constructor - required syntax for UVM automation and utilities
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

function void start_of_simulation_phase(uvm_phase phase);
`uvm_info(get_type_name(), {"start of simulation for ", get_full_name()}, UVM_HIGH)
endfunction : start_of_simulation_phase
...

endclass : yapp_tx_driver

yapp_if.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface yapp_if (input clock, input reset );
timeunit 1ns;
timeprecision 100ps;

import uvm_pkg::*;
`include "uvm_macros.svh"

////////////////// import package //////////////////////
import yapp_pkg::*;
//////////////////////////////////////////////////////

// Actual Signals
logic in_data_vld;
logic in_suspend;
logic [7:0] in_data;

// signal for transaction recording
bit monstart, drvstart;

// local storage for payload
logic [7:0] payload_mem [0:63];
...

endinterface : yapp_if

virtual interface is in package;

yapp_pkg is imported into interface

!!! typedef uvm_config_db#(virtual yapp_if) yapp_vif_config; is forward declaration

A forward typedef declares an identifier as a type in advance of the full definition of that type

VCS compile

1
vcs -full64 -R -sverilog  -ntb_opts uvm-1.2 +UVM_TESTNAME=short_yapp_012_test  -f vcs.f  

vcs.f

1
2
3
4
5
6
7
8
9
-timescale=1ns/1ns

+incdir+../sv

../sv/yapp_if.sv
../sv/yapp_pkg.sv
./clkgen.sv
./hw_top.sv
./tb_top.sv

output ERROR

1
2
3
4
5
6
Error-[SV-LCM-PND] Package not defined
../sv/yapp_if.sv, 18
yapp_if, "yapp_pkg::"
Package scope resolution failed. Token 'yapp_pkg' is not a package.
Originating module 'yapp_if'.
Move package definition before the use of the package.

xrun compile

1
xrun -f xrun.f

xrun.f

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-64

-uvmhome CDNS-1.2

// incdir for include files
-incdir ../sv

+UVM_TESTNAME=short_yapp_012_test

-timescale 1ns/1ns

// compile files
../sv/yapp_if.sv
../sv/yapp_pkg.sv
./clkgen.sv
./hw_top.sv
./tb_top.sv

output Error

1
2
3
4
5
6
file: ../sv/yapp_if.sv
import yapp_pkg::*;
|
xmvlog: *E,NOPBIND (../sv/yapp_if.sv,18|14): Package yapp_pkg could not be bound.
interface worklib.yapp_if:sv
errors: 1, warnings: 0

solution

place ../sv/yapp_pkg.sv before ../sv/yapp_if.sv.

In this particular example, yapp_pkg is NOT needed in interface. just delete import yapp_pkg::* is enough in ../sv/yapp_if.sv

plain example

The order of compilation unit DON'T matter

project

hw_top.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
module hw_top;

// Clock and reset signals
logic [31:0] clock_period;
logic run_clock;
logic clock;
logic reset;

// YAPP Interface to the DUT
yapp_if in0(clock, reset);

// CLKGEN module generates clock
clkgen clkgen (
.clock(clock),
.run_clock(1'b1),
.clock_period(32'd10)
);

yapp_router dut(
.reset(reset),
.clock(clock),
.error(),
// YAPP interface signals connection
.in_data(in0.in_data),
.in_data_vld(in0.in_data_vld),
.in_suspend(in0.in_suspend),
// Output Channels
//Channel 0
.data_0(),
.data_vld_0(),
.suspend_0(1'b0),
//Channel 1
.data_1(),
.data_vld_1(),
.suspend_1(1'b0),
//Channel 2
.data_2(),
.data_vld_2(),
.suspend_2(1'b0),
// Host Interface Signals
.haddr(),
.hdata(),
.hen(),
.hwr_rd());

...

endmodule

yapp_router.sv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
module yapp_router (input clock,                              
input reset,
output error,

// Input channel
input [7:0] in_data,
input in_data_vld,
output in_suspend,
// Output Channels
output [7:0] data_0, //Channel 0
output reg data_vld_0,
input suspend_0,
output [7:0] data_1, //Channel 1
output reg data_vld_1,
input suspend_1,
output [7:0] data_2, //Channel 2
output reg data_vld_2,
input suspend_2,

// Host Interface Signals
input [15:0] haddr,
inout [7:0] hdata,
input hen,
input hwr_rd);
...
endmodule

compile

place hw_top.sv before or after yapp_router.sv doesn't matter, the compiler (xrun, vcs) can compile them successfully.

Conclusion

Always place package before DUT is preferred choice during compiling.

refernce

class forward declaration

https://vlsiverify.com/uvm/uvm_config_db-in-uvm#Precedence_Rule

Two Precedence rules

There are two precedence rules applicable to uvm_config_db. In the build_phase,

  1. A set() call in a context higher up the component hierarchy takes precedence over a set() call that occurs lower in the hierarchical path.
  2. On having same context field, the last set() call takes precedence over the earlier set() call.

Example for rule - 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
`include "uvm_macros.svh"
import uvm_pkg::*;

class component_A extends uvm_component;
int id;
`uvm_component_utils(component_A)

function new(string name = "component_A", uvm_component parent = null);
super.new(name, parent);
id = 1;
endfunction

function display();
`uvm_info(get_type_name(), $sformatf("inside component_A: id = %0d", id), UVM_LOW);
endfunction
endclass

class component_B extends component_A;
int receive_value;
int id;

`uvm_component_utils(component_B)

function new(string name = "component_B", uvm_component parent = null);
super.new(name, parent);
id = 2;
endfunction

function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db #(int)::get(this, "*", "value", receive_value))
`uvm_fatal(get_type_name(), "get failed for resource in this scope");
endfunction

function display();
`uvm_info(get_type_name(), $sformatf("inside component_B: id = %0d, receive_value = %0d", id, receive_value), UVM_LOW);
endfunction
endclass

class env extends uvm_env;
`uvm_component_utils(env)
component_A comp_A;
component_B comp_B;

function new(string name = "env", uvm_component parent = null);
super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
super.build_phase(phase);
comp_A = component_A ::type_id::create("comp_A", this);
comp_B = component_B ::type_id::create("comp_B", this);

uvm_config_db #(int)::set(this, "*", "value", 200);
endfunction

task run_phase(uvm_phase phase);
super.run_phase(phase);
void'(comp_A.display());
void'(comp_B.display());
endtask
endclass

class my_test extends uvm_test;
bit control;
`uvm_component_utils(my_test)
env env_o;

function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction

function void build_phase(uvm_phase phase);
super.build_phase(phase);
////////////////////////////////////////////////////
env_o = env::type_id::create("env_o", this); // line 99

uvm_config_db #(int)::set(null, "*", "value", 100); // line 101
////////////////////////////////////////////////////
endfunction

function void end_of_elaboration_phase(uvm_phase phase);
super.end_of_elaboration_phase(phase);
uvm_top.print_topology();
endfunction
endclass

module tb_top;
initial begin
run_test("my_test");
end
endmodule
1
2
3
4
5
6
7
8
9
10
11
12
UVM_INFO /xcelium20.09/tools//methodology/UVM/CDNS-1.2/sv/src/base/uvm_root.svh(605) @ 0: reporter [UVMTOP] UVM testbench topology:
--------------------------------------
Name Type Size Value
--------------------------------------
uvm_test_top my_test - @1810
env_o env - @1877
comp_A component_A - @1922
comp_B component_B - @1953
--------------------------------------

UVM_INFO testbench.sv(14) @ 0: uvm_test_top.env_o.comp_A [component_A] inside component_A: id = 1
UVM_INFO testbench.sv(36) @ 0: uvm_test_top.env_o.comp_B [component_B] inside component_B: id = 2, receive_value = 100

As you can see , the uvm_config_db::set is after the factory type_id::create, but receive_value is still 100, I believe that build_phase of upper hierarchy finish first before create lower components.

But the recommendation in literature and many web is place uvm_config_db::set before create

Simple Example for rule - 2

1
2
uvm_config_db #(int)::set(null, "*", "value", 100);
uvm_config_db #(int)::set(null, "*", "value", 200);

[complete code]

1
2
3
4
5
6
7
8
9
10
11
12
UVM_INFO /xcelium20.09/tools//methodology/UVM/CDNS-1.2/sv/src/base/uvm_root.svh(605) @ 0: reporter [UVMTOP] UVM testbench topology:
--------------------------------------
Name Type Size Value
--------------------------------------
uvm_test_top my_test - @1810
env_o env - @1877
comp_A component_A - @1924
comp_B component_B - @1955
--------------------------------------

UVM_INFO testbench.sv(14) @ 0: uvm_test_top.env_o.comp_A [component_A] inside component_A: id = 1
UVM_INFO testbench.sv(36) @ 0: uvm_test_top.env_o.comp_B [component_B] inside component_B: id = 2, receive_value = 200

Another Example for rule - 2

The set in super.build_phase and set in current build_phase are at the same hierarchy, so rule-2 apply

base_test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class base_test extends uvm_test;

// component macro
`uvm_component_utils(base_test)

router_tb tb;

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

// UVM build_phase()
function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_int::set(this, "*", "recording_detail", 1);
uvm_config_wrapper::set(this, "tb.yapp.tx_agent.sequencer.run_phase",
"default_sequence",
yapp_5_packets::get_type());
tb = router_tb::type_id::create("tb", this);
endfunction : build_phase

endclass : base_test

test2 - One

yapp_incr_payload_seq override the yapp_5_packets in super.build_phase(phase), yapp_incr_payload_seq is used.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class test2 extends base_test;

// component macro
`uvm_component_utils(test2)

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

function void build_phase(uvm_phase phase);
yapp_packet::type_id::set_type_override(short_yapp_packet::get_type());
super.build_phase(phase);
uvm_config_wrapper::set(this, "tb.yapp.tx_agent.sequencer.run_phase",
"default_sequence",
yapp_incr_payload_seq::get_type());
endfunction : build_phase

endclass : test2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---------------------------------------------------------------------------------------------------------------------                   
Name Type Size Value
---------------------------------------------------------------------------------------------------------------------
req short_yapp_packet - @3045
length integral 6 'h5
addr integral 2 'h1
payload da(integral) 5 -
[0] integral 8 'h0
[1] integral 8 'h1
[2] integral 8 'h2
[3] integral 8 'h3
[4] integral 8 'h4
parity integral 8 'h11
parity_type parity_t 1 GOOD_PARITY
packet_delay integral 32 'd19
begin_time time 64 0
depth int 32 'd2
parent sequence (name) string 21 yapp_incr_payload_seq
parent sequence (full name) string 61 uvm_test_top.tb.yapp.tx_agent.sequencer.yapp_incr_payload_seq
sequencer string 39 uvm_test_top.tb.yapp.tx_agent.sequencer
---------------------------------------------------------------------------------------------------------------------

test2 - Two

the yapp_5_packets in super.build_phase(phase) overide yapp_incr_payload_seq in test2's build_phase, yapp_5_packets is used

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class test2 extends base_test;

// component macro
`uvm_component_utils(test2)

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

function void build_phase(uvm_phase phase);
yapp_packet::type_id::set_type_override(short_yapp_packet::get_type());
uvm_config_wrapper::set(this, "tb.yapp.tx_agent.sequencer.run_phase",
"default_sequence",
yapp_incr_payload_seq::get_type());
super.build_phase(phase);
endfunction : build_phase

endclass : test2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
--------------------------------------------------------------------------------------------------------------                          
Name Type Size Value
--------------------------------------------------------------------------------------------------------------
req short_yapp_packet - @3176
length integral 6 'h6
addr integral 2 'h1
payload da(integral) 6 -
[0] integral 8 'h4f
[1] integral 8 'hbe
[2] integral 8 'hb
[3] integral 8 'h1d
[4] integral 8 'h72
[5] integral 8 'hc5
parity integral 8 'h49
parity_type parity_t 1 GOOD_PARITY
packet_delay integral 32 'd0
begin_time time 64 40
depth int 32 'd2
parent sequence (name) string 14 yapp_5_packets
parent sequence (full name) string 54 uvm_test_top.tb.yapp.tx_agent.sequencer.yapp_5_packets
sequencer string 39 uvm_test_top.tb.yapp.tx_agent.sequencer
--------------------------------------------------------------------------------------------------------------

  • Laplace transform
    • a generalization of the continuous-time Fourier transform
    • converts integro-differential equations into algebraic equations
  • z-transforms
    • a generalization of the discrete-time Fourier transform
    • converts difference equations into algebraic equations

system function, transfer function: \(H(s)\)

frequency response: \(H(j\omega)\), if the ROC of \(H(s)\) includes the imaginary axis, i.e.\(s=j\omega \in \text{ROC}\)

Laplace Transform

To specify the Laplace transform of a signal, both the algebraic expression and the ROC are required. The ROC is the range of values of \(s\) for the integral of \(t\) converges

bilateral Laplace transform

image-20241001140721422

where \(s\) in the ROC and \(\mathfrak{Re}\{s\}=\sigma\)

The formal evaluation of the integral for a general \(X(s)\) requires the use of contour integration in the complex plane. However, for the class of rational transforms, the inverse Laplace transform can be determined without directly evaluating eq. (9.56) by using the technique of partial fraction expansion

ROC Property

The range of values of s for which the integral in converges is referred to as the region of convergence (which we abbreviate as ROC) of the Laplace transform

image-20241001133645466

image-20241001152307222

image-20241001153011746

i.e. no pole in RHP for stable LTI sytem

System Causality

For a causal LTI system, the impulse response is zero for \(t \lt 0\) and thus is right sided

image-20241001164006698

causality implies that the ROC is to the right of the rightmost pole, but the converse is not in general true, unless the system function is rational

System Stability

The system is stable, or equivalently, that \(h(t)\) is absolutely integrable and therefore has a Fourier transform, then the ROC must include the entire \(j\omega\)-axis

image-20241001223128791

image-20241001163547031

all of the poles have negative real parts

Unilateral Laplace transform

analyzing causal systems and, particularly, systems specified by linear constant-coefficient differential equations with nonzero initial conditions (i.e., systems that are not initially at rest)

image-20241001180202348

A particularly important difference between the properties of the unilateral and bilateral transforms is the differentiation property \(\frac{d}{dt}x(t)\)

Laplace Transform
Bilateral Laplace Transform \(sX(s)\)
Unilateral Laplace Transform \(sX(s)-x(0^-)\)

image-20241001183543726

Integration by parts for unilateral Laplace transform

image-20241001184124032

in Bilateral Laplace Transform

image-20241001231719320

In fact, the initial- and final-value theorems are basically unilateral transform properties, as they apply only to signals \(x(t)\) that are identically \(0\) for \(t \lt 0\).

\(z\)-Transform

The \(z\)-transform for discrete-time signals is the counterpart of the Laplace transform for continuous-time signals

image-20241001204402907

where \(z=re^{j\omega}\)

image-20241001204810918

The \(z\)-transform evaluated on the unit circle corresponds to the Fourier transform

image-20241001223944822

ROC Property

image-20241001222148012

system stability

The system is stable, or equivalently, that \(h[n]\) is absolutely summable and therefore has a Fourier transform, then the ROC must include the unit circle

Unilateral \(z\)-Transform

image-20241001225603923

The time shifting property is different in the unilateral case because the lower limit in the unilateral transform definition is fixed at zero, \(x[n-n_0]\)

bilateral \(z\)-transform

image-20241001232749554

unilateral \(z\)-transform

image-20241001233014484

Initial rest condition

image-20241001233548788

Initial Value Theorem & Final Value Theorem

Laplace Transform

Two valuable Laplace transform theorem

  • Initial Value Theorem, which states that it is always possible to determine the initial value of the time function \(f(t)\) from its Laplace transform \[ \lim _{s\to \infty}sF(s) = f(0^+) \]

  • Final Value Theorem allows us to compute the constant steady-state value of a time function given its Laplace transform \[ \lim _{s\to 0}sF(s) = f(\infty) \]

    If \(f(t)\) is step response, then \(f(0^+) = H(\infty)\) and \(f(\infty) = H(0)\), where \(H(s)\) is transfer function

\(z\)-transform

Initial Value Theorem \[ f[0]=\lim_{z\to\infty}F(z) \] final value theorem \[ \lim_{n\to\infty}f[n]=\lim_{z\to1}(z-1)F(z) \]

Coert Vonk. Initial/final value proofs [https://coertvonk.com/physics/lfz-transforms/z/initial-final-value-proofs-31543]

\(s\)- and \(z\)-Domains Conversion

image-20240429220303281

image-20240429215455332

Staszewski, Robert Bogdan, and Poras T. Balsara. All-digital frequency synthesizer in deep-submicron CMOS. John Wiley & Sons, 2006.


Connection between the Laplace transform and the \(z\)-transform

image-20241002112611432

A continuous-time system with transfer function \(H(s)\) that is identical in structure to the discrete-time system \(H[z]\) except that the delays in \(H[z]\) are replaced by elements that delay continuous-time signals.

delay element Time domain Transform
\(z\)-transform \(\delta[n-1]\) \(z^{-1}\)
Laplace transform \(\delta(t-T)\) \(e^{-sT}\)
\(z\)-transform Laplace transform
\(x[n]\) \(X[z]=\sum_{n=0}^{\infty}x[n]z^{-n}\) \(\bar{x}(t)=\sum_{n=0}^{\infty}x[n]\delta(t-nT)\) \(\overline{X}(s)=\sum_{n=0}^{\infty}x[n]e^{-snT}\)
\(y[n]\) \(Y[z]=\sum_{n=0}^{\infty}y[n]z^{-n}\) \(\bar{y}(t)=\sum_{n=0}^{\infty}y[n]\delta(t-nT)\) \(\overline{Y}(s)=\sum_{n=0}^{\infty}y[n]e^{-snT}\)
\(h[n]\) \(H[z]=\sum_{n=0}^{\infty}h[n]z^{-n}\) \(\bar{h}(t)=\sum_{n=0}^{\infty}h[n]\delta(t-nT)\) \(\overline{H}(s)=\sum_{n=0}^{\infty}h[n]e^{-snT}\)
\(y[n]=x[n]*h[n]\) \(Y[z]=X[z]H[z]\) \(\bar{y}(t)=\bar{x}(t)*\bar{h}(t)\) \(\overline{Y}(s)=\overline{X}(s)\overline{H}(s)\)

Therefore, we obtain

\[\begin{align} \sum_{n=0}^{\infty}y[n]z^{-n} &=\sum_{n=0}^{\infty}x[n]z^{-n}\cdot\sum_{n=0}^{\infty}h[n]z^{-n} \\ \sum_{n=0}^{\infty}y[n]e^{-snT} &=\sum_{n=0}^{\infty}x[n]e^{-snT}\cdot \sum_{n=0}^{\infty}h[n]e^{-snT} \end{align}\]

The \(z\)-transform of a sequence can be considered to be the Laplace transform of impulses sampled train with a change of variable \(z = e^{sT}\) or \(s = \frac{1}{T}\ln z\)

Note that the transformation \(z = e^{sT}\) transforms the imaginary axis in the \(s\) plane (\(s = j\omega\)) into a unit circle in the \(z\) plane (\(z = e^{sT} = e^{j\omega T}\), or \(|z| = 1\))

Note: \(\bar{h}(t)\) is the impulse sampled version of \(h(t)\)


Note \(\bar{h}(t)\) is impulse sampled signal, whose CTFT is scaled by \(\frac{1}{T}\) of continuous signal \(h(t)\), \(\overline{H}[e^{sT}]=\overline{H}(e^{sT})\) is the approximation of continuous time system response, for example summation \(\frac{1}{1-z^{-1}}\) \[ \frac{1}{1-z^{-1}} = \frac{1}{1-e^{-sT}} \approx \frac{1}{j\omega \cdot T} \] And we know transform of integral \(u(t)\) is \(\frac{1}{s}\), as expected there is ratio \(T\)

impulse invariance

A straightforward and useful relationship between the continuous-time impulse response \(h_c(t)\) and the discrete-time impulse response \(h[n]\)

image-20241002133303153

\[ h[n] = Th_c(nT) \]

and \(T\) is chosen such that

\[ H_c(j\omega)=0, \space\space |\hat{\omega}| \ge \frac{\pi}{T} \]

When \(h[n]\) and \(h_c(t)\) are related through the above equation, i.e., the impulse response of the discrete-time system is a scaled, sampled version of \(h_c(t)\), the discrete-time system is said to be an impulse-invariant version of the continuous-time system

we have \[ H(e^{j\hat{\omega}}) = H_c\left(j\frac{\hat{\omega}}{T}\right),\space\space |\hat{\omega}| \lt \pi \]

  • \(h[n] = Th_c(nT)\) & \(T\) is small enough

    only guarantees the output equivalence only at the sampling instants, that is, \(y_c(nT) = y_r(nT)\)

  • Provided \(H_c(j\Omega)\) is bandlimited & \(T \lt 1/2f_h\)

    guarantees \(y_c(t) = y_r(t)\)

The scaling of \(T\) can alternatively be thought of as a normalization of the time domain, that is average impulse response shall be same i.e., \(h[n]\times 1 = h(nT)\times T\)

Transfer function & sampled impulse response

continuous-time filter designs to discrete-time designs through techniques such as impulse invariance

useful functions

  • using fft

    The outputs of the DFT are samples of the DTFT

  • using freqz

    modeling as FIR filter, and the impulse response sequence of an FIR filter is the same as the sequence of filter coefficients, we can express the frequency response in terms of either the filter coefficients or the impulse response

    fft is used in freqz internally

freqz method is straightforward, which apply impulse invariance criteria. Though fft is used for signal processing mostly,


image-20241002082937903

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
clear all;
clc;

%% continuous system
s = tf('s');
h = 2*pi/(2*pi+s); % First order lowpass filter with 3-dB frequency 1Hz
[mag, phs, wout] = bode(h);
fct = wout(:)/2/pi;
Hct_dB = 20*log10(mag(:));


fstep = 0.01; % freq resolution
fnyqst = 1000;
Ts = 1/(2*fnyqst);
Fs = 1/Ts; % sampling freq
Ns = ceil(Fs/fstep); % samping points
fstep = Fs/Ns; % update fstep
t = (0:Ns-1)*Ts; % sampling time points

y = impulse(h, t); % impulse resp

%% modelling as discrete system
Y = fft(y); % dft
Hfft = Y * Ts; % !!! multiply Ts
Hfft_dB = 20*log10(abs(Hfft(1:Ns/2+1)));
ffft = (1:Ns/2+1)*fstep - fstep;


[Hfir, ffir] = freqz(y, 1, [], 1/Ts); % modelling as FIR
Hfir = Hfir * Ts; % !!! multiply Ts
Hfir_dB = 20*log10(abs(Hfir));

%% plot
semilogx(fct, Hct_dB, '-ok', ffft, Hfft_dB, 'r.-', ffir, Hfir_dB, 'b--');
legend('bode(s)', 'fft', 'FIR model')
xlabel('Freq(Hz)');
ylabel('dB');
xlim([1e-2 1e2]);
grid on;
title('frequency response of different methods');

!!! only multiply Ts

image-20250517083748566

FIR Equalization

Frequency Response

image-20220322093428287 \[ z = e^{j\omega T_s} \]

Unit impulse

filter coefficients are [-0.131, 0.595, -0.274] and sampling period is 100ps

image-20220428125454912

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
%% Frequency response
w = [-0.131, 0.595, -0.274];
Ts = 100e-12;
[mag, w] = freqz(w, 1, [], 1/Ts);
subplot(2, 1, 1)
plot(w/1e9, 20*log10(abs(mag)));
xlabel('Freq(GHz)');
ylabel('dB');
grid on;
title('frequency response');

%% unit impulse response from transfer function
subplot(2, 1, 2)
z = tf('z', Ts);
h = -0.131 + 0.595*z^(-1) -0.274*z^(-2);
[y, t] = impulse(h);
stem(t*1e10, y*Ts); % !!! y*Ts is essential
grid on;
title("unit impulse response");
xlabel('Time(\times 100ps)');
ylabel('mag');

impulse:

For discrete-time systems, the impulse response is the response to a unit area pulse of length Ts and height 1/Ts, where Ts is the sample time of the system. (This pulse approaches \(\delta(t)\) as Ts approaches zero.)

Scale output:

Multiply impulse output with sample period Ts in order to correct 1/Ts height of impulse function.

PSD transformation

If we have power spectrum or power spectrum density of both edge's absolute jitter (\(x(n)\)) , \(P_{\text{xx}}\)

Then 1UI jitter is \(x_{\text{1UI}}(n)=x(n)-x(n-1)\), and Period jitter is \(x_{\text{Period}}(n)=x(n)-x(n-2)\), which can be modeled as FIR filter, \(H(\omega) = 1-z^{-k}\), i.e. \(k=1\) for 1UI jitter and \(k=2\) Period jitter \[\begin{align} P_{\text{xx}}'(\omega) &= P_{\text{xx}}(\omega) \cdot \left| 1-z^{-k} \right|^2 \\ &= P_{\text{xx}}(\omega) \cdot \left| 1-(e^{j\omega T_s})^{-k} \right|^2 \\ &= P_{\text{xx}}(\omega) \cdot \left| 1-e^{-j\omega T_s k} \right|^2 \end{align}\]

image-20220519172239916

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
clear all
close all
clc

xf_fs = 0:0.01:0.5;
k = 1;
H_1UI = 1 - exp(-1i*2*pi*xf_fs);
HH_1UI = abs(H_1UI).^2;
subplot(2, 1, 1);
plot(xf_fs, HH_1UI);
grid on;
xlabel('Freq');
ylabel('|H|^2')
title('Weight for 1UI jitter');

k = 2;
H_period = 1 - exp(-1i*2*pi*xf_fs);
HH_period = abs(H_period).^2;
subplot(2, 1, 2)
plot(xf_fs, HH_period);
grid on;
xlabel('Freq');
ylabel('|H|^2')
title('Weight for Period jitter');

image-20220709104127384 \[ x(t-\Delta T)\overset{FT}{\longrightarrow} X(s)e^{-\Delta T \cdot s} \]

Bilinear Transform

[https://tttapa.github.io/Pages/Mathematics/Systems-and-Control-Theory/Digital-filters/Discretization/Bilinear-transform.html]

an algebraic transformation between the variables \(s\) and \(z\) that maps the entire imaginary \(j\Omega\)-axis in the \(s\)-plane to one revolution of the unit circle in the \(z\)-plane

\[\begin{align} z &= \frac{1+s\frac{T_s}{2}}{1-s\frac{T_s}{2}} \\ s &= \frac{2}{T_s}\cdot \frac{1-z^{-1}}{1+z^{-1}} \end{align}\]

where \(T_s\) is the sampling period

image-20241125001024136

frequency warping:

image-20241125002523844

  • The bilinear transformation avoids the problem of aliasing encountered with the use of impulse invariance, because it maps the entire imaginary axis of the \(s\)-plane onto the unit circle in the \(z\)-plane

  • impulse invariance cannot be used to map highpass continuous-time designs to high pass discrete-time designs, since highpass continuous-time filters are not bandlimited

  • Due to nonlinear warping of the frequency axis introduced by the bilinear transformation, bilinear transformation applied to a continuous-time differentiator will not result in a discrete-time differentiator. However, impulse invariance can be applied to bandlimited continuous-time differentiator

    The feature of the frequency response of discrete-time differentiators is that it is linear with frequency


The simple approximation \(z=e^{sT}\approx1+sT\), the first equal come from impulse invariance essentially

image-20241024230308374

image-20241026230904395

reference

Alan V. Oppenheim, Alan S. Willsky, and S. Hamid Nawab. 1996. Signals & systems (2nd ed.)

Alan V Oppenheim, Ronald W. Schafer. 2010. Discrete-Time Signal Processing, 3rd edition

B.P. Lathi, Roger Green. Linear Systems and Signals (The Oxford Series in Electrical and Computer Engineering) 3rd Edition

Sam Palermo, ECEN720, Lecture 7: Equalization Introduction & TX FIR Eq

Sam Palermo, ECEN720, Lab5 –Equalization Circuits

B. Razavi, "The z-Transform for Analog Designers [The Analog Mind]," IEEE Solid-State Circuits Magazine, Volume. 12, Issue. 3, pp. 8-14, Summer 2020. [https://www.seas.ucla.edu/brweb/papers/Journals/BR_SSCM_3_2020.pdf]

Jhwan Kim, CICC 2022, ES4-4: Transmitter Design for High-speed Serial Data Communications

Mathuranathan. Digital filter design – Introduction [https://www.gaussianwaves.com/2020/02/introduction-to-digital-filter-design/]

Daniel Boschen, "Fast Track to Designing FIR Filters with Python" [https://events.gnuradio.org/event/24/contributions/598/attachments/186/485/Boschen%20FIR%20Filter%20Presentation.pdf]

Daniel Boschen, "Quick Start on Control Loops with Python" [https://events.gnuradio.org/event/24/contributions/599/attachments/187/480/Boschen%20Control%20Presentation.pdf]

Most user-defined UVM classes are placed in separate files

Classes can only be compiled from a module or package scope

Recommendation:

  • Include related class files into a package
  • Import the package into modules where required

The UVM library is supplied in the package uvm_pkg

  • Import package to access library
  • The macro file uvm_macros.svh must be included separately

yapp_packet.sv

1
2
3
class yapp_packet extends uvm_sequence_item;
...
endclass : yapp_packet

yapp_pkg.sv

1
2
3
4
5
6
package  yapp_pkg;
import uvm_pkg::*;
`include "uvm_macros.svh"

`include "yapp_packet.sv"
endpackage : yapp_pkg

run.f for xrun

1
2
3
4
5
6
7
8
9
10
11
// 64 bit option for AWS labs
-64

-uvmhome CDNS-1.2

// include directories
-incdir ../sv

// compile files
../sv/yapp_pkg.sv
./top.sv
0%