Friday, 29 January 2016

Difference between casex and casez



The case, casex and casez all do bit-wise comparisons between the selecting case expression and individual case item statements.

For casex and casez, comparisons are performed using the identity operator === instead of equality ==. casex ignores any bit position containing an X or Z; casez only ignores bit positions with a Z. 

Verilog literals use the both the  ? and z characters to represent the Z state.

// casex ignores any bit position containing an X or Z,
// Verilog literals use the both the ? and z characters to represent the Z state
module casex_example();
reg [3:0] opcode;
reg [1:0] a,b,c;
reg [1:0] out;
always @ (opcode or a or b or c)
casex(opcode)
4'b1zzx : begin // Don't care 2:0 bits
out = a;
$display($time," 4'b1zzx is selected, opcode %b",opcode);
end
4'b01?? : begin // bit 1:0 is don't care
out = b;
$display($time," 4'b01?? is selected, opcode %b",opcode);
end
4'b001? : begin // bit 0 is don't care
out = c;
$display($time," 4'b001? is selected, opcode %b",opcode);
end
default : begin
$display($time," default is selected, opcode %b",opcode);
end
endcase
// Testbench code goes here
always #2 a = $random;
always #2 b = $random;
always #2 c = $random;
initial begin
opcode = 0;
#2 opcode = 4'b101x;
#2 opcode = 4'b1x1x;
#2 opcode = 4'b1001;
#2 opcode = 4'b0010;
#2 opcode = 4'b01x1;
#2 opcode = 4'b0000;
#2 opcode = 4'b001z;
#2 opcode = 4'b001x;
#2 $finish;
end
endmodule
//Output:
// 0 default is selected, opcode 0000
// 2 4'b1zzx is selected, opcode 101x
// 4 4'b1zzx is selected, opcode 1x1x
// 6 4'b1zzx is selected, opcode 1001
// 8 4'b001? is selected, opcode 0010
// 10 4'b01?? is selected, opcode 01x1
// 12 default is selected, opcode 0000
// 14 4'b001? is selected, opcode 001z
// 16 4'b001? is selected, opcode 001x
view raw casex_ex.sv hosted with ❤ by GitHub

// casez only ignores bit positions with a Z
// Verilog literals use the both the ? and z characters to represent the Z state
module casez_example();
reg [3:0] opcode;
reg [1:0] a,b,c;
reg [1:0] out;
always @ (opcode or a or b or c)
casez(opcode)
4'b1zzx : begin // Don't care about lower 2:1 bit, bit 0 match with x
out = a;
$display($time," 4'b1zzx is selected, opcode %b",opcode);
end
4'b01?? : begin // bit 1:0 is don't care
out = b;
$display($time," 4'b01?? is selected, opcode %b",opcode);
end
4'b001? : begin // bit 0 is don't care
out = c;
$display($time," 4'b001? is selected, opcode %b",opcode);
end
default : begin
$display($time," default is selected, opcode %b",opcode);
end
endcase
// Testbench code goes here
always #2 a = $random;
always #2 b = $random;
always #2 c = $random;
initial begin
opcode = 0;
#2 opcode = 4'b101x;
#2 opcode = 4'b1x1x;
#2 opcode = 4'b1001;
#2 opcode = 4'b0010;
#2 opcode = 4'b01x1;
#2 opcode = 4'b0000;
#2 opcode = 4'b001z;
#2 opcode = 4'b001x;
#2 $finish;
end
endmodule
//Output:
// 0 default is selected, opcode 0000
// 2 4'b1zzx is selected, opcode 101x
// 4 4'b1zzx is selected, opcode 1x1x
// 6 default is selected, opcode 1001
// 8 4'b001? is selected, opcode 0010
// 10 4'b01?? is selected, opcode 01x1
// 12 default is selected, opcode 0000
// 14 4'b001? is selected, opcode 001z
// 16 4'b001? is selected, opcode 001x
view raw casez_ex.sv hosted with ❤ by GitHub

Saturday, 23 January 2016

Different coding style of Flop/latch in VHDL

-- Flop with Sync set/reset.
process (clk)
begin
if (rising_edge(clk)) then
if (rst1 = '0') then
out1 <= '0';
elsif (rst2 = '1') then
out1 <= '1';
else
out1 <= in1;
end if;
end if;
end process;
-- Flop with multi Sync reset.
process (clk)
begin
if (rising_edge(clk)) then
if (rst1 = '0') then
out2 <= '0';
elsif (rst2 = '1') then
out2 <= '0';
else
out2 <= in2;
end if;
end if;
end process;
-- Flop with Async set/reset
process (clk, rst1, rst2)
begin
if (rst1 = '0') then
out1 <= '0';
else
if (rst2 = '1') then
out1 <= '1';
else
if (rising_edge(clk)) then
out1 <= in1;
end if;
end if;
end if;
end process;
-- Flop with multi Async reset
process (clk, rst1, rst2)
begin
if (rst1 = '0') then
out2 <= '0';
else
if (rst2 = '1') then
out2 <= '0';
else
if (rising_edge(clk)) then
out2 <= in2;
end if;
end if;
end if;
end process;
-- Latch with Async set/reset
process (clk, rst1, rst2)
begin
if(rst1 = '0') then
out1 <= '0';
elsif(rst2 = '1') then
out1 <= '1';
elsif (clk = '1') then
out1 <= in1;
end if;
end process;
-- Latch with multi Async reset
process (clk, rst1, rst2)
begin
if (rst1 = '0' or rst2 = '1') then
out2 <= '0';
elsif (clk = '1') then
out2 <= in2;
end if;
end process;
-- Flop with Sync reset
process (clk)
begin
if (rising_edge(clk)) then
if (rst = '0') then
out2 <= '0';
else
out2 <= in1;
end if;
end if;
end process;

What is the difference between ‘0’ and “0” in VHDL?

In VHDL, the '0' (single quote) is a single bit when the signal type is std_logic or bit. If you declare:
     signal foo : std_logic_vector(7 downto 0);
and attempt the assignment:
    foo <= '0';
you'll get a compile-time error indicating a size mismatch. That's because you're trying to assign a one-bit value to an 8-bit vector. (NB: Verilog will happily sign-extend, or is it zero-extend? that single-bit into the full vector.)

library ieee;
use ieee.std_logic_1164.all;
entity core1 is
end entity;
architecture core1 of core1 is
signal a : std_logic := '0';
signal c : std_logic_vector (3 downto 0) := '0';
begin
end core1;
--Outputs:
-- Error-[ANL-EXPRTYP-MISMATCH] Expression type mismatch
-- top.vhd, 11
-- CORE1
--
-- signal c : std_logic_vector (3 downto 0) := '0';
-- ^
-- Expression is not of the required type. Expecting an expression of type
-- STD_LOGIC_VECTOR declared in ARCHITECTURE CORE1.


library ieee;
use ieee.std_logic_1164.all;
entity core1 is
end entity;
architecture core1 of core1 is
signal a : std_logic := '0';
signal c : std_logic_vector (3 downto 0) := "0";
begin
end core1;
--Outputs:
-- Error-[ANL-AGGR-STRLITSHORT] String literal too short
-- top.vhd, 11
-- CORE1
--
-- signal c : std_logic_vector (3 downto 0) := "0";
-- ^
-- Length of 1 is shorter than the expected length of 4.


-- Compile free code
library ieee;
use ieee.std_logic_1164.all;
entity core1 is
end entity;
architecture core1 of core1 is
signal a : std_logic := '0';
signal c : std_logic_vector (3 downto 0) := "0000";
begin
end core1;



For std_logic_vector, it represents a vector of only one bit. For example, the vector may be declared as std_logic_vector(0 downto 0).

library ieee;
use ieee.std_logic_1164.all;
entity core1 is
end entity;
architecture core1 of core1 is
signal a : std_logic := '0';
signal c : std_logic_vector (0 downto 0) := '0';
begin
end core1;
--Outputs:
-- Error-[ANL-EXPRTYP-MISMATCH] Expression type mismatch
-- top.vhd, 9
-- CORE1
--
-- signal c : std_logic_vector (0 downto 0) := '0';
-- ^
-- Expression is not of the required type. Expecting an expression of type
-- STD_LOGIC_VECTOR declared in ARCHITECTURE CORE1.


-- Compile free code
library ieee;
use ieee.std_logic_1164.all;
entity core1 is
end entity;
architecture core1 of core1 is
signal a : std_logic := '0';
signal c : std_logic_vector (0 downto 0) := "0";
begin
end core1;

Difference between rising_edge(clk) and (clk'event and clk='1') in VHDL


In VHDL there are two ways to find an edge transition of any signal (generally clock).

  1. rising_edge(clk)
  2. clk'event and clk = '1' 
So in this article I will explain the difference between rising_edge or falling_edge function and clk'event based edge detection. 

Example 1:
library ieee;
use ieee.std_logic_1164.all;
entity core1 is
end entity;
architecture core1 of core1 is
constant clk_period : time := 2 ns;
signal clk : std_logic := '0';
signal a : std_logic := '0';
signal b : std_logic := '0';
begin
process
begin
clk <= '0';
wait for clk_period/2; --for 1 ns signal is '0'.
clk <= '1';
wait for clk_period/2; --for next 1 ns signal is '1'.
end process;
process (clk)
begin
if (rising_edge(clk)) then
a <= not a;
end if;
if (clk'event and clk='1') then
b <= not b;
end if;
end process;
process
begin
wait for 10 ns; --run the simulation for this duration
assert false
report "simulation ended"
severity failure;
end process;
end core1;


Result of Example 1:
 

Example 2:
library ieee;
use ieee.std_logic_1164.all;
entity core1 is
end entity;
architecture core1 of core1 is
constant clk_period : time := 2 ns;
signal clk : std_logic := '0';
signal a : std_logic := '0';
signal b : std_logic := '0';
begin
process
begin
clk <= 'Z';
wait for clk_period/2; --for 1 ns signal is '0'.
clk <= '1';
wait for clk_period/2; --for next 1 ns signal is '1'.
end process;
process (clk)
begin
if (rising_edge(clk)) then
a <= not a;
end if;
if (clk'event and clk='1') then
b <= not b;
end if;
end process;
process
begin
wait for 10 ns; --run the simulation for this duration
assert false
report "simulation ended"
severity failure;
end process;
end core1;
Result of Example 2:

To get a clear view look at the rising_edge function as implemented in std_logic_1164 library:

FUNCTION rising_edge (SIGNAL s : std_ulogic) RETURN BOOLEAN IS
BEGIN
RETURN (s'EVENT AND (To_X01(s) = '1') AND
(To_X01(s'LAST_VALUE) = '0'));
END;

As you can see the function returns a value "TRUE" only when the present value is '1' and the last value is '0'.If the past value is something like 'Z','U' etc. then it will return a "FALSE" value.This makes the code, bug free, beacuse the function returns only valid clock transitions,that means '0' to '1'.All the rules and examples said above equally apply to falling_edge() function also. 

But the statement (clk'event and clk='1') results TRUE when the present value is '1' and there is an edge transition in the clk.It doesnt see whether the previous value is '0' or not.



Sunday, 17 January 2016

Difference between UVM_DEFAULT and UVM_ALL_ON

 
// A=ABSTRACT,
// Y=PHYSICAL,
// F=REFERENCE,
// S=SHALLOW,
// D=DEEP
// K=PACK,
// R=RECORD,
// P=PRINT,
// M=COMPARE,
// C=COPY
//--------------------------- AYFSD K R P M C
parameter UVM_DEFAULT     = 'b000010101010101;
parameter UVM_ALL_ON      = 'b000000101010101;
 
So, UVM_DEFAULT turns on the "D"EEP bit, whereas UVM_ALL_ON has it turned off.
(You can see it in src/base/uvm_object_globals.svh file (uvm 1.1c or later))

Saturday, 16 January 2016

TLM1 in UVM


Before going into the TLM interface concepts, let’s see why we need TLM interface: 

Port based Data Transfer:
Following is a simple verification environment.
Components generator and driver are implemented as modules. These modules are connected using module ports or SV interfaces.
The advantage of this methodology is, the two above mentioned components are independent. Instead of consumer module, any other component which can understand producer interface can be connected, which gives a great re-usability. 
The disadvantage of this methodology is, data transfer is done at lower level of abstraction. 

Task based Data Transfer:
In the above environment, methods are used to transfer the data between components.
So, this gives a better control and data transfer is done at high level.
The disadvantage is, components are using hierarchical paths which do not allow the re-usability.  

TLM interface:
UVM has TLM interfaces which provide the advantages which we saw in the above two data transfer styles.
Data is transferred at high level of abstraction. Transactions which are developed by extending the uvm_sequence_item can be transferred between components using method calls. These methods are not hierarchical fixed, so that components can be reused.
The advantages of TLM interfaces are
 1) Higher level abstraction
 2) Reusable. Plug and play connections.
 3) Maintainability
 4) Less code.
 5) Easy to implement.
 6) Faster simulation.
 7) Connect to SystemC.
 8) Can be used for reference model development.

TLM-1 and TLM-2.0 are two TLM modeling systems which have been developed as industry standards for building transaction-level models. Both were built in SystemC and standardized within the TLM Working Group of the Open SystemC Initiative (OSCI).

TLM-1 is a message passing system. Interfaces are either untimed or rely on the target for timing. None of the interfaces provide for explicit timing annotations. 

Tlm Terminology:
Producer:
A component which generates a transaction.
Consumer:
A component which consumes the transaction.
Initiator:
A component which initiates process.
Target:
A component which responded to initiator.

Transaction-level interfaces define a set of methods that use transaction objects as arguments. 

Interfaces:
The UVM provides ports, exports and implementation and analysis ports for connecting your components via the TLM interfaces. Port, Export, implementation terminology applies to control flow not to data flow.
Port:
A TLM port defines the set of methods (the application programming interface (API)) to be used for a particular connection.
Import:
Interface that provides an implementation is import or implementation port.
Export:
A TLM export supplies the implementation of those methods which is defined in TLM port. Connecting a port to an export allows the implementation to be executed when the port method is called.
Interface used to route transaction interfaces to other layers of the hierarchy.
Analysis:
Interface used to distribute transactions to passive components. 

  • TLM is all about communication through method calls.
  • A TLM port specifies the “API” to be used. 
  • A TLM export supplies the implementation of the methods. 
  • Connections are between ports/exports, not components. 
  • Transactions are objects. 
  • Ports & exports are parameterized by the transaction type being communicated

Difference between export and import:
Basically, both exports and imps provide the implementations of whatever methods your TLM port requires. The difference is an export is an indirect connection to an implementation. It normally used when there is component hierarchy involved. 

Operation Supported By Tlm Interface:
Putting:
Producer transfers a value to Consumer.
Getting:
Consumer requires a data value from producer.
Peeking:
Copies data from a producer without consuming the data.
Broadcasting:
Transaction is broadcasted to none or one or multiple consumers. 

Putting:
The most basic transaction-level operation allows one component to put a transaction to another. Consider below figure.
The square box on the producer indicates a port and the circle on the consumer indicates the export.
The producer generates transactions and sends them out its put_port:
The actual implementation of the put() call is supplied by the consumer.

`include "uvm_pkg.sv"
import uvm_pkg :: *;
typedef enum {ADD,SUB,MUL,DIV} inst_t;
class instruction extends uvm_sequence_item;
rand inst_t inst;
`uvm_object_utils_begin(instruction)
`uvm_field_enum(inst_t,inst, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "instruction");
super.new(name);
endfunction
endclass : instruction
class producer extends uvm_component;
uvm_blocking_put_port#(instruction) put_port;
`uvm_component_utils(producer)
function new(string name, uvm_component parent = null);
super.new(name,parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase (phase);
put_port = new("put_port", this);
endfunction : build_phase
task run_phase (uvm_phase phase);
for(int i = 0; i < 5; i ++) begin
instruction ints;
#10;
ints = new();
if(ints.randomize()) begin
`uvm_info("producer", $sformatf("sending %s",ints.inst.name()), UVM_LOW)
put_port.put(ints);
end
else begin
`uvm_fatal("producer", $sformatf("Randomization failed"))
end
end
endtask : run_phase
endclass : producer
class consumer extends uvm_component;
// Here We provide only put method, so declared as a blocking imp.
// IF you declared it as a uvm_put_imp then you have to
// supply implementation of non-blocking method (try_put).
uvm_blocking_put_imp#(instruction,consumer) put_export;
`uvm_component_utils(consumer)
function new(string name, uvm_component parent = null);
super.new(name,parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase (phase);
put_export = new("put_export", this);
endfunction : build_phase
task put(instruction t);
`uvm_info("consumer", $sformatf("receiving %s",t.inst.name()), UVM_LOW)
//push the transaction into queue or array
//or drive the transaction to next level
//or drive to interface
endtask : put
endclass : consumer
class my_test extends uvm_test;
producer p;
consumer c;
`uvm_component_utils(my_test)
function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase (phase);
p = new("producer", this);
c = new("consumer", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
p.put_port.connect(c.put_export);
endfunction : connect_phase
task run_phase (uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
//Output:
// UVM_INFO @ 0: reporter [RNTST] Running test my_test...
// UVM_INFO put_port.sv(35) @ 10: uvm_test_top.producer [producer] sending DIV
// UVM_INFO put_port.sv(62) @ 10: uvm_test_top.consumer [consumer] receiving DIV
// UVM_INFO put_port.sv(35) @ 20: uvm_test_top.producer [producer] sending ADD
// UVM_INFO put_port.sv(62) @ 20: uvm_test_top.consumer [consumer] receiving ADD
// UVM_INFO put_port.sv(35) @ 30: uvm_test_top.producer [producer] sending ADD
// UVM_INFO put_port.sv(62) @ 30: uvm_test_top.consumer [consumer] receiving ADD
// UVM_INFO put_port.sv(35) @ 40: uvm_test_top.producer [producer] sending ADD
// UVM_INFO put_port.sv(62) @ 40: uvm_test_top.consumer [consumer] receiving ADD
// UVM_INFO put_port.sv(35) @ 50: uvm_test_top.producer [producer] sending DIV
// UVM_INFO put_port.sv(62) @ 50: uvm_test_top.consumer [consumer] receiving DIV
// UVM_INFO /remote/vtgimages/SAFE/linux_RH5_EM64T_TD_32_Engineer/release-structure/vcs-mx/etc/uvm-1.1/base/uvm_objection.svh(1267) @ 100: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase

In this case, the put()call in the producer will block until the consumer’s put implementation is complete.
Consumer could be replaced by another component that also implements put and producer will continue to work in exactly the same way. 

Getting:
In this case, the consumer requests transactions from the producer via its get port:
The get() implementation is supplied by the producer.
`include "uvm_pkg.sv"
import uvm_pkg :: *;
typedef enum {ADD,SUB,MUL,DIV} inst_t;
class instruction extends uvm_sequence_item;
rand inst_t inst;
`uvm_object_utils_begin(instruction)
`uvm_field_enum(inst_t,inst, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "instruction");
super.new(name);
endfunction
endclass : instruction
class producer extends uvm_component;
uvm_blocking_get_imp#(instruction,producer) get_export;
`uvm_component_utils(producer)
function new(string name, uvm_component parent = null);
super.new(name,parent);
get_export = new("get_export", this);
endfunction : new
task get(ref instruction inst);
inst = new();
if(inst.randomize()) begin
`uvm_info("producer", $sformatf("sending %s",inst.inst.name()), UVM_LOW)
end
endtask : get
endclass : producer
class consumer extends uvm_component;
uvm_blocking_get_port#(instruction) get_port;
`uvm_component_utils(consumer)
function new(string name, uvm_component parent = null);
super.new(name,parent);
get_port = new("get_port", this);
endfunction
task run_phase (uvm_phase phase);
for(int i = 0 ; i < 5; i ++ )begin
instruction inst;
get_port.get(inst);
`uvm_info("consumer", $sformatf("receiving %s",inst.inst.name()), UVM_LOW)
//push the transaction into queue or array
//or drive the transaction to next level
//or drive to interface
end
endtask : run_phase
endclass : consumer
class my_test extends uvm_test;
producer p;
consumer c;
`uvm_component_utils(my_test)
function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase (phase);
p = new("producer", this);
c = new("consumer", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
c.get_port.connect(p.get_export);
endfunction : connect_phase
task run_phase (uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
//Output:
// UVM_INFO @ 0: reporter [RNTST] Running test my_test...
// UVM_INFO get_port.sv(29) @ 0: uvm_test_top.producer [producer] sending SUB
// UVM_INFO get_port.sv(47) @ 0: uvm_test_top.consumer [consumer] receiving SUB
// UVM_INFO get_port.sv(29) @ 0: uvm_test_top.producer [producer] sending MUL
// UVM_INFO get_port.sv(47) @ 0: uvm_test_top.consumer [consumer] receiving MUL
// UVM_INFO get_port.sv(29) @ 0: uvm_test_top.producer [producer] sending SUB
// UVM_INFO get_port.sv(47) @ 0: uvm_test_top.consumer [consumer] receiving SUB
// UVM_INFO get_port.sv(29) @ 0: uvm_test_top.producer [producer] sending SUB
// UVM_INFO get_port.sv(47) @ 0: uvm_test_top.consumer [consumer] receiving SUB
// UVM_INFO get_port.sv(29) @ 0: uvm_test_top.producer [producer] sending MUL
// UVM_INFO get_port.sv(47) @ 0: uvm_test_top.consumer [consumer] receiving MUL
// UVM_INFO /remote/vtgimages/SAFE/linux_RH5_EM64T_TD_32_Engineer/release-structure/vcs-mx/etc/uvm-1.1/base/uvm_objection.svh(1267) @ 100: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase

get() call will block until the get_producer’s method completes. In TLM terms, put() and get() are blocking methods. 

Communicating between Processes:
In the basic put example above, the consumer will be active only when its put() method is called. In many cases, it may be necessary for components to operate independently, where the producer is creating transactions in one process while the consumer needs to operate on those transactions in another. UVM provides the uvm_tlm_fifo channel to facilitate such communication. The uvm_tlm_fifo implements all of the TLM interface methods, so the producer puts the transaction into the uvm_tlm_fifo, while the consumer independently gets the transaction from the fifo, as shown in below figure.

When the producer puts a transaction into the fifo, it will block if the fifo is full, otherwise it will put the object into the fifo and return immediately.

`include "uvm_pkg.sv"
import uvm_pkg :: *;
typedef enum {ADD,SUB,MUL,DIV} inst_t;
class instruction extends uvm_sequence_item;
rand inst_t inst;
`uvm_object_utils_begin(instruction)
`uvm_field_enum(inst_t,inst, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "instruction");
super.new(name);
endfunction
endclass : instruction
class producer extends uvm_component;
uvm_blocking_put_port#(instruction) put_port;
`uvm_component_utils(producer)
function new(string name, uvm_component parent = null);
super.new(name,parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase (phase);
put_port = new("put_port", this);
endfunction : build_phase
task run_phase (uvm_phase phase);
for(int i = 0; i < 5; i ++) begin
instruction ints;
#10;
ints = new();
if(ints.randomize()) begin
`uvm_info("producer", $sformatf("sending %s",ints.inst.name()), UVM_LOW)
put_port.put(ints);
end
else begin
`uvm_fatal("producer", $sformatf("Randomization failed"))
end
end
endtask : run_phase
endclass : producer
class consumer extends uvm_component;
uvm_blocking_get_port#(instruction) get_port;
`uvm_component_utils(consumer)
function new(string name, uvm_component parent = null);
super.new(name,parent);
get_port = new("get_port", this);
endfunction
task run_phase (uvm_phase phase);
//for(int i = 0 ; i < 5; i ++ )begin
forever begin
instruction inst;
get_port.get(inst);
`uvm_info("consumer", $sformatf("receiving %s",inst.inst.name()), UVM_LOW)
//push the transaction into queue or array
//or drive the transaction to next levelt
//or drive to interface
end
endtask : run_phase
endclass : consumer
class my_test extends uvm_test;
producer p;
consumer c;
uvm_tlm_fifo #(instruction) f;
`uvm_component_utils(my_test)
function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase (phase);
p = new("producer", this);
c = new("consumer", this);
f = new ("tlm_fifo", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
p.put_port.connect(f.put_export);
c.get_port.connect(f.get_export);
endfunction : connect_phase
task run_phase (uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
//Output:
// UVM_INFO @ 0: reporter [RNTST] Running test my_test...
// UVM_INFO tlm_fifo.sv(35) @ 10: uvm_test_top.producer [producer] sending DIV
// UVM_INFO tlm_fifo.sv(59) @ 10: uvm_test_top.consumer [consumer] receiving DIV
// UVM_INFO tlm_fifo.sv(35) @ 20: uvm_test_top.producer [producer] sending ADD
// UVM_INFO tlm_fifo.sv(59) @ 20: uvm_test_top.consumer [consumer] receiving ADD
// UVM_INFO tlm_fifo.sv(35) @ 30: uvm_test_top.producer [producer] sending ADD
// UVM_INFO tlm_fifo.sv(59) @ 30: uvm_test_top.consumer [consumer] receiving ADD
// UVM_INFO tlm_fifo.sv(35) @ 40: uvm_test_top.producer [producer] sending ADD
// UVM_INFO tlm_fifo.sv(59) @ 40: uvm_test_top.consumer [consumer] receiving ADD
// UVM_INFO tlm_fifo.sv(35) @ 50: uvm_test_top.producer [producer] sending DIV
// UVM_INFO tlm_fifo.sv(59) @ 50: uvm_test_top.consumer [consumer] receiving DIV
// UVM_INFO /remote/vtgimages/SAFE/linux_RH5_EM64T_TD_32_Engineer/release-structure/vcs-mx/etc/uvm-1.1/base/uvm_objection.svh(1267) @ 100: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase

The get operation will return immediately if a transaction is available (and will then be removed from the fifo), otherwise it will block until a transaction is available.
Thus, two consecutive get() calls will yield different transactions to the consumer. The related peek()method returns a copy of the available transaction without removing it. Two consecutive peek() calls will return copies of the same transaction. 

Tlm Interface Compilation Models:
Blocking:
A blocking interface conveys transactions in blocking fashion; its methods do not return until the transaction has been successfully sent or retrieved. Its methods are defined as tasks.
virtual task put(input T1 t)
virtual task get(output T2 t)
virtual task peek(output T2 t) 

Non-Blocking:
A non-blocking interface attempts to convey a transaction without consuming simulation time. Its methods are declared as functions. Because delivery may fail (e.g. the target component is busy and cannot accept the request), the methods may return with failed status.
virtual function bit try_put(input T1 t)
virtual function bit can_put()
virtual function bit try_get(output T2 t)
virtual function bit can_get()
virtual function bit try_peek(output T2 t)
virtual function bit can_peek()
The try_put()method returns TRUE if the transaction is sent. 

Combined:
A combination interface contains both the blocking and non-blocking variants. 

Peer-to-Peer connections
When connecting components at the same level of hierarchy, ports are always connected to exports. All connect() calls between components are done in the parent’s connect() method.
See “my_test” class in examples in putting section. 

Port/Export Compatibility:
In order for a connection to be valid, the export must provide implementations for at least the set of methods defined by the port and the transaction type parameter for the two must be identical. 

Hierarchical Connections:
Making connections across hierarchical boundaries involves some additional issues, which are discussed in this section. Consider the hierarchical design shown in below figure.
The hierarchy of this design contains two components, producer and consumer.
Producer contains three components, stim, tlm_fi, and conv. consumer contains two components, tlm_fi and drive.
Notice that, from the perspective of top, the producer and consumer appear identical to those in first figure.

Connections A, B, D, and F are standard peer-to-peer connections as discussed above.
gen.put_port.connect(fifo.put_export);

Connections C and E are of a different sort than what have been shown. Connection C is a port-to-port connection, and connection E is an export-to-export connection. These two kinds of connections are necessary to complete hierarchical connections.

All export-to-export connections in a parent component are of the form export.connect(subcomponent.export);

so connection E would be coded as:
class consumer extends uvm_component;
uvm_put_export #(trans) put_export;
uvm_tlm_fifo #(trans) fifo;
...
function void connect();
put_export.connect(fifo.put_export); // E
bfm.get_port.connect(fifo.get_export); // F
endfunction
...
endclass

Conversely, port-to-port connections are of the form:
subcomponent.port.connect(port);

so connection C would be coded as:
class producer extends uvm_component;
uvm_put_port #(trans) put_port;
conv c;
...
function void connect();
c.put_port.connect(put_port);
...
endfunction 


In short:
  • Port-to-Export
    • port.connect(export);
  • Port-to-Port
    • child.port.connect(parent_port);
  • Export-to-Export
    • parent_export.connect(child.export);
  • Last Export is actually an ‘imp’ 

Connection Type:

  • All TLM connections go from ‘origin’ to ‘destination’
  • port.connect(export);
  • child_port.connect(parent_port);
  • parent_export.connect(child_export); // or imp

Analysis Communication:


Analysis Port:
The uvm_analysis_port (represented as a diamond on the monitor in Figure) is a specialized TLM port whose interface consists of a single function, write().
The analysis port contains a list of analysis_exports that are connected to it. When the component calls analysis_port.write(), the analysis_port cycles through the list and calls the write() method of each connected export.
If nothing is connected, the write() call simply returns. Thus, an analysis port may be connected to zero, one, or many analysis exports, but the operation of the component that writes to the analysis port does not depend on the number of exports connected. Because write() is a void function, the call will always complete in the same delta cycle, regardless of how many  components (for example, scoreboards, coverage collectors, and so on) are connected.

In the parent environment, the analysis port gets connected to the analysis export of the desired components, such as coverage collectors and scoreboards.
As with other TLM connections, it is up to each component connected to an analysis port to provide an implementation of write() via an analysis_export. The uvm_subscriber base component can be used to simplify this operation, so a typical analysis component would extend uvm_subscriber as:
class sub1 #(type T = simple_trans) extends uvm_subscriber #(T);
...
function void write(T t);
// Record coverage information of t.
endfunction
endclass

TLM connection between an analysis port and export, allows the export to supply the implementation of write(). If multiple exports are connected to an analysis port, the port will call the write() of each export, in order. Since all implementations of write() must be functions, the analysis port’s write() function completes immediately, regardless of how many exports are connected to it.

Note:
When multiple subscribers are connected to an analysis_port, each is passed a pointer to the same transaction object, the argument to the write() call. Each write() implementation must make a local copy of the transaction and then operate on the copy to avoid corrupting the transaction contents for any other subscriber that may have received the same pointer.

UVM also includes an analysis_fifo, which is a uvm_tlm_fifo that also includes an analysis export, to allow blocking components access to the analysis transaction stream. The analysis_fifo is unbounded, so the monitor’s write() call is guaranteed to succeed immediately. The analysis component may then get the transactions from the analysis_fifo at its leisure. 

Analysis Export:
uvm_subscriber has built-in single analysis_export. So it allows us to send only single transaction stream.
If you need multiple transaction streams then extend your class from uvm_component and include multiple analysis_export.

There are two ways to do this:
  1. Use imp suffixes defined via macro:
    1. Declare macros outside of component
    2. Instantiate suffixed imps
    3. Implement write_SUFFIX methods
  • Write methods are functions 
    • Can’t synchronize between streams (because there is no way through which we can delay in write() method since it is a function)
`include "uvm_pkg.sv"
import uvm_pkg :: *;
class my_seq_item extends uvm_sequence_item;
rand logic [7:0] addr;
rand logic [7:0] data;
constraint addr_range_cn {
addr inside {[10:20]};
}
constraint data_range_cn {
data inside {[100:200]};
}
`uvm_object_utils_begin(my_seq_item)
`uvm_field_int(addr, UVM_ALL_ON| UVM_DEC)
`uvm_field_int(data, UVM_ALL_ON| UVM_DEC)
`uvm_object_utils_end
function new(string name="my_seq_item");
super.new(name);
endfunction : new
virtual function string convert2string();
convert2string = $sformatf("addr=%0h, data=%0h", addr, data);
endfunction : convert2string
function bit do_compare (uvm_object rhs, uvm_comparer comparer);
my_seq_item tx;
bit mismatch = 1;
int unsigned payload_size;
if(!$cast(tx, rhs)) begin
`uvm_fatal ("do_compare", "Casting failed")
end
do_compare = 1;
mismatch = comparer.compare_field("Packet Addr",this.addr, tx.addr, $bits(this.addr));
if(mismatch == 1'b0) begin
`uvm_error(get_full_name(), $sformatf("Payload addr mismatches, expected = %0h, Actual = %0h", this.addr, tx.addr))
end
do_compare &= mismatch;
mismatch = comparer.compare_field("Packet Paylod",this.data, tx.data, $bits(this.data));
if(mismatch == 1'b0) begin
`uvm_error(get_full_name(), $sformatf("Payload data mismatches, expected = %0h, Actual = %0h", this.data, tx.data))
end
do_compare &= mismatch;
endfunction : do_compare
endclass : my_seq_item
class my_monitor1 extends uvm_monitor;
uvm_analysis_port #(my_seq_item) anls_port;
`uvm_component_utils(my_monitor1)
function new(string name, uvm_component parent = null);
super.new(name,parent);
anls_port = new("anls_port", this);
endfunction : new
task run_phase (uvm_phase phase);
my_seq_item tr;
for (int unsigned i = 0; i < 4; i ++) begin
tr = new(); // Make sure you new object every time, new must be inside loop.
#5;
if (i == 0) begin
tr.addr = 8'h00;
tr.data = 8'h00;
end
else if (i == 1) begin
tr.addr = 8'h11;
tr.data = 8'h11;
end
else if (i == 2) begin
tr.addr = 8'h22;
tr.data = 8'h22;
end
else if (i == 3) begin
tr.addr = 8'h33;
tr.data = 8'h33;
end
`uvm_info(get_full_name(), $sformatf("sending %s", tr.convert2string()), UVM_LOW)
anls_port.write(tr);
end
endtask : run_phase
endclass : my_monitor1
class my_monitor2 extends uvm_monitor;
uvm_analysis_port #(my_seq_item) anls_port;
`uvm_component_utils(my_monitor2)
function new(string name, uvm_component parent = null);
super.new(name,parent);
anls_port = new("anls_port", this);
endfunction : new
task run_phase (uvm_phase phase);
my_seq_item tr;
for (int unsigned i = 0; i < 4; i ++) begin
tr = new(); // Make sure you new object every time, new must be inside loop.
#10;
if (i == 0) begin
tr.addr = 8'h00;
tr.data = 8'h00;
end
else if (i == 1) begin
tr.addr = 8'h11;
tr.data = 8'h11;
end
else if (i == 2) begin
tr.addr = 8'h33;
tr.data = 8'h33;
end
else if (i == 3) begin
tr.addr = 8'h33;
tr.data = 8'h33;
end
`uvm_info(get_full_name(), $sformatf("sending %s", tr.convert2string()), UVM_LOW)
anls_port.write(tr);
end
endtask : run_phase
endclass : my_monitor2
`uvm_analysis_imp_decl(_expected_tr)
`uvm_analysis_imp_decl(_actual_tr)
class my_scoreboard extends uvm_scoreboard;
uvm_in_order_class_comparator #(my_seq_item) data_comparator;
uvm_analysis_imp_expected_tr #(my_seq_item, my_scoreboard) expected_imp;
uvm_analysis_imp_actual_tr #(my_seq_item, my_scoreboard) actual_imp;
/*! Stores count of total received actual packets*/
int act_no_total = 0;
/*! Stores count of total received expected packets*/
int exp_no_total = 0;
// Used to disable scoreboard runtime for some X number of transaction.
bit compare_en = 1;
`uvm_component_utils(my_scoreboard)
function new(string name = "my_scoreboard",uvm_component parent=null);
super.new (name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
expected_imp = new ("expected_imp", this);
actual_imp = new ("actual_imp", this);
data_comparator = new("data_comparator", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
endfunction : connect_phase
task run_phase(uvm_phase phase);
endtask : run_phase
function write_expected_tr(my_seq_item xact);
my_seq_item _xact;
_xact = my_seq_item::type_id::create("_xact");
_xact.copy(xact);
//if (_xact.pkt_direction == FORWARD) begin
`uvm_info (get_full_name(), $sformatf("[write_expected_tr], *****Expected transaction***** = %s",
_xact.convert2string()), UVM_LOW);
if (compare_en) begin
exp_no_total ++;
data_comparator.before_export.write(_xact);
end
//end
endfunction : write_expected_tr
function write_actual_tr(my_seq_item xact);
my_seq_item _xact;
_xact = my_seq_item::type_id::create("_xact");
_xact.copy(xact);
//if (_xact.pkt_direction == FORWARD) begin
`uvm_info (get_full_name(), $sformatf("[write_actual_tr], *****Actual transaction***** = %s",
_xact.convert2string()), UVM_LOW);
if (compare_en) begin
act_no_total ++;
data_comparator.after_export.write(_xact);
end
//end
endfunction : write_actual_tr
function void report_phase(uvm_phase phase);
super.report_phase(phase);
print_report("Master -> Slave", exp_no_total, act_no_total, data_comparator.m_matches, data_comparator.m_mismatches);
endfunction : report_phase
function void print_report(string direction, int exp_no_total, int act_no_total, int matche, int mismatches);
if ((act_no_total == exp_no_total) && (mismatches == 0))
begin
`uvm_info(get_full_name(),
$sformatf(
{"\n|----------------------------------------- |\n",
"| SUMMARY of %s |\n",
"|----------------------------------------- |\n",
"| Direction | %s |\n",
"|----------------------------------------- |\n",
"| Transmitted Packets | %4d |\n",
"|----------------------------------------- |\n",
"| Received Packets | %4d |\n",
"|----------------------------------------- |\n",
"| Comparator Matches | %4d |\n",
"|----------------------------------------- |\n",
"| Comparator Mismatches | %4d |\n",
"|----------------------------------------- |\n"
},
get_full_name(), direction, exp_no_total, act_no_total, matche, mismatches),UVM_LOW)
end
else
begin
`uvm_error(get_full_name(),
$sformatf(
{"\n|----------------------------------------- |\n",
"| SUMMARY of %s |\n",
"|----------------------------------------- |\n",
"| Direction | %s |\n",
"|----------------------------------------- |\n",
"| Transmitted Packets | %4d |\n",
"|----------------------------------------- |\n",
"| Received Packets | %4d |\n",
"|----------------------------------------- |\n",
"| Comparator Matches | %4d |\n",
"|----------------------------------------- |\n",
"| Comparator Mismatches | %4d |\n",
"|----------------------------------------- |\n"
},
get_full_name(), direction, exp_no_total, act_no_total, matche, mismatches))
end
endfunction : print_report
endclass : my_scoreboard
class my_test extends uvm_test;
my_monitor1 mon1;
my_monitor2 mon2;
my_scoreboard sb;
`uvm_component_utils(my_test)
function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase (phase);
mon1 = my_monitor1 :: type_id :: create ("mon1", this);
mon2 = my_monitor2 :: type_id :: create ("mon2", this);
sb = my_scoreboard :: type_id :: create ("sb", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
mon1.anls_port.connect(sb.expected_imp);
mon2.anls_port.connect(sb.actual_imp);
endfunction : connect_phase
task run_phase (uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
//Output:
//UVM_INFO @ 0: reporter [RNTST] Running test my_test...
//UVM_INFO imp_macro.sv(79) @ 5: uvm_test_top.mon1 [uvm_test_top.mon1] sending addr=0, data=0
//UVM_INFO imp_macro.sv(164) @ 5: uvm_test_top.sb [uvm_test_top.sb] [write_expected_tr], *****Expected transaction***** = addr=0, data=0
//UVM_INFO imp_macro.sv(116) @ 10: uvm_test_top.mon2 [uvm_test_top.mon2] sending addr=0, data=0
//UVM_INFO imp_macro.sv(179) @ 10: uvm_test_top.sb [uvm_test_top.sb] [write_actual_tr], *****Actual transaction***** = addr=0, data=0
//UVM_INFO imp_macro.sv(79) @ 10: uvm_test_top.mon1 [uvm_test_top.mon1] sending addr=11, data=11
//UVM_INFO imp_macro.sv(164) @ 10: uvm_test_top.sb [uvm_test_top.sb] [write_expected_tr], *****Expected transaction***** = addr=11, data=11
//UVM_INFO @ 10: uvm_test_top.sb.data_comparator [Comparator Match] addr=0, data=0
//UVM_INFO imp_macro.sv(79) @ 15: uvm_test_top.mon1 [uvm_test_top.mon1] sending addr=22, data=22
//UVM_INFO imp_macro.sv(164) @ 15: uvm_test_top.sb [uvm_test_top.sb] [write_expected_tr], *****Expected transaction***** = addr=22, data=22
//UVM_INFO imp_macro.sv(116) @ 20: uvm_test_top.mon2 [uvm_test_top.mon2] sending addr=11, data=11
//UVM_INFO imp_macro.sv(179) @ 20: uvm_test_top.sb [uvm_test_top.sb] [write_actual_tr], *****Actual transaction***** = addr=11, data=11
//UVM_INFO imp_macro.sv(79) @ 20: uvm_test_top.mon1 [uvm_test_top.mon1] sending addr=33, data=33
//UVM_INFO imp_macro.sv(164) @ 20: uvm_test_top.sb [uvm_test_top.sb] [write_expected_tr], *****Expected transaction***** = addr=33, data=33
//UVM_INFO @ 20: uvm_test_top.sb.data_comparator [Comparator Match] addr=11, data=11
//UVM_INFO imp_macro.sv(116) @ 30: uvm_test_top.mon2 [uvm_test_top.mon2] sending addr=33, data=33
//UVM_INFO imp_macro.sv(179) @ 30: uvm_test_top.sb [uvm_test_top.sb] [write_actual_tr], *****Actual transaction***** = addr=33, data=33
//UVM_INFO @ 30: reporter [MISCMP] Miscompare for _xact.addr: lhs = 'h22 : rhs = 'h33
//UVM_ERROR imp_macro.sv(37) @ 30: reporter@@_xact [_xact] Payload addr mismatches, expected = 22, Actual = 33
//UVM_ERROR imp_macro.sv(43) @ 30: reporter@@_xact [_xact] Payload data mismatches, expected = 22, Actual = 33
//UVM_INFO @ 30: reporter [MISCMP] 3 Miscompare(s) (1 shown) for object _xact@714 vs. _xact@693
//UVM_WARNING @ 30: uvm_test_top.sb.data_comparator [Comparator Mismatch] addr=33, data=33 differs from addr=22, data=22
//UVM_INFO imp_macro.sv(116) @ 40: uvm_test_top.mon2 [uvm_test_top.mon2] sending addr=33, data=33
//UVM_INFO imp_macro.sv(179) @ 40: uvm_test_top.sb [uvm_test_top.sb] [write_actual_tr], *****Actual transaction***** = addr=33, data=33
//UVM_INFO @ 40: uvm_test_top.sb.data_comparator [Comparator Match] addr=33, data=33
//UVM_INFO /remote/vtgimages/SAFE/linux_RH5_EM64T_TD_32_Engineer/release-structure/vcs-mx/etc/uvm-1.1/base/uvm_objection.svh(1267) @ 100: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
//UVM_ERROR imp_macro.sv(216) @ 100: uvm_test_top.sb [uvm_test_top.sb]
//|----------------------------------------- |
//| SUMMARY of uvm_test_top.sb |
//|----------------------------------------- |
//| Direction | Master -> Slave |
//|----------------------------------------- |
//| Transmitted Packets | 4 |
//|----------------------------------------- |
//| Received Packets | 4 |
//|----------------------------------------- |
//| Comparator Matches | 3 |
//|----------------------------------------- |
//| Comparator Mismatches | 1 |
//|----------------------------------------- |
//
//
//--- UVM Report Summary ---
//
//** Report counts by severity
//UVM_INFO : 23
//UVM_WARNING : 1
//UVM_ERROR : 3
//UVM_FATAL : 0
//** Report counts by id
//[Comparator Match] 3
//[Comparator Mismatch] 1
//[MISCMP] 2
//[RNTST] 1
//[TEST_DONE] 1
//[_xact] 2
//[uvm_test_top.mon1] 4
//[uvm_test_top.mon2] 4
//[uvm_test_top.sb] 9

If you need to be able to have streams somehow synchronized, then go with second approach.
  1. Use embedded fifos 
    1. Declare analysis exports 
    2. Connect exports to fifos
  • Run_phase must actively pull from fifos
Here you do whatever synchronization you need to between two transaction stream
Give flexibility to compare out of order transaction

`include "uvm_pkg.sv"
import uvm_pkg :: *;
class my_seq_item extends uvm_sequence_item;
rand logic [7:0] addr;
rand logic [7:0] data;
constraint addr_range_cn {
addr inside {[10:20]};
}
constraint data_range_cn {
data inside {[100:200]};
}
`uvm_object_utils_begin(my_seq_item)
`uvm_field_int(addr, UVM_DEFAULT| UVM_DEC)
`uvm_field_int(data, UVM_DEFAULT| UVM_DEC)
`uvm_object_utils_end
function new(string name="my_seq_item");
super.new(name);
endfunction : new
virtual function string convert2string();
convert2string = $sformatf("addr=%0h, data=%0h", addr, data);
endfunction : convert2string
function bit do_compare (uvm_object rhs, uvm_comparer comparer);
my_seq_item tx;
bit mismatch = 1;
int unsigned payload_size;
if(!$cast(tx, rhs)) begin
`uvm_fatal ("do_compare", "Casting failed")
end
do_compare = 1;
mismatch = comparer.compare_field("Packet Addr",this.addr, tx.addr, $bits(this.addr));
if(mismatch == 1'b0) begin
`uvm_error(get_full_name(), $sformatf("Payload addr mismatches, expected = %0h, Actual = %0h", this.addr, tx.addr))
end
do_compare &= mismatch;
mismatch = comparer.compare_field("Packet Paylod",this.data, tx.data, $bits(this.data));
if(mismatch == 1'b0) begin
`uvm_error(get_full_name(), $sformatf("Payload data mismatches, expected = %0h, Actual = %0h", this.data, tx.data))
end
do_compare &= mismatch;
endfunction : do_compare
endclass : my_seq_item
class my_monitor1 extends uvm_monitor;
uvm_analysis_port #(my_seq_item) anls_port;
`uvm_component_utils(my_monitor1)
function new(string name, uvm_component parent = null);
super.new(name,parent);
anls_port = new("anls_port", this);
endfunction : new
task run_phase (uvm_phase phase);
my_seq_item tr;
for (int unsigned i = 0; i < 4; i ++) begin
#5;
tr = new(); // Make sure you new object every time, new must be inside loop.
if (i == 0) begin
tr.addr = 8'h00;
tr.data = 8'h00;
end
else if (i == 1) begin
tr.addr = 8'h11;
tr.data = 8'h11;
end
else if (i == 2) begin
tr.addr = 8'h22;
tr.data = 8'h22;
end
else if (i == 3) begin
tr.addr = 8'h33;
tr.data = 8'h33;
end
`uvm_info(get_full_name(), $sformatf("sending %s", tr.convert2string()), UVM_LOW)
anls_port.write(tr);
end
endtask : run_phase
endclass : my_monitor1
class my_monitor2 extends uvm_monitor;
uvm_analysis_port #(my_seq_item) anls_port;
`uvm_component_utils(my_monitor2)
function new(string name, uvm_component parent = null);
super.new(name,parent);
anls_port = new("anls_port", this);
endfunction : new
task run_phase (uvm_phase phase);
my_seq_item tr;
for (int unsigned i = 0; i < 4; i ++) begin
tr = new(); // Make sure you new object every time, new must be inside loop.
#10;
if (i == 0) begin
tr.addr = 8'h00;
tr.data = 8'h00;
end
else if (i == 1) begin
tr.addr = 8'h11;
tr.data = 8'h11;
end
else if (i == 2) begin
tr.addr = 8'h33;
tr.data = 8'h33;
end
else if (i == 3) begin
tr.addr = 8'h33;
tr.data = 8'h33;
end
`uvm_info(get_full_name(), $sformatf("sending %s", tr.convert2string()), UVM_LOW)
anls_port.write(tr);
end
endtask : run_phase
endclass : my_monitor2
class my_scoreboard extends uvm_scoreboard;
uvm_in_order_class_comparator #(my_seq_item) data_comparator;
uvm_analysis_export #(my_seq_item) expected_export;
uvm_analysis_export #(my_seq_item) actual_export;
uvm_tlm_analysis_fifo #(my_seq_item) expected_fifo;
uvm_tlm_analysis_fifo #(my_seq_item) actual_fifo;
/*! Stores count of total received actual packets*/
int act_no_total = 0;
/*! Stores count of total received expected packets*/
int exp_no_total = 0;
// Used to disable scoreboard runtime for some X number of transaction.
bit compare_en = 1;
`uvm_component_utils(my_scoreboard)
function new(string name = "my_scoreboard",uvm_component parent=null);
super.new (name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
expected_export = new ("expected_export", this);
actual_export = new ("actual_export", this);
expected_fifo = new ("expected_fifo", this);
actual_fifo = new ("actual_fifo", this);
data_comparator = new ("data_comparator", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
expected_export.connect (expected_fifo.analysis_export);
actual_export.connect (actual_fifo.analysis_export);
endfunction : connect_phase
task run_phase(uvm_phase phase);
my_seq_item expected_tr;
my_seq_item actual_tr;
forever begin
fork
begin
my_seq_item _expected_tr;
expected_fifo.get(expected_tr);
_expected_tr = my_seq_item::type_id::create("_expected_tr");
_expected_tr.copy(expected_tr);
`uvm_info (get_full_name(), $sformatf("Expected transaction = %s",
_expected_tr.convert2string()), UVM_LOW);
exp_no_total ++;
data_comparator.before_export.write(_expected_tr);
end
begin
my_seq_item _actual_tr;
actual_fifo.get(actual_tr);
_actual_tr = my_seq_item::type_id::create("_actual_tr");
_actual_tr.copy(actual_tr);
`uvm_info (get_full_name(), $sformatf("Actual transaction = %s",
_actual_tr.convert2string()), UVM_LOW);
act_no_total ++;
data_comparator.after_export.write(_actual_tr);
end
join
end
endtask : run_phase
function void report_phase(uvm_phase phase);
super.report_phase(phase);
print_report("Master -> Slave", exp_no_total, act_no_total, data_comparator.m_matches, data_comparator.m_mismatches);
endfunction : report_phase
function void print_report(string direction, int exp_no_total, int act_no_total, int matche, int mismatches);
if ((act_no_total == exp_no_total) && (mismatches == 0))
begin
`uvm_info(get_full_name(),
$sformatf(
{"\n|----------------------------------------- |\n",
"| SUMMARY of %s |\n",
"|----------------------------------------- |\n",
"| Direction | %s |\n",
"|----------------------------------------- |\n",
"| Transmitted Packets | %4d |\n",
"|----------------------------------------- |\n",
"| Received Packets | %4d |\n",
"|----------------------------------------- |\n",
"| Comparator Matches | %4d |\n",
"|----------------------------------------- |\n",
"| Comparator Mismatches | %4d |\n",
"|----------------------------------------- |\n"
},
get_full_name(), direction, exp_no_total, act_no_total, matche, mismatches),UVM_LOW)
end
else
begin
`uvm_error(get_full_name(),
$sformatf(
{"\n|----------------------------------------- |\n",
"| SUMMARY of %s |\n",
"|----------------------------------------- |\n",
"| Direction | %s |\n",
"|----------------------------------------- |\n",
"| Transmitted Packets | %4d |\n",
"|----------------------------------------- |\n",
"| Received Packets | %4d |\n",
"|----------------------------------------- |\n",
"| Comparator Matches | %4d |\n",
"|----------------------------------------- |\n",
"| Comparator Mismatches | %4d |\n",
"|----------------------------------------- |\n"
},
get_full_name(), direction, exp_no_total, act_no_total, matche, mismatches))
end
endfunction : print_report
endclass : my_scoreboard
class my_test extends uvm_test;
my_monitor1 mon1;
my_monitor2 mon2;
my_scoreboard sb;
`uvm_component_utils(my_test)
function new(string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase (phase);
mon1 = my_monitor1 :: type_id :: create ("mon1", this);
mon2 = my_monitor2 :: type_id :: create ("mon2", this);
sb = my_scoreboard :: type_id :: create ("sb", this);
endfunction : build_phase
function void connect_phase (uvm_phase phase);
super.connect_phase (phase);
mon1.anls_port.connect(sb.expected_export);
mon2.anls_port.connect(sb.actual_export);
endfunction : connect_phase
task run_phase (uvm_phase phase);
phase.raise_objection(this);
#100;
phase.drop_objection(this);
endtask : run_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
//Output:
// UVM_INFO @ 0: reporter [RNTST] Running test my_test...
// UVM_INFO imp_tlm_fifo.sv(79) @ 5: uvm_test_top.mon1 [uvm_test_top.mon1] sending addr=0, data=0
// UVM_INFO imp_tlm_fifo.sv(172) @ 5: uvm_test_top.sb [uvm_test_top.sb] Expected transaction = addr=0, data=0
// UVM_INFO imp_tlm_fifo.sv(116) @ 10: uvm_test_top.mon2 [uvm_test_top.mon2] sending addr=0, data=0
// UVM_INFO imp_tlm_fifo.sv(79) @ 10: uvm_test_top.mon1 [uvm_test_top.mon1] sending addr=11, data=11
// UVM_INFO imp_tlm_fifo.sv(182) @ 10: uvm_test_top.sb [uvm_test_top.sb] Actual transaction = addr=0, data=0
// UVM_INFO @ 10: uvm_test_top.sb.data_comparator [Comparator Match] addr=0, data=0
// UVM_INFO imp_tlm_fifo.sv(172) @ 10: uvm_test_top.sb [uvm_test_top.sb] Expected transaction = addr=11, data=11
// UVM_INFO imp_tlm_fifo.sv(79) @ 15: uvm_test_top.mon1 [uvm_test_top.mon1] sending addr=22, data=22
// UVM_INFO imp_tlm_fifo.sv(116) @ 20: uvm_test_top.mon2 [uvm_test_top.mon2] sending addr=11, data=11
// UVM_INFO imp_tlm_fifo.sv(79) @ 20: uvm_test_top.mon1 [uvm_test_top.mon1] sending addr=33, data=33
// UVM_INFO imp_tlm_fifo.sv(182) @ 20: uvm_test_top.sb [uvm_test_top.sb] Actual transaction = addr=11, data=11
// UVM_INFO @ 20: uvm_test_top.sb.data_comparator [Comparator Match] addr=11, data=11
// UVM_INFO imp_tlm_fifo.sv(172) @ 20: uvm_test_top.sb [uvm_test_top.sb] Expected transaction = addr=22, data=22
// UVM_INFO imp_tlm_fifo.sv(116) @ 30: uvm_test_top.mon2 [uvm_test_top.mon2] sending addr=33, data=33
// UVM_INFO imp_tlm_fifo.sv(182) @ 30: uvm_test_top.sb [uvm_test_top.sb] Actual transaction = addr=33, data=33
// UVM_INFO @ 30: reporter [MISCMP] Miscompare for _expected_tr.addr: lhs = 'h22 : rhs = 'h33
// UVM_ERROR imp_tlm_fifo.sv(37) @ 30: reporter@@_expected_tr [_expected_tr] Payload addr mismatches, expected = 22, Actual = 33
// UVM_ERROR imp_tlm_fifo.sv(43) @ 30: reporter@@_expected_tr [_expected_tr] Payload data mismatches, expected = 22, Actual = 33
// UVM_INFO @ 30: reporter [MISCMP] 3 Miscompare(s) (1 shown) for object _actual_tr@840 vs. _expected_tr@832
// UVM_WARNING @ 30: uvm_test_top.sb.data_comparator [Comparator Mismatch] addr=33, data=33 differs from addr=22, data=22
// UVM_INFO imp_tlm_fifo.sv(172) @ 30: uvm_test_top.sb [uvm_test_top.sb] Expected transaction = addr=33, data=33
// UVM_INFO imp_tlm_fifo.sv(116) @ 40: uvm_test_top.mon2 [uvm_test_top.mon2] sending addr=33, data=33
// UVM_INFO imp_tlm_fifo.sv(182) @ 40: uvm_test_top.sb [uvm_test_top.sb] Actual transaction = addr=33, data=33
// UVM_INFO @ 40: uvm_test_top.sb.data_comparator [Comparator Match] addr=33, data=33
// UVM_INFO /remote/vtgimages/SAFE/linux_RH5_EM64T_TD_32_Engineer/release-structure/vcs-mx/etc/uvm-1.1/base/uvm_objection.svh(1267) @ 100: reporter [TEST_DONE] 'run' phase is ready to proceed to the 'extract' phase
// UVM_ERROR imp_tlm_fifo.sv(219) @ 100: uvm_test_top.sb [uvm_test_top.sb]
// |----------------------------------------- |
// | SUMMARY of uvm_test_top.sb |
// |----------------------------------------- |
// | Direction | Master -> Slave |
// |----------------------------------------- |
// | Transmitted Packets | 4 |
// |----------------------------------------- |
// | Received Packets | 4 |
// |----------------------------------------- |
// | Comparator Matches | 3 |
// |----------------------------------------- |
// | Comparator Mismatches | 1 |
// |----------------------------------------- |
//
//
// --- UVM Report Summary ---
//
// ** Report counts by severity
// UVM_INFO : 23
// UVM_WARNING : 1
// UVM_ERROR : 3
// UVM_FATAL : 0
// ** Report counts by id
// [Comparator Match] 3
// [Comparator Mismatch] 1
// [MISCMP] 2
// [RNTST] 1
// [TEST_DONE] 1
// [_expected_tr] 2
// [uvm_test_top.mon1] 4
// [uvm_test_top.mon2] 4
// [uvm_test_top.sb] 9