|HOME |ABOUT |ARTICLES |ACK |FEEDBACK |TOC |LINKS |BLOG |JOBS |


Tutorials



PHASE 6 DRIVER


Driver



In this phase we will develop the driver. Driver is defined by extending ovm_driver. Driver takes the transaction from the sequencer using seq_item_port. This transaction will be driven to DUT as per the interface specification. After driving the transaction to DUT, it sends the transaction to scoreboard using ovm_analysis_port.

In driver class, we will also define task for resetting DUT and configuring the DUT. After completing the driver class implementation, we will instantiate it in environment class and connect the sequencer to it. We will also update the test case and run the simulation to check the implementation which we did till now.






1) Define the driver class by extending ovm_driver;


`ifndef GUARD_DRIVER
`define GUARD_DRIVER

class Driver extends ovm_driver #(Packet);

endclass : Driver


2) Create a handle to configuration object. Using this object we can get DUT interfaces and DUT port addresses.


Configuration cfg;


3) Declare input and memory interfaces


virtual input_interface.IP input_intf;
virtual mem_interface.MEM mem_intf;


4) Declare ovm_analysis_port which is used to send packets to scoreboard.


ovm_analysis_port #(Packet) Drvr2Sb_port;


5) Declare component utilities macro.


`ovm_component_utils(Driver)


6) Define the constructor method. Pass the parent object to super class.


function new( string name = "" , ovm_component parent = null) ;
super.new( name , parent );
endfunction : new


7) In the build method and construct Drvr2Sb_port object.


function void build();
super.build();
Drvr2Sb_port = new("Drvr2Sb_port", this);
endfunction : build


8) In the end_of_elaboration() method, get the configuration object using get_config_object and update the virtual interfaces.


function void end_of_elaboration();
ovm_object tmp;
super.end_of_elaboration();
assert(get_config_object("Configuration",tmp));
$cast(cfg,tmp);
this.input_intf = cfg.input_intf;
this.mem_intf = cfg.mem_intf;
endfunction : end_of_elaboration



9) Define the reset_dut() method which will be used for resetting the DUT.


virtual task reset_dut();
ovm_report_info(get_full_name(),"Start of reset_dut() method ",OVM_LOW);
mem_intf.mem_data <= 0;
mem_intf.mem_add <= 0;
mem_intf.mem_en <= 0;
mem_intf.mem_rd_wr <= 0;
input_intf.data_in <= 0;
input_intf.data_status <= 0;

input_intf.reset <= 1;
repeat (4) @ input_intf.clock;
input_intf.reset <= 0;

ovm_report_info(get_full_name(),"End of reset_dut() method ",OVM_LOW);
endtask : reset_dut



10) Define the cfg_dut() method which does the configuration due port address.


virtual task cfg_dut();
ovm_report_info(get_full_name(),"Start of cfg_dut() method ",OVM_LOW);
mem_intf.mem_en <= 1;
@(posedge mem_intf.clock);
mem_intf.mem_rd_wr <= 1;

@(posedge mem_intf.clock);
mem_intf.mem_add <= 8'h0;
mem_intf.mem_data <= cfg.device0_add;
ovm_report_info(get_full_name(),
$psprintf(" Port 0 Address %h ",cfg.device0_add),OVM_LOW);

@(posedge mem_intf.clock);
mem_intf.mem_add <= 8'h1;
mem_intf.mem_data <= cfg.device1_add;
ovm_report_info(get_full_name(),
$psprintf(" Port 1 Address %h ",cfg.device1_add),OVM_LOW);

@(posedge mem_intf.clock);
mem_intf.mem_add <= 8'h2;
mem_intf.mem_data <= cfg.device2_add;
ovm_report_info(get_full_name(),
$psprintf(" Port 2 Address %h ",cfg.device2_add),OVM_LOW);

@(posedge mem_intf.clock);
mem_intf.mem_add <= 8'h3;
mem_intf.mem_data <= cfg.device3_add;
ovm_report_info(get_full_name(),
$psprintf(" Port 3 Address %h ",cfg.device3_add),OVM_LOW);

@(posedge mem_intf.clock);
mem_intf.mem_en <=0;
mem_intf.mem_rd_wr <= 0;
mem_intf.mem_add <= 0;
mem_intf.mem_data <= 0;

ovm_report_info(get_full_name(),"End of cfg_dut() method ",OVM_LOW);
endtask : cfg_dut


11) Define drive() method which will be used to drive the packet to DUT. In this method pack the packet fields using the pack_bytes() method of the transaction and drive the packed data to DUT interface.


task drive(Packet pkt);
byte unsigned bytes[];
int pkt_len;
pkt_len = pkt.pack_bytes(bytes);
ovm_report_info(get_full_name(),"Driving packet ...",OVM_LOW);

foreach(bytes[i])
begin
@(posedge input_intf.clock);
input_intf.data_status <= 1 ;
input_intf.data_in <= bytes[i];
end

@(posedge input_intf.clock);
input_intf.data_status <= 0 ;
input_intf.data_in <= 0;
repeat(2) @(posedge input_intf.clock);
endtask : drive


12) Now we will use the above 3 defined methods and update the run() method of ovm_driver.
First call the reset_dut() method and then cfg_dut(). After completing the configuration, in a forever loop get the transaction from seq_item_port and send it DUT using drive() method and also to scoreboard using Drvr2SB_port .


task run();
Packet pkt;
@(posedge input_intf.clock);
reset_dut();
cfg_dut();
forever begin
seq_item_port.get_next_item(pkt);
Drvr2Sb_port.write(pkt);
@(posedge input_intf.clock);
drive(pkt);
@(posedge input_intf.clock);
seq_item_port.item_done();
end
endtask : run


(S) Driver class source code

`ifndef GUARD_DRIVER
`define GUARD_DRIVER

class Driver extends ovm_driver #(Packet);

Configuration cfg;

virtual input_interface.IP input_intf;
virtual mem_interface.MEM mem_intf;

ovm_analysis_port #(Packet) Drvr2Sb_port;

`ovm_component_utils(Driver)

function new( string name = "" , ovm_component parent = null) ;
super.new( name , parent );
endfunction : new

function void build();
super.build();
Drvr2Sb_port = new("Drvr2Sb", this);
endfunction : build

function void end_of_elaboration();
ovm_object tmp;
super.end_of_elaboration();
assert(get_config_object("Configuration",tmp));
$cast(cfg,tmp);
this.input_intf = cfg.input_intf;
this.mem_intf = cfg.mem_intf;
endfunction : end_of_elaboration

task run();
Packet pkt;
@(posedge input_intf.clock);
reset_dut();
cfg_dut();
forever begin
seq_item_port.get_next_item(pkt);
Drvr2Sb_port.write(pkt);
@(posedge input_intf.clock);
drive(pkt);
@(posedge input_intf.clock);
seq_item_port.item_done();
end
endtask : run

virtual task reset_dut();
ovm_report_info(get_full_name(),"Start of reset_dut() method ",OVM_LOW);
mem_intf.mem_data <= 0;
mem_intf.mem_add <= 0;
mem_intf.mem_en <= 0;
mem_intf.mem_rd_wr <= 0;
input_intf.data_in <= 0;
input_intf.data_status <= 0;

input_intf.reset <= 1;
repeat (4) @ input_intf.clock;
input_intf.reset <= 0;

ovm_report_info(get_full_name(),"End of reset_dut() method ",OVM_LOW);
endtask : reset_dut

virtual task cfg_dut();
ovm_report_info(get_full_name(),"Start of cfg_dut() method ",OVM_LOW);
mem_intf.mem_en <= 1;
@(posedge mem_intf.clock);
mem_intf.mem_rd_wr <= 1;

@(posedge mem_intf.clock);
mem_intf.mem_add <= 8'h0;
mem_intf.mem_data <= cfg.device0_add;
ovm_report_info(get_full_name(),
$psprintf(" Port 0 Address %h ",cfg.device0_add),OVM_LOW);

@(posedge mem_intf.clock);
mem_intf.mem_add <= 8'h1;
mem_intf.mem_data <= cfg.device1_add;
ovm_report_info(get_full_name(),
$psprintf(" Port 1 Address %h ",cfg.device1_add),OVM_LOW);

@(posedge mem_intf.clock);
mem_intf.mem_add <= 8'h2;
mem_intf.mem_data <= cfg.device2_add;
ovm_report_info(get_full_name(),
$psprintf(" Port 2 Address %h ",cfg.device2_add),OVM_LOW);

@(posedge mem_intf.clock);
mem_intf.mem_add <= 8'h3;
mem_intf.mem_data <= cfg.device3_add;
ovm_report_info(get_full_name(),
$psprintf(" Port 3 Address %h ",cfg.device3_add),OVM_LOW);

@(posedge mem_intf.clock);
mem_intf.mem_en <=0;
mem_intf.mem_rd_wr <= 0;
mem_intf.mem_add <= 0;
mem_intf.mem_data <= 0;

ovm_report_info(get_full_name(),"End of cfg_dut() method ",OVM_LOW);
endtask : cfg_dut

task drive(Packet pkt);
byte unsigned bytes[];
int pkt_len;
pkt_len = pkt.pack_bytes(bytes);
ovm_report_info(get_full_name(),"Driving packet ...",OVM_LOW);

foreach(bytes[i])
begin
@(posedge input_intf.clock);
input_intf.data_status <= 1 ;
input_intf.data_in <= bytes[i];
end

@(posedge input_intf.clock);
input_intf.data_status <= 0 ;
input_intf.data_in <= 0;
repeat(2) @(posedge input_intf.clock);
endtask : drive

endclass : Driver

`endif

Environment Updates



We will take the instance of Sequencer and Driver and connect them in Environment class.






1) Declare handles to Driver and Sequencer.


Sequencer Seqncr;
Driver Drvr;


2) In build method, construct Seqncr and Drvr object using create() method.


Drvr = Driver::type_id::create("Drvr",this);
Seqncr = Sequencer::type_id::create("Seqncr",this);


2) In connect() method connect the sequencer seq_item_port to drivers seq_item_export.


Drvr.seq_item_port.connect(Seqncr.seq_item_export);

(S)Environment class code

`ifndef GUARD_ENV
`define GUARD_ENV

class Environment extends ovm_env;

`ovm_component_utils(Environment)

Sequencer Seqncr;
Driver Drvr;

function new(string name , ovm_component parent = null);
super.new(name, parent);
endfunction: new

function void build();
super.build();
ovm_report_info(get_full_name(),"START of build ",OVM_LOW);

Drvr = Driver::type_id::create("Drvr",this);
Seqncr = Sequencer::type_id::create("Seqncr",this);

ovm_report_info(get_full_name(),"END of build ",OVM_LOW);
endfunction

function void connect();
super.connect();
ovm_report_info(get_full_name(),"START of connect ",OVM_LOW);

Drvr.seq_item_port.connect(Seqncr.seq_item_export);

ovm_report_info(get_full_name(),"END of connect ",OVM_LOW);
endfunction


endclass : Environment

`endif

Testcase Updates



We will update the testcase and run the simulation.

1)In the build() method, update the configuration address in the configuration object which in top module.


function void build();
super.build();

cfg.device0_add = 0;
cfg.device1_add = 1;
cfg.device2_add = 2;
cfg.device3_add = 3;


2) In the build() method itself, using set_config_object , configure the configuration object with the one which is in top module.
with this, the configuration object in Sequencer and Driver will be pointing to the one which in top module.


set_config_object("t_env.*","Configuration",cfg);


3) In the build method, using set_config_string, configure the default_sequence of the sequencer to use the sequence which we defined.


set_config_string("*.Seqncr", "default_sequence", "Seq_device0_and_device1");


4) Set the sequencer count value to 2 .


set_config_int("*.Seqncr", "count",2);


5) Update the run() method to print the Sequencer details.


t_env.Seqncr.print();

(S)Testcase code

class test1 extends ovm_test;

`ovm_component_utils(test1)

Environment t_env ;

function new (string name="test1", ovm_component parent=null);
super.new (name, parent);
t_env = new("t_env",this);
endfunction : new

function void build();
super.build();

cfg.device0_add = 0;
cfg.device1_add = 1;
cfg.device2_add = 2;
cfg.device3_add = 3;

set_config_object("t_env.*","Configuration",cfg);
set_config_string("*.Seqncr", "default_sequence", "Seq_device0_and_device1");
set_config_int("*.Seqncr", "count",2);

endfunction

task run ();

t_env.Seqncr.print();

#1000;
global_stop_request();
endtask : run

endclass : test1


(S)Download the source code


ovm_switch_6.tar
Browse the code in ovm_switch_6.tar


(S)Command to run the simulation


vcs -sverilog +incdir+$OVM_HOME/src $OVM_HOME/src/ovm_pkg.sv +incdir+. rtl.sv interface.sv top.sv -R +OVM_TESTNAME=test1
qverilog +incdir+$OVM_HOME/src $OVM_HOME/src/ovm_pkg.sv +incdir+. rtl.sv interface.sv top.sv -R +OVM_TESTNAME=test1



(S)Log report after simulation

OVM_INFO @ 0 [RNTST] Running test test1...
OVM_INFO @ 0: ovm_test_top.t_env [ovm_test_top.t_env] START of build
OVM_INFO @ 0: ovm_test_top.t_env [ovm_test_top.t_env] END of build
OVM_INFO @ 0: ovm_test_top.t_env [ovm_test_top.t_env] START of connect
OVM_INFO @ 0: ovm_test_top.t_env [ovm_test_top.t_env] END of connect
----------------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------------
Seqncr Sequencer - Seqncr@14
--rsp_export ovm_analysis_export - rsp_export@16
--seq_item_export ovm_seq_item_pull_+ - seq_item_export@40
--default_sequence string 19 ovm_random_sequence
--count integral 32 -1
--max_random_count integral 32 'd10
--sequences array 5 -
----[0] string 19 ovm_random_sequence
----[1] string 23 ovm_exhaustive_sequ+
----[2] string 19 ovm_simple_sequence
----[3] string 23 Seq_device0_and_dev+
----[4] string 19 Seq_constant_length
--max_random_depth integral 32 'd4
--num_last_reqs integral 32 'd1
--num_last_rsps integral 32 'd1
----------------------------------------------------------------------
OVM_INFO @ 30: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
Start of reset_dut() method
OVM_INFO @ 70: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
End of reset_dut() method
OVM_INFO @ 70: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
Start of cfg_dut() method
OVM_INFO @ 110: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
Port 0 Address 00
OVM_INFO @ 130: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
Port 1 Address 01
OVM_INFO @ 150: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
Port 2 Address 02
OVM_INFO @ 170: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
Port 3 Address 03
OVM_INFO @ 190: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
End of cfg_dut() method
OVM_INFO @ 210: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
Driving packet ...
OVM_INFO @ 590: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
Driving packet ...
OVM_INFO @ 970: ovm_test_top.t_env.Drvr [ovm_test_top.t_env.Drvr]
Driving packet ...

--- OVM Report Summary ---

** Report counts by severity
OVM_INFO : 16
OVM_WARNING : 0
OVM_ERROR : 0
OVM_FATAL : 0

Index
Introduction
Specification
Verification Plan
Phase 1 Top
Phase 2 Configuration
Phase 3 Environment N Testcase
Phase 4 Packet
Phase 5 Sequencer N Sequence
Phase 6 Driver
Phase 7 Receiver
Phase 8 Scoreboard

Report a Bug or Comment on This section - Your input is what keeps Testbench.in improving with time!





<< PREVIOUS PAGE

TOP

NEXT PAGE >>

copyright 2007-2017 :: all rights reserved www.testbench.in::Disclaimer