Guys,
I’m implementing a simple protocol based interface UVMA-UVC.
Basically, UVC sends req_length to the DUT and DUT should respond back with res_lengths and UVC needs to compare the request and response.
Lets consider the request and responses as follows.
req_len = 10;
res_len = 3; last = 0;
after few clocks;
req_len = 7;
res_len = 5; last = 0;
after few clocks;
res_len = 2; last = 1;
after few clocks;
req_len = 8;
after few clocks;
res_len = 7; last = 1;
after few clocks;
res_len = 4; last = 0;
after few clocks;
res_len = 4; last = 1;
Here is my implementation for the above UVC…
interface simple_if (input logic clk, rst);
import simple_pkg::simple_seq_item;
import simple_pkg::simple_mon;
import simple_pkg::simple_drv;
logic[31:0] req_length;
logic abort;
logic req_valid;
logic [31:0] res_length;
logic res_valid;
logic last;
simple_drv drv_proxy;
simple_mon mon_proxy;
clocking cb_simple_if @(posedge clk)
input res_length;
input last;
input res_valid;
output rst;
output req_length;
output abort;
output req_valid;
endclocking
task send_request (simple_seq_item item);
simple_seq_item cloned_item;
wait_n_clkcycles(item.delay);
@(posedge clk);
// Drive signals
if (item.abort) begin
abort = item.abort;
req_valid = 1'b0;
end
else begin
req_len = item.req_len;
req_valid = 1'b1;
$cast(cloned_item, item.clone());
drv_proxy.simple_drv_analysis_port.write(cloned_item);
end
endtask
task collect_response (simple_seq_item item);
simple_seq_item mon_item;
int length;
@(posedge clk);
//Update Signals
item.req_valid = req_valid;
item.req_length = req_length;
item.abort = abort;
item.res_valid = res_valid;
item.res_length = res_length;
item.last = last;
if (req_valid || res_valid) begin
if (res_valid) begin
length = res_length + length;
if (last) begin
item.total_res_length = length;
length = 0;
end
end
else begin
length = 0;
end
$cast(mon_item, item.clone());
mon_proxy.mon_analysis_port.write(mon_item);
end
endtask
endinterface
class simple_seq_item extends uvm_sequence_item;
rand int delay;
rand logic[31:0] req_length;
rand logic abort;
logic[31:0] res_length;
logic last;
logic [31:0] total_res_length;
endclass
class simple_drv extends uvm_driver #(simple_seq_item);
..
endclass
task simple_drv::run_phase(uvm_phase phase);
//Set proxy path
simple_if.drv_proxy = this;
forever begin
seq_item_port.get_next_item(simple_seq_item);
simple_if.send_request(simple_seq_item);
seq_item_port.item_done(simple_seq_item);
end
endtask
class simple_mon extends uvm_monitor;
..
endclass
task simple_mon::run_phase(uvm_phase phase);
//Set proxy path
simple_if.mon_proxy = this;
forever begin
simple_if.collect_responses(simple_trans_collected);
end
endtask : run_phase
class simple_cov extends uvm_subscriber #(simple_seq_item);
..
endclass
class simple_sbd extends uvm_scoreboard;
...
simple_seq_item request_queue[$];
function void write_drv (simple_seq_item item);
request_queue.push_back(item);
endfunction: write_drv
function void write_mon (simple_seq_item t);
if (t.abort) begin
simple_trans_mon_sbd = request_queue.pop_front();
end
else begin
if (t.total_res_length) begin
simple_trans_mon_sbd = request_queue.pop_front();
if (t.total_res_length != simple_trans_mon_sbd.request_length) begin
`uvm_error("ScoreBoard:", $sformatf("Length Mismatch ! : Expected: 0x%0x Actual: 0x%0x",simple_trans_mon_sbd.request_length,t.total_res_length))
end
end
end
endfunction: write_mon
endclass
class simple_agent extends uvm_agent;
..
endclass
function void simple_agent::build_phase (uvm_phase phase);
...
simple_cov = simple_cov::type_id::create("simple_cov",this);
simple_mon = simple_mon::type_id::create("simple_mon",this);
simple_drv = simple_drv::type_id::create("simple_drv",this);
simple_seqr = simple_seqr::type_id::create("simple_seqr",this);
endfunction
function void simple_agent::connect_phase (uvm_phase phase);
super.connect_phase(phase);
simple_drv.seq_item_port.connect(simple_seqr.seq_item_export);
simple_mon.simple_mon_analysis_port.connect(simple_cov.analysis_export);
endfunction
class simple_env extends uvm_env;
..
endclass
function void simple_env::build_phase (uvm_phase phase);
super.build_phase(phase);
simple_agent = simple_agent::type_id::create("simple_agent",this);
simple_sbd = simple_sbd::type_id::create("simple_sbd",this);
endfunction
function void simple_env::connect_phase (uvm_phase phase);
super.connect_phase(phase);
simple_agent.simple_drv.simple_drv_analysis_port.connect(simple_sbd.drv_sbd);
simple_agent.simple_mon.simple_mon_analysis_port.connect(simple_sbd.mon_sbd);
endfunction
class simple_request_seq extends uvm_sequence#(simple_seq_item);
..
virtual task body();
simple_seq_item = simple_seq_item::type_id::create("simple_seq_item");
start_item (simple_seq_item);
if (!simple_seq_item.randomize() with {simple_seq_item.abort == 1'b0;})
`uvm_fatal(get_type_name(), "Randomization Failed !")
finish_item(simple_seq_item);
endtask
endclass
class simple_abort_seq extends uvm_sequence#(simple_seq_item);
..
virtual task body();
simple_seq_item = simple_seq_item::type_id::create("simple_seq_item");
start_item (simple_seq_item);
if (!simple_seq_item.randomize() with {simple_seq_item.abort == 1'b1;})
`uvm_fatal(get_type_name(), "Randomization Failed !")
finish_item(simple_seq_item);
endtask
endclass
class simple_test extends uvm_test;
..
task simple_test::run_phase (uvm_phase phase);
..
phase.raise_objection(this);
`uvm_info(get_type_name(), $sformatf(" Test Starting.."), UVM_LOW)
simple_req_seq.start(simple_env.simple_agent.simple_seqr);
simple_abort_seq.start(simple_env.simple_agent.simple_seqr);
simple_req_seq.start(simple_env.simple_agent.simple_seqr);
phase.drop_objection(this);
`uvm_info(get_type_name(), $sformatf(" Test Ending.."), UVM_LOW)
endtask
endclass
Questions:
- Is my implementation right? Both Driver and Monitor sending seq_item to Scoreboard. Is there any better approach?
- Is UVMA compliance? If not, please advise.
Note: this is my first implementation on UVMA*
Thanks
John