1.下图是基本测试平台的结构
2.verification harness
Instead of a monolithic block, the testbenches should be designed with a layer of physical-level busfunctional models. This physical-level layer, common to all testbenches for the design under verification, is called the verification harness. The test functions required to implement the testcases identified in the verification plan are built on top of the verification harness, as illustrated in Figure 6-3. The test function and the harness together form a testbench.
2.1 Encapsulate the verification harness
The design under verification, the clock generators and the top-level signals and interfaces are instantiated in a top-level module. The bus-functional model instances and their connectivity to the interface instances are encapsulated in a program or a class eventually instantiated in a program. The various test functions then instantiate the encapsulated verification harness.Figure6-4 depicts the structure of the verification harness for the ATM switch node and the location of the components in the top-level module or encapsulating program or class.
//Top-level module for ATM switch node
module top;
utopia_L1_if tx_0(), tx_1(), tx_2(), tx_3();
utopia_L1_if rx_0(), rx_1(), rx_2(), rx_3();
utopia_mgmt_if mgmt();
...
reg reset = 0;
reg clk = 0;
switch_node dut(tx_0.clk, ..., clk, reset);
always #5 clk = ~clk;
endmodule: top
Bus-functional models in top- level program or class
The bus-functional models are instantiated in a top-level program or class that will eventually be instanted in a program. This ensures that the testbench executes as program threads and can reliably react to events and assertions in the design. Sample 6-4 shows the bus-functional models instantiated in a program whereas Sample 6-5 shows them instantiated in a class. Both look very similar but I prefer to use a class as it has a few advantages over a program.
//Sample 6-4. Top-level program for ATM switch node
program harness;
utopia_L1_bfm atm0 = new(top.tx_0, rx_0);
utopia_L1_bfm atm1 = new(top.tx_1, rx_1);
utopia_L1_bfm atm2 = new(top.tx_2, rx_2);
utopia_L1_bfm atm3 = new(top.tx_3, rx_3);
utopia_mgmt_bfm cpu = new(top.mgmt);
task reset;
top.rst <= 1;
repeat (3) @(negedge top.clk);
top.rst <= 0;
endtask
endprogram
//Sample 6-5. Top-level class for ATM switch node
class harness;
utopia_L1_bfm atm0 = new(top.tx_0, rx_0);
utopia_L1_bfm atm1 = new(top.tx_1, rx_1);
utopia_L1_bfm atm2 = new(top.tx_2, rx_2);
utopia_L1_bfm atm3 = new(top.tx_3, rx_3);
utopia_mgmt_bfm cpu = new(top.mgmt);
task reset;
top.rst <= 1;
repeat (3) @(negedge top.clk);
top.rst <= 0;
endtask
endclass: harness
Test functions use transaction interfaces in the verification harness
A complete verification harness provides a transaction-level abstraction of the design to be verified. It provides a foundation on which the data generation mechanism, the self-checking structure and the functional coverage measurements are built. Test functions are implemented by using the transaction-level interface elements
in the bus-functional models and verification harness itself. These interface elements are accessed using hierarchical references in a single instance of the verification harness. Sample 6-6 shows a partial test function that configures the device then injects an ATM cell in one of the ports.
//Sample 6-6. Test function using a verification harness
program my_test;
harness th = new;
initial
begin
atm_cell cell;
th.cpu.write(16’h0001, 16’h0010);
...
th.atm0.send(cell);
...
$finish;
end
endprogram: my_test
3. design configuration
it is good practice to collect all device configuration information under a single descriptor.
4. self checking testbench
5.directed stimulus
//Sample 6-22. Random-filled directed instruction sequence stimulus
instr.randomize() with {
opcode == CMP;
};
...
repeat (3) begin
instr.randomize() with {
opcode == NOP;
};
end
...
instr.randomize() with {
opcode == BLT;
};
...
6.random stimulus
6.1产生随机数据流很简单
generators are output-only bus-functional models. Their output is a transaction-level interface, the stream of generated random transactions.
//Sample 6-24. Simple random generator
class atm_gen;
...
function new();
...
fork
this.main();
join_none
endfunction: new
task main();
atm_cell cell;
repeat (100) begin
cell = new;
if (!cell.randomize()) ...
...
end
endtask: main
endclass: atm_gen
6.2 定义中断机制
The simple random generator in Sample 6-24 will always generate 100 cells then terminate. This number is completely arbitrary and is unlikely to satisfy the needs of all testcases. During initial design debug stage, generating just a single data item is often required. A random testbench must run for much longer to increase the likelihood that functional coverage points will be hit. Generators should have several termination mechanisms that can be armed at the start of the simulation (such as the number of objects to generate) or externally triggered by the testbench. Sample 6-25 shows a generator that, by default, will generate an infinite number of objects. It will also not start immediately, leaving time for the testbench to configure the device before starting to generate stimulus. The generator also can be suspended at anytime by calling the stop() method. Sample 6-26 shows how a debug testcase can configure the generator to generate a single cell on a randomly selected port and no cells on the others.
//Sample 6-25. Random generator with termination mechanisms
class atm_gen;
...
int stop_after = -1;
function new();
...
endfunction: new
function void start();
fork
this.main();
join_none
endfunction: start
function void stop();
this.stop_after = 0;
endfunction: stop
virtual task main();
atm_cell cell;
while (this.stop_after != 0) begin
cell = new;
if (!cell.randomize()) ...
...
if (this.stop_after > 0) begin
this.stop_after--;
end
end
endtask: main
endclass: atm_gen
6.3 adding constraints
The simplest mechanism for adding constaints is to add them to the class being randomized.The problem with this approach is that the constraint will apply to every
instance of the object in all future simulations and models. If the objective was to inject a specific condition for a specific testcase or simulation, this approach will not work.
可以打开和关闭constraint:
class atm_cell;
...
constraint vpi_is_0 {
vpi == 0;
}
function new();
this.vpi_is_0.constraint_mode(0);
endfunction: new
endclass: atm_cell
6.4 constraint sequence
//Sample 6-40. Generating sequences using an array
class atm_cell_seq;
rand atm_cell randomized_cells[];
endclass: atm_cell_seq
class atm_gen;
...
atm_cell_seq randomized_seq;
virtual task main();
while (this.stop_after != 0) begin
...
ok = this.randomized_seq.randomize();
...
end
endtask: main
endclass: atm_gen