gvarshne
04-13-2009, 12:04 AM
This is probably not a OVM specific issue. However since we are seeing this with OVM code, posting here first.
Code shown below is an simplified version of how our actual code looks like. Protocol timing is identical to a " non-pipelined AHB NSEQ single transfer with 1 clock wait state inserted by slave".
This simplified code (along with tb_top, env, etc - not shown here) runs fine w/o any issues with correct expected protocol timing.
However with our actual code, sometimes we see that 'synchronization event' at 'POINT C' (shown in driver code listed below) gets missed i.e. driven interface signal 'trans_type' does not change to "ACTIVE" for 1 clock. Rather it stays IDLE. If we add a synchronization event between 'POINT A' and 'POINT B' mentioned below, code behaves correctly except that now we have extra clock between two transfers :(
Is there anything wrong in the way sequence, interface and driver is coded which could be CAUSING this potential race condition? :confused:
Has anybody else seen similar behavior with Questa?
Just wanted to check with larger community base before we raise the issue with tool vendor.
//
// NOTE: class declarations are listed out of order to list problem code first.
//
//======================SEQUENCE==================== ===
class basic_seq extends ovm_sequence #(foo_xfer);
foo_xfer temp_xfer;
`ovm_sequence_utils(basic_seq, foo_seqncr)
function new(string name="basic_seq");
super.new(name);
endfunction
virtual task body();
`ovm_do_with(temp_xfer, {trans_type == ACTIVE;} )
p_sequencer.if0.wait_cycles(1);
`ovm_do_with(temp_xfer, {trans_type == ACTIVE;} )
p_sequencer.if0.wait_cycles(2);
`ovm_do_with(temp_xfer, {trans_type == ACTIVE;} )
p_sequencer.if0.wait_cycles(3);
`ovm_do_with(temp_xfer, {trans_type == ACTIVE;} )
p_sequencer.if0.wait_cycles(4);
#1000;
global_stop_request();
endtask // body
endclass
//===================INTERFACE====================== ========
interface foo_if (input clk);
logic trans_type;
logic direction;
logic ready;
clocking drv_cb @(posedge clk);
output trans_type;
output direction;
endclocking
clocking mon_cb @(posedge clk);
default input #1step;
input trans_type;
input direction;
input ready;
endclocking
task wait_cycles(input int unsigned num_cycles);
repeat (num_cycles)
@(mon_cb);
endtask
endinterface : foo_if
//===================DRIVER========================= ========
class foo_driver extends ovm_driver #(foo_xfer);
virtual foo_if if0;
`ovm_component_utils(foo_driver)
function new (string name, ovm_component parent);
super.new(name, parent);
endfunction : new
virtual task run();
ovm_sequence_item item;
foo_xfer this_xfer;
@(if0.drv_cb);
forever begin
seq_item_port.get_next_item(item);
$cast(this_xfer, item);
this_xfer.print();
do_xfer(this_xfer);
this_xfer.print();
seq_item_port.item_done();
end
endtask : run
virtual protected task do_xfer (foo_xfer fx);
// POINT A:
// POINT B:
if0.drv_cb.trans_type <= fx.trans_type; // xfer.trans_type == ACTIVE here
// POINT C:
@(if0.mon_cb);
// POINT D:
if0.drv_cb.trans_type <= IDLE;
if0.drv_cb.direction <= fx.direction;
@(if0.mon_cb);
while(if0.mon_cb.ready == 1'b0)
@(if0.mon_cb);
endtask // do_xfer
endclass : foo_driver
//===================SEQUENCER====================== ========
class foo_seqncr extends ovm_sequencer #(foo_xfer);
virtual foo_if if0;
`ovm_sequencer_utils(foo_seqncr)
function new (string name, ovm_component parent);
super.new(name, parent);
`ovm_update_sequence_lib_and_item(foo_xfer)
endfunction : new
endclass : foo_seqncr
//===================TRANSACTION==================== ========
typedef enum {IDLE, ACTIVE} foo_trans_t;
typedef enum {READ, WRITE} foo_rw_t;
class foo_xfer extends ovm_sequence_item;
rand foo_trans_t trans_type;
rand foo_rw_t direction;
`ovm_object_utils_begin(foo_xfer)
`ovm_field_enum(foo_trans_t, trans_type, OVM_ALL_ON)
`ovm_field_enum(foo_rw_t, direction, OVM_ALL_ON)
`ovm_object_utils_end
function new(string name="foo_xfer");
super.new(name);
endfunction : new
endclass // foo_xfer
Code shown below is an simplified version of how our actual code looks like. Protocol timing is identical to a " non-pipelined AHB NSEQ single transfer with 1 clock wait state inserted by slave".
This simplified code (along with tb_top, env, etc - not shown here) runs fine w/o any issues with correct expected protocol timing.
However with our actual code, sometimes we see that 'synchronization event' at 'POINT C' (shown in driver code listed below) gets missed i.e. driven interface signal 'trans_type' does not change to "ACTIVE" for 1 clock. Rather it stays IDLE. If we add a synchronization event between 'POINT A' and 'POINT B' mentioned below, code behaves correctly except that now we have extra clock between two transfers :(
Is there anything wrong in the way sequence, interface and driver is coded which could be CAUSING this potential race condition? :confused:
Has anybody else seen similar behavior with Questa?
Just wanted to check with larger community base before we raise the issue with tool vendor.
//
// NOTE: class declarations are listed out of order to list problem code first.
//
//======================SEQUENCE==================== ===
class basic_seq extends ovm_sequence #(foo_xfer);
foo_xfer temp_xfer;
`ovm_sequence_utils(basic_seq, foo_seqncr)
function new(string name="basic_seq");
super.new(name);
endfunction
virtual task body();
`ovm_do_with(temp_xfer, {trans_type == ACTIVE;} )
p_sequencer.if0.wait_cycles(1);
`ovm_do_with(temp_xfer, {trans_type == ACTIVE;} )
p_sequencer.if0.wait_cycles(2);
`ovm_do_with(temp_xfer, {trans_type == ACTIVE;} )
p_sequencer.if0.wait_cycles(3);
`ovm_do_with(temp_xfer, {trans_type == ACTIVE;} )
p_sequencer.if0.wait_cycles(4);
#1000;
global_stop_request();
endtask // body
endclass
//===================INTERFACE====================== ========
interface foo_if (input clk);
logic trans_type;
logic direction;
logic ready;
clocking drv_cb @(posedge clk);
output trans_type;
output direction;
endclocking
clocking mon_cb @(posedge clk);
default input #1step;
input trans_type;
input direction;
input ready;
endclocking
task wait_cycles(input int unsigned num_cycles);
repeat (num_cycles)
@(mon_cb);
endtask
endinterface : foo_if
//===================DRIVER========================= ========
class foo_driver extends ovm_driver #(foo_xfer);
virtual foo_if if0;
`ovm_component_utils(foo_driver)
function new (string name, ovm_component parent);
super.new(name, parent);
endfunction : new
virtual task run();
ovm_sequence_item item;
foo_xfer this_xfer;
@(if0.drv_cb);
forever begin
seq_item_port.get_next_item(item);
$cast(this_xfer, item);
this_xfer.print();
do_xfer(this_xfer);
this_xfer.print();
seq_item_port.item_done();
end
endtask : run
virtual protected task do_xfer (foo_xfer fx);
// POINT A:
// POINT B:
if0.drv_cb.trans_type <= fx.trans_type; // xfer.trans_type == ACTIVE here
// POINT C:
@(if0.mon_cb);
// POINT D:
if0.drv_cb.trans_type <= IDLE;
if0.drv_cb.direction <= fx.direction;
@(if0.mon_cb);
while(if0.mon_cb.ready == 1'b0)
@(if0.mon_cb);
endtask // do_xfer
endclass : foo_driver
//===================SEQUENCER====================== ========
class foo_seqncr extends ovm_sequencer #(foo_xfer);
virtual foo_if if0;
`ovm_sequencer_utils(foo_seqncr)
function new (string name, ovm_component parent);
super.new(name, parent);
`ovm_update_sequence_lib_and_item(foo_xfer)
endfunction : new
endclass : foo_seqncr
//===================TRANSACTION==================== ========
typedef enum {IDLE, ACTIVE} foo_trans_t;
typedef enum {READ, WRITE} foo_rw_t;
class foo_xfer extends ovm_sequence_item;
rand foo_trans_t trans_type;
rand foo_rw_t direction;
`ovm_object_utils_begin(foo_xfer)
`ovm_field_enum(foo_trans_t, trans_type, OVM_ALL_ON)
`ovm_field_enum(foo_rw_t, direction, OVM_ALL_ON)
`ovm_object_utils_end
function new(string name="foo_xfer");
super.new(name);
endfunction : new
endclass // foo_xfer