• No results found

7 Simple Testbench Integration

7.2 Creating a Simple Testbench

The steps you need to perform to create a simple testbench using reusable UVCs are:

1. Identify the UVCs you will need for your testbench and review their configuration parameters and built-in sequence libraries.

2. Create a testbench class and instantiate reusable UVCs along with other control and checking logic.

3. Configure, create, and hook up the testbench components.

4. Create additional reusable sequences for interface UVCs (optional).

5. Add a virtual sequencer and create virtual sequences (optional).

6. Add checking (scoreboard) and functional coverage extensions.

When your testbench is complete, create tests to exercise the sequence sets and achieve coverage goals.

7.2.1 Instantiating UVCs in a Testbench

Figure 7-2, Simple UVM Testbench for a UART Controller Device, shows a typical verification environment for a UART Controller device.

It also includes the test class containing the uart_ctrl testbench class.

It also includes the test class containing the uart_ctrl testbench class.

Figure 7-2 Simple UVM Testbench for a UART Controller Device

Referring to Figure 7-2, let’s look at how we develop the testbench. The UART Controller design pictured has an APB bus interface and a UART serial interface. Assuming we have developed a UVC for each interface, we instantiate these in a testbench along with a scoreboard and a virtual sequencer. The scoreboard and virtual sequencer will be discussed later in this chapter. The testbench class is derived from the uvm_env class. It instantiates and configures the reusable components for the design being tested. Multiple tests can instantiate this testbench class and determine the nature of traffic to generate and send for the selected configuration.

Example 7–1 UART Controller Testbench

1 class uart_ctrl_tb extends uvm_env;

2 // UVC Components

3 apb_env apb0; // APB UVC 4 uart_env uart0; // UART UVC 5 // Scoreboard

6 uart_ctrl_tx_scbd tx_scbd;

7 uart_ctrl_rx_scbd rx_scbd;

8 // Virtual sequencer

9 uart_ctrl_virtual_sequencer virtual_sequencer;

10 // UVC Configuration Classes 11 apb_config apb_cfg;

12 uart_config uart_cfg;

13 // UVM component automation

14 ‘uvm_component_utils(uart_ctrl_tb) 15 // Constructor - required UVM syntax

16 function new(input string name, input uvm_component parent=null);

17 super.new(name,parent);

18 endfunction

19 // Additional class methods

20 extern virtual function void build();

21 extern virtual function void connect();

22 endclass : uart_ctrl_tb

Line 1: Extend the testbench class from uvm_env.

Lines 3-4: Place instances of the APB and UART reusable UVCs.

Lines 6-7: Place instances of the scoreboards for checking. This design has two scoreboard components.

Line 9: Place an instance of a Virtual Sequencer.

Lines 11-12: Instances of the APB and UART config classes. These can also optionally be combined into a uart_ctrl_config class, which contains instances of these two classes.

Lines 20-22: Declarations of additional testbench class methods (UVM phases). The testbench configuration and creation are performed in the build() method. The connect() method is used to make connections between the UVC monitors and the scoreboard. The connect() method is also where the virtual interface connections are made.

Use the UVM build() method to create and configure the testbench components. First the UVC configuration classes are created (and optionally randomized). Then the configuration mechanism is used to set the configuration object for each UVC before it is created. This is shown in the build() code below:

1 function void uart_ctrl_tb::build();

2 super.build();

3 // Create the UVC configurations if they have not already been set 4 if (apb_cfg == null) begin

5 apb_cfg = apb_config::type_id::create("apb_cfg", this);

6 apb_cfg.add_master("master", UVM_ACTIVE);

7 apb_cfg.add_slave("slave0", 32'h000000, 32'h81FFFF, 0, UVM_PASSIVE);

8 end

9 if (uart_cfg == null)

10 uart_cfg= uart_config::type_id::create('uart_cfg',this);

11 set_config_object("apb0", "cfg", apb_cfg);

12 set_config_object("uart0", "cfg", uart_cfg);

13 apb0 = apb_env::type_id::create("apb0",this);

14 uart0 = uart_env::type_id::create("uart0",this);

15 virtual_sequencer =

uart_ctrl_virtual_sequencer::type_id::create("virtual_sequencer",this);

16 tx_scbd = uart_ctrl_tx_scbd::type_id::create("tx_scbd", this);

17 rx_scbd = uart_ctrl_rx_scbd::type_id::create("rx_scbd", this);

18 endfunction : build

There are three things to remember when implementing the build() method:

1. Call super.build() first to update the configuration fields of the testbench.

2. Use create() instead of new() to allocate objects so you can override sub-components with derivative versions using the UVM factory.

3. We recommended that you set configuration parameters before you create the components. This allows the component to fetch configuration values in their constructors (for example, to control coverage group instantiation).

Now use the UVM connect() method to hook up the testbench components after they have been built. The connect phase includes connecting the UVC TLM monitor ports to the scoreboard, connecting the virtual sequencer to the UVC sequencers and setting the virtual interface connections. This is shown in the connect() code below:

connections. This is shown in the connect() code below:

1 function void uart_ctrl_tb::connect();

2 super.connect();

3 // Connect TLM ports from the monitors to the scoreboards

4 uart0.Rx.monitor.frame_collected_port.connect(rx_scbd.uart_match);

5 apb0.bus_monitor.item_collected_port.connect(rx_scbd.apb_add);

6 uart0.Tx.monitor.frame_collected_port.connect(tx_scbd.uart_add);

7 apb0.bus_monitor.item_collected_port.connect(tx_scbd.apb_match);

8 // Hook up virtual sequencer to interface sequencers 9 virtual_sequencer.apb_seqr = apb0.master.sequencer;

10 if (uart0.Tx.is_active == UVM_ACTIVE)

11 virtual_sequencer.uart_seqr = uart0.Tx.sequencer;

12 // Set the virtual interfaces

13 apb0.assign_vi(uart_tb_top.apb_if0);

14 uart0.assign_vi(uart_tb_top.uart_if0);

15 endfunction : connect

When the testbench infrastructure is captured in a testbench class, many test classes may instantiate the testbench class above, so test writers do not need to understand all the details of how it is created and configured.