MGC DVCon 13 Sequence Sequence On The Wall Who's The Fairest of Them All
MGC DVCon 13 Sequence Sequence On The Wall Who's The Fairest of Them All
MGC DVCon 13 Sequence Sequence On The Wall Who's The Fairest of Them All
of Them All?
Using SystemVerilog UVM Sequences for Fun and Profit
Rich Edelman
Raghu Ardeishar
Mentor Graphics
Fremont, CA, US
Mentor Graphics
McLean, VA, US
I.
INTRODUCTION
Agent
Agent
MON
seq
SQR
MON
DUT
DRVR
DRVR
SQR
A UVM TESTBENCH
DRVR
start_item
start_item
seq
blocked
get_next_item
ARB
start_item
return
driver
blocked
finish_item
sync
II.
seq
SQR
seq
blocked
finish_item
return
start_item
return
item_done
Figure 2 - Sequence/Sequencer/Driver
A. Transactions
A transaction is a collection of information which is to be
sent from one point to another. In use with a sequence a
transaction is called a sequence_item. A sequence may create
a transaction, filling in the required information, and then send
B. Sequences
Once a transaction is defined, a sequence of transactions
can be created and sent to the driver and on to the device under
test. In the UVM, the most convenient way to achieve this is
with a uvm_sequence.
A sequence of transactions is created below, specifically, a
sequence of dice rolls.
class roll_sequence extends
uvm_sequence#(dice_item);
`uvm_object_utils(roll_sequence)
rand int how_many;
constraint val { how_many > 70;
how_many < 100; }
task body();
dice_item t;
for(int i = 0; i < how_many; i++) begin
t = dice_item::type_id::create(
$sformatf("t%0d", i));
if (!t.randomize()) begin
`uvm_fatal("SEQ", "Randomize failed")
end
start_item(t);
finish_item(t);
#2;
end
endtask
endclass
task body();
for(int i=0; i<10; i++) begin
trans = trans_c::type_id::create(t);
start_item(trans);
if (!trans.randomize())
`uvm_fatal(Randomize failed ...)
finish_item(trans);
end
endtask
endclass
class driver extends uvm_driver#(trans_c);
`uvm_component_utils(driver)
bit [7:0] mem [256] = '{default:0};
...
task run_phase(uvm_phase phase);
trans_c t;
forever begin
seq_item_port.get_next_item(t);
if (t.rw == 0) // WRITE
mem[t.addr] = t.data;
else
// READ
t.data = mem[t.addr];
#10;
seq_item_port.item_done();
end
endtask
endclass
`uvm_fatal("TEST",
"Randomization failed for seq")
seq.start(e.sqr);
phase.drop_objection(this);
endtask
endclass
...
task body();
bit [7:0] expected_data;
trans t;
if (grabbing) grab();
if (locking) lock();
for (int i=0; i<n; i++) begin
t = trans::type_id::create(
"t",, get_full_name());
start_item(t, qos);
if (!t.randomize() with {
t.rw == 0; // Always do a WRITE first.
!(t.addr inside { addr_used });
} )
`uvm_fatal("seq", "Randomize failed")
finish_item(t);
// Remember this address. Collect stats
addr_used[t.addr] = t.addr;
expected_data = t.data;
t.data = -1;
// Now do a READ.
t.rw = 1;
start_item(t, qos);
`uvm_info("seq", $sformatf("Sending R %s",
t.convert2string()), UVM_MEDIUM)
finish_item(t);
// Now CHECK.
if (t.data != expected_data)
`uvm_error("seq", $sformatf(
"Mismatch: expected %3d, got %3d",
expected_data, t.data))
end
if (locking) unlock();
if (grabbing) ungrab();
endtask
endclass
Arbitration Mode
Definition
Requests
are granted in FIFO
SEQ_ARB_FIFO
order (default)
Requests are granted
SEQ_ARB_WEIGHTED
randomly by weight
Requests
are granted
SEQ_ARB_RANDOM
randomly
Requests at highest priority
SEQ_ARB_STRICT_FIFO
granted in fifo order
Requests
at highest priority
SEQ_ARB_STRICT_RANDOM
granted in randomly
Arbitration is delegated to the
SEQ_ARB_USER
user-defined function
Table 1 - Sequencer Arbitration Modes
III.
data.size() == length; }
constraint val_data {
foreach (data[i]) {
data[i] inside {["a":"z"]};
}
}
endclass
post_randomize();
"; // Four spaces.
0; i < 4; i++) begin
(v%26) + "a";
end
endfunction
endclass
"h" := 72,
"i" := 63,
"j" := 6,
"k" := 6,
"l" := 27,
"m" := 44,
"n" := 24,
"o" := 63,
"p" := 25,
"q" := 2,
"r" := 17,
"s" := 78,
"t" := 167,
"u" := 15,
"v" := 6,
"w" := 68,
"x" := 1,
"y" := 16,
"z" := 01
};
else // Remaining letters of the word
data[i] dist {
"a" := 81,
"b" := 15,
"c" := 28,
"d" := 42,
"e" := 127,
"f" := 22,
"g" := 20,
"h" := 60,
"i" := 70,
"j" := 01,
"k" := 08,
"l" := 40,
"m" := 24,
"n" := 67,
"o" := 75,
"p" := 19,
"q" := 01,
"r" := 60,
"s" := 63,
"t" := 90,
"u" := 28,
"v" := 10,
"w" := 23,
"x" := 02,
"y" := 20,
"z" := 01
};
}
}
...
endclass
D. Hierachy of sequences
Given the two sequences that can generate a word,
short_word_seq and the four_letter_word_seq, new sequences
to generate sentences, paragraphs and chapters can be created
easily as layers of sequences.
int
int
int
bit
how_many;
gap;
max_gap = 5;
use_four_letter_word = 0;
constraint val_how_many {
how_many > 0; how_many < 200;
}
constraint val_max_gap {
max_gap >= 5; max_gap < 10;
}
constraint val_gap {
gap >= 0; gap < max_gap;
}
virtual dut_if vif;
task body();
sequencer p_sequencer;
uvm_sequence_base seq;
$cast(p_sequencer, m_sequencer);
vif = p_sequencer.vif;
for(int i = 0; i < how_many; i++) begin
if (use_four_letter_word)
seq = four_letter_words::type_id::create(
"four_letter_words");
else
seq = short_word_seq::type_id::create(
"short_word_seq");
if (!seq.randomize())
`uvm_fatal("SEQ", "Randomize failed")
seq.start(m_sequencer);
vif.wait_for_clk(gap);
end
endtask
endclass
class paragraph_seq extends
uvm_sequence#(transaction);
`uvm_object_utils(paragraph_seq)
...
task body();
sentence_seq seq;
for(int i = 0; i < how_many; i++) begin
seq = sentence_seq::type_id::create(
"sentence");
if (!seq.randomize())
`uvm_fatal("SEQ", "Randomize failed")
seq.start(m_sequencer);
end
endtask
endclass
class chapter_seq extends
uvm_sequence#(transaction);
`uvm_object_utils(chapter_seq)
...
task body();
paragraph_seq seq;
for(int i = 0; i < how_many; i++) begin
seq = paragraph_seq::type_id::create(
"paragraph");
if (!seq.randomize())
`uvm_fatal("SEQ", "Randomize failed")
seq.start(m_sequencer);
end
endtask
endclass
factory.print();
#### Factory Configuration (*)
#
# Instance Overrides:
#
# Requested Type: packet
# Override Path:
#
uvm_test_top.e1.sequencer.seq1.packet
# Override Type: big_packet
#
# Type Overrides:
#
#
Requested Type Override Type
#
-------------- -----------------------------#
packet
packet_with_randc_addr
#
IV.
SEQUENCES
A. Using Overrides
The packet and big_packet transactions are registered with
the factory and are constructed using the factory. By using the
factory, they enable factory overrides. Factory overrides are
used to replace a class instance with a derived class instance. In
the case of packet and big_packet, a big_packet, derived from
packet can replace an instance of packet.
There are two types of factory overrides; type overrides and
specific instance overrides. A type override means that any
time that type is requested from the factory, the override type
should be returned instead. An instance specific override means
that any time that specific instance name is requested from the
factory, the override type should be returned instead.
The syntax for type overrides in the factory can be read as
when a packet is requested, please substitute a
packet_with_randc_addr instead.
packet::type_id::set_type_override(
packet_with_randc_addr::get_type());
XYZ
Agent
DUT
XYZ
Agent
The task read has an input and an output. The task creates a
sequence, and assigns the address field. It then starts the
sequence and when the sequence returns the task output
argument is assigned from the sequence data field.
task read(input bit [7:0] addr,
output bit [7:0] data);
read_seq seq;
seq = read_seq::type_id::create(
"read",, get_full_name());
seq.addr = addr;
seq.start(seqr);
data = seq.data;
endtask
V.
uvm_cmdline_processor clp;
clp = uvm_cmdline_processor::get_inst();
if (clp.get_arg_values("+FILE=",
filenames) == 0) begin
`uvm_fatal(get_type_name(),
"+FILE=<file> not specified")
end
foreach (filenames[n]) begin
fd = $fopen(filenames[n], "r");
while(!$feof(fd)) begin
ret = $fscanf(fd, "%s %d",
sequence_name, count);
if ($feof(fd))
break;
obj = factory.create_object_by_name(
sequence_name);
if (obj == null) begin
factory.print(1);
`uvm_fatal(get_type_name(),$sformatf(
"factory.create_ (%s) failed",
sequence_name))
end
if (!$cast(seq, obj)) begin
factory.print(1);
`uvm_error(get_type_name(),$sformatf(
"Sequence (%s) is not compatible with %s.",
sequence_name,
simple_value_base_class::type_name))
end
else begin
if (!seq.randomize() with
{seq.how_many == count;})
`uvm_fatal(get_type_name(),
"Randomization failed")
seq.start(m_sequencer);
end
end // while !EOF
end // foreach filenames
endtask
endclass
The code first gets all the file names into a list filenames.
It iterates each filename, using $fopen() to open the file, and
$fscanf to read one line at a time. The parsing is simple a
string followed by an integer. The factory routine
create_object_by_name() is used to create a sequence. Some
error checking is performed and the sequence is randomized
using a with constraint controlling the value of how_many.
Finally, the sequence is started.
Many other kinds of files could be used to drive
verification. For example, for an encryption engine actual files
to be encrypted could be used as input, with the encrypted file
used as the expected check. For a video encoding system, the
input could be video data. The reader is encouraged to explore
using files as a way to connect legacy verification
environments to new UVM based verification environments.
VI.
DPI-C Usage
Examples
desired
Simple import
C
code
is
used
by
SystemVerilog to perform
some
calculation.
This
calculation is not specific to
any instance, and the C code
never calls back into the
SystemVerilog code.
of
functionality
$display("x=%0d", x);
end
endtask
endclass
C code:
int x = 0;
int
c_calc(int *val) {
*val = x++;
return 0;
}
C code:
#include "dpiheader.h"
int x = 0;
int
c_calc(int id, int *val) {
int y;
calling_back(id, &y);
printf("Interface Id%0d -> y=%d", id, y);
*val = x++;
return 0;
}
= new();
Connect
= new();
Connect
= new();
Connect
C Code:
#include "dpiheader.h"
int x = 0;
int
c_calc(int *val) {
int id;
svScope scope;
scope = svGetScope();
tick(10);
// id is a variable stored one per scope.
if ((id=(int)svGetUserData(scope, "id")) == 0) {
intf_calling_back(&id);
if (svPutUserData(scope, "id", id) != 0)
printf("FATAL: svPutUserData() failed.\n");
}
printf("c_calc(%0d, %0d) called. Scope = %s\n",
id, *val, svGetNameFromScope(svGetScope()));
*val = x++;
return 0;
}
get_next_item
item_done
work-list
execute
(item_really_started)
response-list
dispatch responses
(item_really_done)
Figure 4 - Out-of-order request/response handling
A pipelined driver therefore may contain multiple
processes. The first process fills the work-list, and the second
process removes item from the work-list and performs the
actual execution of the transactions.
T1
T2
T3
T4
Figure 5 - Pipelined transaction - in-order completion
C. Out-of-order completion
Out-of-order completion is a natural extension of the
pipelined mode. In the pipelined mode above, transactions are
completed inorder. This allows for parallel operation, but does
not allow maximum throughput when there are operations that
complete in different amounts of time.
Out-of-order completion requires one addition to the
pipelined implementation a tag or id. Each transaction must
be identified by a tag or id, so that requests and responses can
be connected together. Request 1 and 2 are sent to the driver
and on to the hardware. Response 2 comes back first, followed
by response 1. The driver can connect the out-of-order
responses with the requests using the tag or id.
class trans extends my_transaction_base;
`uvm_object_utils(trans)
static int
int
int
bit [7:0]
g_id = 1;
id;
finished_id;
output_value;
endclass
The
run_phase
starts
three
helper
processes
(execute_requests, service_interrupts and dispatch_responses).
It also has its own process, which is a traditional forever loop
that does a get_next_item and an item_done. The
get_next_item receives a transaction and immediately puts it in
a mailbox (tags_to_be_executed_mb).
APPENDIX
class C;
static int g_id;
int id;
function new();
id = g_id++;
dpi_register(this, id);
endfunction
// ===============================================
// File simple-import-only/t.sv
// ===============================================
import "DPI-C" context task c_calc(output int x);
class C;
static int g_id;
int id;
function new();
id = g_id++;
endfunction
task class_calc(output int x);
c_calc(x);
endtask
task go();
int x;
for(int i = 0; i < 5; i++) begin
class_calc(x);
$display("x=%0d", x);
end
endtask
endclass
module top();
initial begin
automatic C c = new(); c.go();
end
initial begin
automatic C c = new(); c.go();
end
initial begin
automatic C c = new(); c.go();
end
endmodule
// ===============================================
// File simple-import-only/t.c
// ===============================================
#include "dpiheader.h"
task go();
int x;
for(int i = 0; i < 5; i++) begin
class_calc(x);
$display("x=%0d", x);
end
endtask
endclass
module top();
initial begin
automatic C c = new(); c.go();
end
initial begin
automatic C c = new(); c.go();
end
initial begin
automatic C c = new(); c.go();
end
endmodule
// ===============================================
// File simple-import-export/t.c
// ===============================================
#include "dpiheader.h"
int x = 0;
int
c_calc(int id, int *val) {
int y;
calling_back(id, &y);
printf("Interface Id%0d -> y=%d", id, y);
int x = 0;
int
c_calc(int *val) {
*val = x++;
return 0;
}
*val = x++;
return 0;
}
3) Bound to an Interface
// ===============================================
// File bound-to-an-interface/t.sv
// ===============================================
typedef class seq;
interface my_interface();
import "DPI-C" context task c_calc(output int x);
export "DPI-C"
task intf_calling_back;
export "DPI-C"
task tick;
seq my_class_handle;
function void init(seq s);
s.vif = interface::self();
my_class_handle = s;
endfunction
task tick(int tics_to_wait = 1);
if (tics_to_wait <= 0)
return;
#(tics_to_wait);
endtask
task intf_calling_back(output int x);
// virtual my_interface vif;
// vif = interface::self();
my_class_handle.class_calling_back(x);
endtask
endinterface
class seq;
static int g_id = 1;
int id;
virtual my_interface vif;
function void init(virtual my_interface l_vif);
id = g_id++;
vif = l_vif;
vif.my_class_handle = this;
endfunction
task class_calc(output int x);
vif.c_calc(x);
endtask
task class_calling_back(output int x);
x = id;
#(10);
$display("SV: @%0t: classid=%0d", $time, id);
endtask
task body();
int x;
for(int i = 0; i < 5; i++) begin
class_calc(x);
$display("SV: @%0t: x=%0d", $time, x);
end
endtask
endclass
module top();
my_interface if0();
my_interface if1();
my_interface if2();
initial begin
automatic seq s =
s.init(if0); /*or
end
initial begin
automatic seq s =
s.init(if1); /*or
end
initial begin
automatic seq s =
s.init(if2); /*or
end
endmodule
new();
if0.init(s);*/ s.body();
new();
if1.init(s);*/ s.body();
new();
if2.init(s);*/ s.body();
// ===============================================
// File bound-to-an-interface/t.c
// ===============================================
#include "dpiheader.h"
int x = 0;
int
c_calc(int *val) {
int id;
svScope scope;
scope = svGetScope();
import uvm_pkg::*;
`include "uvm_macros.svh"
class trans extends uvm_sequence_item;
`uvm_object_utils(trans)
rand bit rw;
rand bit [7:0] addr;
rand bit [7:0] data;
int
qos;
constraint addr_value {
addr > 0;
addr < 100;
addr[1:0] == 0;
}
constraint data_value {
data > 0;
data < 100;
}
function new(string name = "test");
super.new(name);
endfunction
function string convert2string();
return $sformatf("%s(a=%3d, d=%3d) [qos=%0d]",
(rw==1)?" READ":"WRITE",
addr, data, qos);
endfunction
endclass
class read_seq extends uvm_sequence#(trans);
`uvm_object_utils(read_seq)
bit [7:0] addr; // Input
bit [7:0] data; // Output
function new(string name = "test");
super.new(name);
endfunction
task body();
trans t;
t = trans::type_id::create("t",, get_full_name());
t.rw = 1;
t.addr = addr;
start_item(t);
finish_item(t);
data = t.data;
endtask
endclass
class write_seq extends uvm_sequence#(trans);
`uvm_object_utils(write_seq)
bit [7:0] addr; // Input
bit [7:0] data; // Input
function new(string name = "test");
super.new(name);
endfunction
task body();
trans t;
t = trans::type_id::create("t",, get_full_name());
t.rw = 0;
t.addr = addr;
t.data = data;
start_item(t);
finish_item(t);
endtask
endclass
*val = x++;
return 0;
tick(10);
if ((id=(int)svGetUserData(scope, "id")) == 0) {
intf_calling_back(&id);
if ((int)svPutUserData(scope, "id", (int)id) != 0)
printf("FATAL: svPutUserData() failed.\n");
}
int grabbing = 0;
int locking = 0;
int qos = 100; // default is 100 in the UVM
rand int n;
constraint val { n > 10; n < 30; }
bit [7:0] addr_used[bit[7:0]];
`uvm_info("run",
$sformatf("Got %s", t.convert2string()),
UVM_MEDIUM)
task body();
bit [7:0] expected_data;
trans t;
`uvm_info("seq", $sformatf(
"Starting. n=%0d qos=%0d, grab=%0d **********",
n, qos, grabbing), UVM_MEDIUM)
#10;
if (grabbing) grab();
if (locking) lock();
for (int i=0; i<n; i++) begin
t = trans::type_id::create(
"t",, get_full_name());
start_item(t, qos);
if (!t.randomize() with {
t.rw == 0; // Always do a WRITE first.
!(t.addr inside { addr_used });
} )
`uvm_fatal("seq", "Randomize failed")
t.qos = qos;
`uvm_info("seq", $sformatf("Sending W %s",
t.convert2string()), UVM_MEDIUM)
finish_item(t);
// Remember this address.
addr_used[t.addr] = t.addr;
expected_data = t.data;
// Now do a READ.
t.rw = 1;
start_item(t, qos);
`uvm_info("seq", $sformatf("Sending R %s",
t.convert2string()), UVM_MEDIUM)
finish_item(t);
// Now CHECK.
if (t.data != expected_data)
`uvm_error("seq",
$sformatf("Mismatch: expected %3d, got %3d",
expected_data, t.data))
end
if (locking) unlock();
if (grabbing) ungrab();
`uvm_info("seq",
$sformatf("Finished. n=%0d **********", n),
UVM_MEDIUM)
endtask
endclass
class driver extends uvm_driver#(trans);
`uvm_component_utils(driver)
bit [7:0] mem [256] = '{default:0};
function void dump();
$display("=====================================");
for(int addr = 0; addr < 256; addr+=8) begin
$display("%3x: %3x %3x %3x %3x %3x %3x %3x %3x",
addr,
mem[addr],
mem[addr+1],
mem[addr+2], mem[addr+3],
mem[addr+4], mem[addr+5],
mem[addr+6], mem[addr+7]);
end
$display("=====================================");
endfunction
function new(string name = "test",
uvm_component parent = null);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
trans t;
forever begin
seq_item_port.get_next_item(t);
if (t.rw == 0) // WRITE
mem[t.addr] = t.data;
else
// READ
t.data = mem[t.addr];
seq_item_port.item_done();
end
endtask
endclass
class env extends uvm_env;
`uvm_component_utils(env)
driver d;
uvm_sequencer#(trans) seqr;
function new(string name = "test",
uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
d
=driver
::type_id::create("d",this);
seqr=uvm_sequencer#(trans)::type_id::create(
"seqr", this);
seqr.set_arbitration(SEQ_ARB_STRICT_FIFO);
endfunction
function void connect_phase(uvm_phase phase);
d.seq_item_port.connect(seqr.seq_item_export);
endfunction
task read(input bit [7:0] addr, output bit [7:0] data);
read_seq seq;
seq = read_seq::type_id::create(
"read",, get_full_name());
seq.addr = addr;
seq.start(seqr);
data = seq.data;
endtask
task write(input bit [7:0] addr, input bit [7:0] data);
write_seq seq;
seq=write_seq::type_id::create(
"write",, get_full_name());
seq.addr = addr;
seq.data = data;
seq.start(seqr);
endtask
endclass
class test extends uvm_test;
`uvm_component_utils(test)
env e;
function new(string name = "test",
uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
e = env::type_id::create("e", this);
endfunction
task run_phase(uvm_phase phase);
factory.print();
phase.raise_objection(this);
fork
// -----------------------------------------------for (int i=0; i < 3; i++) begin // Normal
seq s1;
s1 = seq::type_id::create("s1",,
get_full_name());
if (!s1.randomize())
`uvm_fatal("seq", "Randomize failed for s1")
s1.start(e.seqr);
end
// -----------------------------------------------for (int i=0; i < 3; i++) begin // QOS == 1000
seq s2;
s2 = seq::type_id::create("s2",,
get_full_name());
s2.qos = 1000;
if (!s2.randomize())
bit
bit
bit[7:0]
bit
bit[7:0]
bit[7:0]
clk,
go,
itag,
interrupt,
otag,
output_value);
int s;
initial
// Creates a stream named /top/dut_i/DUT
s = $create_transaction_stream("DUT", "kind");
task automatic process(bit[7:0]tag);
int tr;
int dut_delay;
int wait_time;
dut_delay = $urandom_range(1000, 200);
// Transaction Begin
tr = $begin_transaction(s, $sformatf("tr%0d", tag));
$add_attribute(tr, tag, "tag");
$add_attribute(tr, $time, "start_time");
$add_attribute(tr, dut_delay, "dut_delay");
// ----------------#dut_delay;
// Calculate the DUT function (+1)
output_value = tag+1;
// Put the calculated value on the interface
otag = tag;
`ifdef UVM_DISABLE_AUTO_ITEM_RECORDING
sequencer = item.get_sequencer();
sequencer.end_tr(item, $time);
`endif
endtask
endclass
// ------------------------------------------
endtask
always @(posedge clk) begin
if (go == 1) begin
fork
automatic bit[7:0] my_tag = itag;
process(my_tag);
join_none
end
end
endmodule
// -----------------------------------------class my_transaction_base extends uvm_sequence_item;
`uvm_object_utils(my_transaction_base)
bit item_really_started_e; //
//
//
//
//
g_id = 1;
id;
finished_id;
output_value;
rand int n;
constraint val { n > 10; n < 100; }
function new(string name = "sequenceN");
super.new(name);
endfunction
task body();
for(int i = 0; i < n; i++) begin
fork
automatic int j = i;
begin
trans t;
t = trans::type_id::create(
$sformatf("t_%0d", i));
t.set_name($sformatf("tr%0d", t.id));
if (!t.randomize()) begin
`uvm_fatal("SEQ1", "Randomize failed")
end
start_item(t);
finish_item(t);
if (t.id+1 != t.output_value)
`uvm_fatal("SEQ", "DUT Failed")
else
`uvm_info("SEQ",
$sformatf("tr%0d matches", t.id),
UVM_MEDIUM)
end
join_none
end
// Don't finish until all the threads are done
wait fork;
endtask
function void do_record(uvm_recorder recorder);
super.do_record(recorder);
$add_attribute(recorder.tr_handle, n, "n");
endfunction
endclass
t.item_really_started();
vif.execute(t.id);
end
endtask
task service_interrupts();
bit [7:0] tag;
bit [7:0] output_value;
trans t;
forever begin
// Wait for an interrupt / response.
vif.handle_interrupt(tag, output_value);
forever begin
trans t;
bit [7:0] tag;
seq_item_port.get_next_item(t);
`uvm_info("DRVR",
{" Got ", t.convert2string()}, UVM_MEDIUM)
tag = t.id; // Truncates 32 bits to 8.
t_outstanding[tag] = t;
seq_item_port.item_done(); // Immediate item_done!
tags_to_be_executed_mb.put(tag);
end
endtask
task execute_requests();
trans t;
bit [7:0] tag;
forever begin
// Get a tag to be executed.
tags_to_be_executed_mb.get(tag);
// What's the transaction handle?
t = t_outstanding[tag];
if (t == null)
`uvm_fatal("execute_requests",
"Null transaction")
if (t.id != tag)
`uvm_fatal("execute_requests", "Tag mismatch")
// Execute.
// The transaction finally has a chance to
//
run on the hardware.
e = env::type_id::create("e", this);
endfunction
task run_phase(uvm_phase phase);
sequenceN seq;
phase.raise_objection(this);
seq = sequenceN::type_id::create("sequenceN");
if (!seq.randomize())
`uvm_fatal("TEST",
"Randomization failed for sequenceN")
seq.start(e.sqr);
phase.drop_objection(this);
endtask
endclass
module top();
reg clk;
dut_if dut_if_i(
.clk(clk));
dut
dut_i(
.clk(clk),
.go(dut_if_i.go),
.itag(dut_if_i.itag),
.interrupt(dut_if_i.interrupt),
.otag(dut_if_i.otag),
.output_value(dut_if_i.output_value)
);
initial begin
// Streams in use:
//
/uvm_root/uvm_test_top/e/sequencer/sequenceN
//
/top/dut_i/DUT
uvm_config_db#(int)
::set(
null, "", "recording_detail", 1);
uvm_config_db#(virtual dut_if)::set(
null, "", "vif",
dut_if_i);
run_test("test");
end
always begin
#10 clk = 1;
#10 clk = 0;
end
endmodule
addr,
payload[0], payload[1], payload[2], payload[3],
payload[4], payload[5], payload[6], payload[7],
checksum
);
endfunction
virtual function void calc_checksum();
checksum = 0;
foreach(payload[i])
checksum = checksum + payload[i];
endfunction
function void do_record(uvm_recorder
$add_attribute(recorder.tr_handle,
$add_attribute(recorder.tr_handle,
$add_attribute(recorder.tr_handle,
"payload");
$add_attribute(recorder.tr_handle,
"checksum");
$add_attribute(recorder.tr_handle,
endfunction
endclass
recorder);
som, "som");
addr, "addr");
payload,
checksum,
eom, "eom");
class randc_addr_c;
randc bit[7:0] addr;
endclass
class packet_with_randc_addr extends packet;
`uvm_object_utils(packet_with_randc_addr)
static randc_addr_c randc_addr;
function new(string name = "packet_with_randc_addr");
super.new(name);
endfunction
function void post_randomize();
if (randc_addr == null)
randc_addr = new();
if (!randc_addr.randomize())
`uvm_fatal("RANDC", "Randomize failed for
randc_addr")
addr = randc_addr.addr;
endfunction
endclass
class big_packet extends packet;
`uvm_object_utils(big_packet)
rand bit[7:0] payload [32]; // Size goes from 8 to 32.
D. Packet Example
import uvm_pkg::*;
`include "uvm_macros.svh"
bit[7:0] io_type;
// New attribute
constraint val {
how_many >= 10; how_many <= 256; }
function new(string name = "n_packets");
super.new(name);
endfunction
task create_called_N_times();
packet t;
for(int i = 0; i < how_many; i+=8) begin
for (int j = 0; j < 8; j++) begin
fork
begin
packet my_t;
my_t = packet::type_id::create(
"packet",,get_full_name());
if (!my_t.randomize() )
`uvm_fatal("SEQ", "Randomize failed")
start_item(my_t, packet_priority);
finish_item(my_t);
end
join_none
end
wait fork;
#5;
end
endtask
task create_called_once();
packet t;
t = packet::type_id::create(
"packet",,get_full_name());
for(int i = 0; i < how_many; i++) begin
if (!t.randomize() )
`uvm_fatal("SEQ", "Randomize failed")
start_item(t, packet_priority);
finish_item(t);
#5;
end
endtask
task body();
create_called_N_times();
// create_called_once();
endtask
function void do_record(uvm_recorder recorder);
super.do_record(recorder);
$add_attribute(recorder.tr_handle,
how_many, "how_many");
endfunction
endclass
// -----------------------------------------class driver extends uvm_driver#(packet);
`uvm_component_utils(driver)
function new(string name = "driver",
uvm_component parent = null);
super.new(name, parent);
endfunction
task run_phase(uvm_phase phase);
forever begin
packet t;
seq_item_port.get_next_item(t);
`uvm_info("DRVR",
{" Got ", t.convert2string()}, UVM_MEDIUM)
foreach (t.payload[i]) // Consume time.
#10;
seq_item_port.item_done();
end
endtask
endclass
packet::type_id::set_type_override(
packet_with_randc_addr::get_type);
packet::type_id::set_inst_override(
big_packet::get_type(),
"uvm_test_top.e1.sequencer.seq1.packet");
seq1=n_packets::type_id::create(
"seq1",,get_full_name());
seq2=n_packets::type_id::create(
"seq2",,get_full_name());
seq1.packet_priority = 1000;
factory.print();
if (!seq1.randomize() with {how_many == 256;})
`uvm_fatal("TEST",
"Randomization failed for seq1")
if (!seq2.randomize() with {how_many == 256;})
`uvm_fatal("TEST",
"Randomization failed for seq2")
fork
seq1.start(e1.sqr);
seq2.start(e1.sqr);
join
phase.drop_objection(this);
endtask
endclass
module top();
initial begin
uvm_config_db#(int)::set(null, "",
"recording_detail", 1);
run_test("test");
end
endmodule