SystemVerilog Verification Test Platform Writing Guide Chapter 4 Connection Design and Test Platform

Verifying a design requires several steps: generating input stimuli, capturing output responses, deciding right and wrong, and measuring progress. To complete this design, the first step is how to connect the DUT (Design Under Test) to the test platform. The content of this chapter will solve this problem.

4.1 Separating the test platform
from the design The code of the test platform is independent of the design code. The designer needs to write code that meets the specifications, and the verification engineer needs to create a scenario that makes the design not meet the design specifications.The use of modules to save the test platform often causes timing problems during driving and sampling. A program block is introduced in SystemVerilog to separate the test platform from logic and time.
4.1.1 Communication between the test platform and the DUT
As the design complexity increases, the connection between the modules also becomes complicated. In this chapter, in order to let everyone better understand the connection between the test platform and the DUT, we take the test platform of the arbiter as an example for analysis.
Usually in our Verilog port description is more cumbersome, now for this example, let's take a look at the complex port description.
Example 4.1 using verilog portArbiter model

module arb_port (output logic[1:0] grant,
				input logic[1:0] request,
				input logic rst,
				input logic clk );
	always@(posedge clk or posedge rst)
	begin
		if(rst)
			grant<=2'b00;
		else
			......
	end
endmodule

The test platform is defined in another module, independent of the module in which the design is located. In general, the test platform is connected to the design through the port.
Example 4.2 Using portstesting platform

module test (input logic[1:0],
			input logic clk,
			output logic[1:0] request,
			output logic rst);
	initial begin
			@(posedge clk) request<=2'b01;
			$ display("@%0t:Drove req=01",$ time);
			repeat(2) @(posedge clk);
			if(grant!=2'b01)
				$ diaplay("@%0t:a1:grant!=2'b01",$time);
			......
			$finish;
	end
endmodule

The top-level netlist connects the test platform and the DUT, and contains a simple clock generator.
Example 4.3 Without interfaceTop net list
module top;
logic [1: 0] grant, request;
bit clk, rst;
always # 5 clk = ~ clk; // clock generator

arb_port a1 (grant,request,rst,clk); //例4.1 使用端口的仲裁器模型
test t1 (grant,request,rst,clk); //例4.2 使用端口的测试平台


Although the endmodule is connected through the port, the above example does not look too complicated, but inThe actual real design often contains hundreds of port signals, requiring several pages of code to declare the signals and ports. All these connections are extremely error-prone. if you wantAdd a new signal, it must be defined and connected in multiple files. For the above problems, SystemVerilog has corresponding solutions.

4.2 Interface interface
SystemVerilog uses interfaces to model communication between blocks. The interface is seen as a bundle of intelligent connections. The interface includes connection, synchronization, and even communication between two or more blocks, which connect the design block and the test platform.
4.2.1 Use interfaces to simplify connections
We bundle ports into one interface. The interface is extended to the test platform and the driving and receiving function modules of the DUT. The clock can be part of the interface or a separate port.
The simplest interface is just a combination of two-way signals. These signals use the logic data type and can be driven by procedural statements.
Example 4.4 Simple interface of arbiter

interface arb_if (input bit clk);
	logic[1:0] grant,request;
	logic rst;
endinterface

Example 4.5 Arbiter model using a simple interface

module arb (arb_if arbif); //DUT使用接口,接口实例名arbif
	......
	always@(posedge arbif.clk or posedge arbif.rst)
		begin
		if(arb.rst)
			arb.grant<=2'b00;
		else
		......
		end
endmodule

You can refer to the signal of the interface by using the instance name arbif.request.Interface signals must be driven using non-blocking assignment.
Example 4.6 Test platform using simple arbiter interface

module test (arb_if arbif);
......
	initial 
	begin //此处省略了复位代码
		@(posedge arbif.clk);
		arbif.request<=2'b01;
		$ display("@%0t:Drove req=01",$ time);
		repeat(2) @(posedge arbif.clk);
		if(arbif.grant!=2'b01)
			$ diaplay("@%0t:a1:grant!=2'b01",$time);
		......
		$finish;
	end
endmodule

All these blocks are instantiated and connected in the top module.
Example 4.7 The top module using a simple arbiter interface

module  top;
	bit clk;
	always #5 clk=~clk;
	arb_if arbif(clk); //例4.4
	arb a1(arbif); //例4.5
	test t1 (arbif); //例4.6
endmodule:top

The top-level module connects the design DUT described in the module, the test platform TEST in the program block, and the interface interface; the shortcut symbol. ** (implicit port connection) can automatically connect the port of the module instance to the specific signal at the current level As long as the names and data types of the ports and signals are the same.
Need to pay attention to a few points:
When building a test platform, interface signals must be driven using non-blocking assignments
When using interfaces, make sure to declare interface variables outside of your modules and blocks.
4.2.2 Connect interface and port
You can directly connect the signal of the interface to the port.

module  top;
	bit clk;
	always #5 clk=~clk;
	arb_if arbif(clk);
	arb a1(.grant(arbif.grant), //.port(ifc.signal)
			.request(arbif.request),
			.rst(arbif.rst),
			.clk(arbif.clk));
	test t1 (arbif);
endmodule:top

4.3 Using modport to group the signals
in the interface In the previous interface definition, the direction of the signal is not mentioned, but a point-to-point connection with no signal direction is used.Use the modport structure in the interface to group signals and specify the direction.
The monitor modport statement in the following example code enables the test platform to connect to a newly added monitor module.
Example 4.10 Interface with modport

interface arb_if (input bit clk);
	logic[1:0] grant,request;
	logic rst;
	modport TEST(output request,rst,
				input grant, clk);
	modport DUT (input request, rst, clk,
				output grant);
	modport MONITOR(input request,grant,rst,clk);
endinterface

The following is the corresponding arbiter model and test platform, they all use modport in their port connection table. It should be noted that you need to put the modport name, DUT or TEST, behind the interface name, arb_if.
Example 4.11 arbiter model using modport in interface

module arb (arb_if.DUT arbif);
	......
endmodule

Example 4.12 Test platform using modport in the interface

module test (arb_if.TEST arbif);
	......
endmodule

The top-level module is the same as in Example 4.7. Modport is specified only in the module header, and need not be specified when the module is instantiated.
Note: When defining the interface above, we used modport MONITOR (Example 4.10), which can connect the test platform to a newly added monitor module.
Example 4.13 arbiter model using modport for the interface

module monitor (arb_if.MONITOR arbif);
	always@(posedge arbif.request[0])
	......
endmodule

4.3 Stimulus Timing
On the test platform at the clock cycle level, you need to drive and receive the synchronization signal at the appropriate time relative to the clock signal. Driving too late or sampling too early, the test platform's action will miss a clock cycle. SystemVerilog has several structures that can help you control timing issues in communications.
4.3.1 Use the clock block cb to control the timing of the synchronization signal The
interface block can use the clock block to specify the timing of the synchronization signal relative to the clock. Most of the clock blocks are used in the test platform.
Any signal in the clock block will be driven or sampled synchronously, which ensures that the test platform interacts with the signal at the correct time.
An interface can contain multiple clock blocks. Because there is only one clock expression in each block, it corresponds to a clock domain.
Example 4.14 Interface with clock block

interface arb_if (input bit clk);
	logic[1:0] grant,request;
	logic rst;
	clocking cb @(posedge clk); //声明cb
		output request;
		input grant;
	endclocking
	modport TEST(clocking cb, //使用cb
		output rst);
	modport DUT (input request, rst,
		output grant);
endinterface

As can be seen from the above example, TEST modport treats request and grant as synchronization signals.
When using modport in the clock block, the interface name and clock block name must be added in front of any synchronous interface signal. arbif.cb.request is legal, and arbif.request is illegal

//简单的测试平台
module test (arb_if.TEST arbif);
	initial 
	begin
		arbif.cb.request<=0;
		@arbif.cb;
		$ diaplay("@%0t:grant!=%0b",$time,arbif.cb.grant);
	end
endmodule

arbif.cb represents the effective edge of the clock without describing the exact clock signal and edge. arb.cb.request <= 0, indicating that the value of 0 is assigned to the request at the valid edge of the clock.
Interface signals use non-blocking assignment.
4.3.2 Comparison of logic and wire
in the interface We recommend defining the signal as logic type in the interface. The logic type statement can be assigned in the process statement or can be directly driven. The wire type can only use continuous assignment.
4.3.4 Program Block and Timing Region
test platform should be independent of design not only in logic but also in timing. There is usually a state of competition between the test platform and the design. The root cause of this problem is that the design and test platform events are mixed in the same event slice.
If there is a way to separate these events on the timeline, ensure that the test platform can start the next action after all events have been executed. Then it will solve the timing problem well.
How does SystemVerilog separate the events of the test platform from the designed events??
In SystemVerilog, the test platform code is in aProgram blockIn, this is very similar to the module, but,Program blocks cannot have any hierarchical levels, Such as module instances, interfaces, or other programs.
SystemVerilog introduces a new way of dividing time slices.
Active: Design code in the simulation module
Observed: Execute SystemVerilog assertion
Reactive: Executing the test platform part of the program
Postponed: For the input sampling of the test platform, all the design activities are completed in the read-only time period sampling signal.
Example 4.27 Using a test platform with a clock block interface

program automatic test (arbif.TEST arbif);
...
	initial begin
		arbif.cb.request<=2'b01;
		repeat(2) @arbif.cb; //@arbif.cb语句将等待时钟块给出有效沿@(posedge clk)
		if(arbif.cb.grant!=2'b01);
			$ display ("@%0t:a1:grant!=2'b01",$time);
	end
endprogram:test

The test code should be contained in a single program block. OOP should be used to create a dynamic, layered test platform through objects rather than modules.
4.3.4 End of simulation
Type 1
: End of $ finish encounter Type 2 : If there is only one program block, then when the last statement in all initial blocks is completed, the simulation ends. If there are multiple blocks, the simulation ends at the end of the last block.
The third type: executing $ exit can terminate any program block in advance.

4.4 Driver and sampling of the interface The
test platform needs to drive and sample the designed signal, which is mainly done through the interface with clock block.
4.4.2 Interface signal sampling
The information to be sampled by the interface samples the designed output signal. When you read a signal from the clock block, you get the sampled value before the clock.
Example 4.19 Sampling and driving of the synchronous interface in the module

'timescale 1ns/1ns;
program test (arcif.TEST arbif);
	initial begin
		$ monitor("@%0t:grant=%h",$time,arbif.cb.grant);
	#50 $display("End of test");
	end
endprogram
module arb(arb_if.DUT arbif);
	initial begin
		#7 arbif.grant=1;
		#10 arbif.grant=2;                        
		#18 arbif.grant=3;
	end
endmodule

arbif.grant is driven by a module and can use blocking assignment.
In the program block in the test platform, when using modport in the clock block, any synchronized interface signals must be in the form of arbif.cb.grant.
Insert picture description here
4.4.3 Interface signal drive
The default timing of the clock block is to sample the input signal after # 1step delay, and drive the output signal after # 0 delay. The
# 1step delay specifies that the signal is in the Postponed area of ​​the previous time slice. Any new actions are sampled before.
# 0, because of the clock module, the output signals of the test platform are synchronized, so they are directly sent to the design.
Example 4.22 Driving a synchronous interface

program test(arb_if.TEST arbif);
	initial begin
		#7 arbif.cb.request<=3; //@7ns
		#10 arbif.cb.request<=2; //@17ns
		#18 arbif.cb.request<=1; //@25ns
		#15 finish;
	end
endprogram
module arb(arb_if.DUT arbif);
	initial
		$monitor("@%0t:req=%h", $time,arbif.request);
endmodule

From the above example, we can see that if the test platform drives the synchronous interface signal at the valid edge of the clock, its value will be immediately transferred to the design.
Example 4.23 Interface signal driver

 ##2 arbif.cb,request<=0; //等待两个时钟周期然后赋值
##3;//非法——必须跟赋值语句同时使用

It means to wait for two clock cycles before driving the signal. It must be followed by an assignment statement or it is illegal.
4.4.6 Why is the always block not allowed in the program?
In SystemVerilog, you can use the initial block in the program, but you cannot use the always block. This is because in a design, the always block may trigger execution at the rising edge of each clock from the beginning of simulation. However, the execution process of a test platform is completed after the steps of initialization, driving and corresponding design behavior. Here, a continuously executed always block does not work properly.
If you add an always block to the program block, it will never end, so you must call $ exit to signal the end of the program block
If you really need an always block, you can use "initial forever" to accomplish the same thing.
4.4.7 Clock generator The
clock is more closely integrated with the test platform than it is with the design, soThe clock generator should be defined as a module, not a program blockIf you put the clock generator in the program and start the transmission in Reactive, the sequence of the signals that arrive in Active may cause competition at time 0. Therefore, the clock edges are generated using blocking assignments, and they will trigger the generation of events in the Active area.
Example 4.25 Wrong clock generator in a block

program bad_generator (output bit clk, out_sig);
	initial
		forever # 5clk=~clk;
	initial
		foever@(posedge clk)
			out_sig<=~out_sig;
endprogram

The clk and out_sig signals will cause a race condition.
Example 4.26 The correct clock generator in the module

module clock_generator(output bit clk);
	initial
		always #5 clk=~clk; //在时间0之后生成时钟沿
endmodule

4.6 Top-level scope
In Verilog, only macro definitions can cross module boundaries, and are often used to create global variables. SystemVerilog introduces a compilation unit, which is a combination of source files compiled together.Any scope outside of the boundaries of module, interface, program, package, etc. is called the compilation unit scope, which also becomes $ unit.
Any members of this scope, such as parameter, are similar to global members, because they can be accessed by all lower-level blocks, but they are different from true global members, such as parameter. Other source files are not visible at compile time. The following example shows the usage of parameter and const of the compilation unit.
Example 4.31 Top-level scope of arbiter design

//root.sv
`timescale 1ns/1ns
parameter int TIMEOUT=1_000_000;
const string time_out_msg="ERROR";

module top;
	test t1();
endmodule

program automatic test;
	......
	initial 
	begin
		#TIMEOUT;
		$ display("%s",time_out_msg);
		$ finish;
	end
endprogram

The instance name $ root allows you to explicitly reference the member names in the system from the top-level scope. At this point it is similar to "/" in the Unix file system. You can specify the absolute path through $ root to explicitly direct variables across modules. Here are several usages of cross-module references. In the program block, reference the clk signal in module top.
Absolute reference:
$ root.top.clk
uses macro definition:
'define TOP $ root.top
' top.clk
relative reference:
top.clk
example 4.32 cross-module reference using $ root

`timescale 1ns/1ns
parameter int TIMEOUT=1_000_000;
top t1(); //顶层模块的显示例化
    
module top;
	bit clk;
	test t1(.*);
endmodule
    
'define TOP $root.top
program automatic test;
	......
	initial 
	begin
		//绝对引用
		$ display("clk=%b", $root.top.clk);
		$ display("clk=%b", 'top.clk); //使用宏
		//相对引用
		$ display("clk=%b", top.clk);
endprogram

4.7 Program-Module Interaction The
program block can read and write all signals in the module, and can call all the history in the module. These history can change the value of the internal signal, but the module can not see the program block. This is because the test platform needs to access and control the design, but the design is independent of anything on the test platform.
Use the functions in the DUT to obtain information in the test platform. The functions in the module can encapsulate the communication between the two and make the test platform more easily synchronized with the design.

4.8 System Verilog Assertion Assertion SVA
uses SystemVerilog Assertions (SVA) to create timing assertions in your design. The emulator keeps track of which assertions are activated, so that data on functional coverage can be collected on this basis.
4.8.1 Immediate assertion: Immediate Assertion

bus.cb.request<=1;
repeat(2) @bus.cb;
a1:assert (bus.cb.grant==2'b01);

We expect the content in the assert to be true, otherwise output an error message. Similar to if statements, but equivalent procedural code may be far more complicated and lengthy than these assertions.
4.8.2 Custom assert behavior
a1: assert (bus.cb.grant == 2'b01)
else $ error (“Grant not asserted”);
assert asserts optional then and else clauses to change the default information, you can add your own The desired output information.
SystemVerilog has four functions for outputting messages:$info $warning $error $fatal. These functions are limited to use inside assertions.
4.8.3 Concurrent assertions

interface arb_if(input bit clk);
	logic[1:0] grant,request;
	logic rst;

	property request_2state;
		@(posedge clk) disable iff (rst);
		$isunknown(request)==0;//确保没有Z/X值存在
	endproperty
	assert_request_2state:assert property(request_2state);
endinterface

The request signal is not X or Z at any time except during reset.
1. It is a continuously running module that checks the value of the signal for the entire simulation process.
2. A sampling clock is specified in the assertion.

4.8 ref port
SystemVerilog introduces a new port direction: ref. In Verilog, the port directions we are familiar with are: input, output, inout.
If multiple inout ports are used to drive a signal, SystemVerilog will calculate the final signal value based on the value and drive strength of all drivers.
If multiple ref ports drive a variable, there may be competition, because the ports of multiple modules may update the same variable. The ref port is actually a reference to a variable (not net), and its value is the last value assigned to the variable.

Published 38 original articles · Like 29 · Visits 10,000+

Guess you like

Origin blog.csdn.net/weixin_45270982/article/details/97526783