Verilog study notes (4): simulation verification and Testbench writing


1. Overview of Verilog circuit simulation and verification

Simulation, also called simulation, is to confirm whether the correct design results consistent with expectations are obtained by using EDA simulation tools, by inputting test signals, comparing output signals (waveform, text or VCD files) and expected values, and verifying the design. correctness.
Verification is a process of proving how the design idea is implemented and ensuring that the design is functionally correct.

Verification is divided into 4 stages in the whole process of Verilog design:

  • Phase 1: Functional Verification;
  • Phase 2: post-synthesis validation;
  • Phase 3: timing verification;
  • Phase 4: Board Level Verification.

2. Verilog test program design basis

2.1Testbench and its structure

During simulation, Testbench is used to generate test stimulus for Design Under Verification (DUV), or Design Under Test (DUT).
insert image description here
The general structure of the test program:
insert image description here
Testbench is a test platform, the signal is integrated inside the module, there is no input and output. In the Testbench module, the top-level module of the design to be tested is instantiated, and the code of the test behavior is encapsulated in it to directly provide test incentives for the system to be tested.

Example: T flip-flop test program

module TFF_tb;
reg clk, rst_n, T;
wire data_out;
TFF U1(.data_out(data_out), .T(T), .clk(clk), .rst_n(rst_n)); //对被测模块实例化
	always
		#5 clk = ~clk;
	Initial 
		begin
			clk = 0;
			#3 rst_n = 0;
			#5 rst_n = 1;
			T = 1;
			#30 T = 0;
			#20 T = 1;
		end
	Initial
		begin
			$monitor($time,"T=%b, clk=%b,rst_n=%b,data_out=%b", T, clk, rst_n,data_out);
		end
endmodule

Waveform simulation and text output of T flip-flop:
insert image description here
The main functions of Testbench can be clearly seen from the figure below:
(1) Provide excitation signal 0 for DUT.
(2) Correctly instantiate the DUT.
(3) Display the simulation data on the terminal or save it as a file, or display it in the waveform window for analysis and inspection.
(4) EDA tools can be used for complex designs, or the simulation results can be automatically compared with ideal values ​​through the user interface to realize automatic inspection of the results.

insert image description here
Issues that need to be paid attention to when writing Testbench:
(1) Testbench code does not need to be synthesizable.
Testbench code is only a description of hardware behavior, not a hardware design.
(2) Behavior-level description is highly efficient
Verilog HDL language has 5 description levels, which are switch level, gate level, RTL level, algorithm level and system level.
(3) Grasp the structured and stylized description method
Structured description is beneficial to design and maintenance, and different test incentives can be divided through initial, always and assign statements.
Generally do not put all tests in a statement block

2.2 Examples of test platforms

The test platform needs to generate clock signals, reset signals and a series of simulation vectors, observe the response of DUT, and confirm the simulation results.
insert image description here
(1) Build the simulation environment of combinational logic circuit
The truth table of the full adder:
insert image description here

module adder1 (a, b, ci, so, co); 
input a, b, ci ; 
output so, co ; 
assign { co , so } = a + b + ci ; 
endmodule

The full adder test program written according to the truth table of the full adder is as follows:

module adder1_tb ;
 wire so, co;
 reg a, b, ci; 
 adder1 U1(a, b, ci, so, co); //模块例化
 initial //测试信号产生 
 	begin
 		 a = 0; b = 0; ci = 0; 
 		 #20 a = 0; b = 0; ci = 1; 
 		 #20 a = 0; b = 1; ci = 0; 
 		 #20 a = 0; b = 1; ci = 1; 
 		 #20 a = 1; b = 0; ci = 0;
 		 #20 a = 1; b = 0; ci = 1; 
 		 #20 a = 1; b = 1; ci = 0; 
 		 #20 a = 1; b = 1; ci = 1; 
 		 #200 $finish; 
	end 
endmodule

The input a, b and ci of the full adder are defined as reg-type variables, and the output so and co are defined as wire-type variables; use the module instantiation statement "
adder1 U1(a, b, ci, so, co);" to put all The design circuit of the adder is instantiated into the test simulation environment; the input change is changed and the test condition is generated with the initial block statement, and the input change statement is completely written according to the truth table of the full adder (2) The construction of the sequential logic circuit simulation
insert image description here
environment
is In the sequential logic circuit simulation environment, it is necessary to consider timing, timing information, global reset, set and other signals, and define these signals.

Decimal addition counter written in Verilog:

module cnt10(cIk ,rst, ena, q, cout); 
input clk,rst,ena; 
output [3:0] q;
output cout; 
reg [3:0] q;
always@(posedge clk or posedge rst) 
	begin
		if(rst)q=4’b0000;
			else if(ena)
				begin
					if(q<9)
						q=q+1;
					else 
						q=0;
				end
	end
assign cout=q[3]&q[0];
endmodule

Test program code:

module cnt10_tb; 
reg clk, rst, ena; 
wire [3:0] q;
wire cout;
cnt10 U1(clk ,rst, ena, q, cout); 
always #50 clk = ~clk; 
initial 
	begin
		clk=0;rst=0;ena=1; 
		#1200 rst=1;
		#120 rst=0;
		#2000 ena=0;
		#200 ena=1;
		#20000 $finish; 
	end
endmodule

Instantiate the statement "cnt10 U1(clk,rst,ena,q,cout);" to instantiate the decimal counting module into the simulation environment;
use the statement "#50 clk=~clk;" in always to generate a cycle of 100 (standard Time unit) clock square wave;
use the initial block to generate test conditions for the reset signal rst and the enable control signal ena.
The test results are shown in the figure:

insert image description here

2.3Verilog simulation result confirmation

(1) Directly observe the waveform
By directly observing the output of each signal waveform, comparing the test value and the expected value, to determine the correctness of the simulation result.
insert image description here

(2) Print text output method

module adder1_tb; 
wire so,co; 
reg a,b,ci; 
adderl U1(a,b,ci,so,co);
initial
	begin
		a=0;b=0;ci=0;
		#20 a=0;b=0;ci=1;
		#20 a=0;b=1 ;ci=0;
		#20 a=0;b=1 ;ci=1;
		#20 a=1 ;b=0;ci=0;
		#20 a=1 ;b=0;ci=1;
		#20 a=1 ;b=1 ;ci=0;
		#20 a=1;b=1;ci=1;
		#200 $finish; 
	end
	$monitor($time, "%b %b %b -> %b %b”,a, b, ci, so, co);
endmodule

Output result:

0 0 0 0 -> 0 0
20 0 0 1 -> 1 0
40 0 1 0 -> 1 0
60 0 1 1 -> 0 1
80 1 0 0-> 1 0

System task printing task:
$display: directly output to the standard output device;
$monitor: monitor parameter changes;
$fdisplay: output to a file, etc.

(3) Automatically check the simulation results
Automatically check the simulation results by adding assertion monitors to key nodes in the design code to form comments on circuit logic synthesis or descriptions of design features to improve the observability of design modules.

(4) Using VCD files
Verilog provides a series of system tasks for recording signal value changes and saving them to a standard VCD (Value Change Dump) format database. VCD file is a waveform record file in a standard format, which only records the changed waveform.

2.4 Verilog simulation efficiency

Because the conversion of parallel semantics needs to be completed through serial software codes, the execution time of Verilog behavior-level simulation codes is relatively long.

Improve the simulation code execution time of Verilog HDL code:
(1) Reduce the hierarchical structure
The less the simulation code is, the shorter the execution time will be.
(2) Reduce the use of gate-level codes
Since gate-level modeling belongs to structural-level modeling, it is recommended that simulation codes use behavior-level statements as much as possible. The more abstract the modeling level, the shorter the execution time.
(3) The higher the simulation accuracy, the lower the efficiency.
The greater the difference between the timing unit value and the timing accuracy value, the longer the simulation time. `timescale emulates the timescale.
(4) The fewer processes, the higher the efficiency
The fewer statement blocks in the code, the faster the simulation, because it takes time for the emulator to switch between different processes.
(5) Reduce the output of the emulator.
The Verilog language contains some system tasks. Some prompt information can be output in the console display window of the emulator, but the execution efficiency of the emulator will be reduced.

3. System tasks related to simulation

3.1$display和 $write

Grammar format:
$display("<format_specifiers>", <signal1 , signal2, ..., signaln>);
$write("<format_specifiers>", <signal1, signa12, ..., signaln>);

"<format_specifiers>" is usually called "format control"
"<signal1, signal12, …, signaln>" is "signal output list"
$display automatically wraps after output
$write outputs specific information without wrapping

The output format description consists of "%" and format characters, and its function is to convert the output data into the specified format for output.
Several commonly used output formats are as follows:
insert image description here
Some special characters can be output through the conversion sequence in the table:
insert image description here
Example: $display and $write statements

module disp_tb; 
reg[31 :0] rval; 
pulldown(pd);
initial
	begin
		rval=101;
		$display("\\\t%%\n\"\123”);
		$display("rval=%h hex %d decimal" rval, rval);
		$display("rval=%o otal %b binary", rval, rval);
		$display("rval has %c ascii character value",rval);
		$display("pd strength value is %v", pd);
		$display("current scope is %m");
		$display("%s is ascii value for 101",101);
		$write("simulation time is");
		$write("%t\n", $time); 
	end
endmodule

insert image description here
In $display, the display width of the data in the output list is automatically adjusted according to the output format, and the current value of the expression is always displayed with the number of digits occupied by the maximum value of the expression.

3.2 $monitor and $stobe

Both $monitor and $stobe provide the function of monitoring and outputting the value of characters or variables in the parameter list
(1) Syntax format of $monitor:
$monitor("<format_specifiers>", <signal1, signal2, ..., signaln>);
Task $monitor provides the function of monitoring and outputting the expression or variable value in the parameter list. Whenever the value of a variable or expression in the parameter list changes, the value of the variable or expression in the entire parameter list will be output and displayed.
For example: $monitor($time, , “rxd=%b txd=%b”,rxd txd);
Note that in the above statement, ", ," represents an empty parameter. Empty arguments appear as spaces on output.

The role of the $monitoron and $monitoroff tasks is to control the start and stop of the monitoring task $monitor by turning on and off the monitoring flag, so that the programmer can easily control when the $monitor occurs.
The difference between $monitor and $display is that $monitor is often called in the initial block. As long as $monitoroff is not called, $monitor will continuously monitor the set signal.

Example: Application of $monitor system task

module monitor_tb;
	integer a,b;
	initial 
		begin
			a = 2;
			b = 2;
			forever 
				begin
					#5 a = a + b;
					#5 b = a - 1;
				end
		end
	initial $monitor($time,"a = %d,b = %d",a, b);
endmodule

insert image description here
(2) Syntax format of $strobe:
$strobe(<functions_or_signals>);
$strobe("<string_and/or_variables>",<functions_or_signals>);
The detection task is used to output a line of formatted text at the end of this time step after all the time has been processed at a certain moment. Commonly used system tasks are as follows:
$stobe: After all time processing, output a line of formatted text in decimal format;
$strobeb: After all time processing, output a line of formatted text in binary format;
$strobeo: In After all times are processed, output a line of formatted text in octal format;
$strobeh: After all times have been processed, output a line of formatted text in hexadecimal format.

When the $strobe task is called, all the assignment statements are completed, and then the corresponding text information is output. The $strobe task provides another data display mechanism, which ensures that the data is only displayed after all assignment statements have been executed.

3.3 $time and $reltime

  • The current simulation time can be obtained by using these two time system functions. The difference is that the $time function returns the simulation time in the form of a 64-bit integer value, while the $realtime function returns the simulation time in real number data.
    (1) Example of system function $time
    : application example of $time system task
`timescale 1ns/1ns
module time_tb;
	reg ts;
	parameter dalay = 2;
	initial 
		begin
			#delay ts = 1;
			#delay ts = 0;
			#delay ts = 1;
			#delay ts = 0;
		end
	initial
		$monitor($time,,"ts = %b",ts);
	$time
endmodule

Output result:

0 ts = x
3 ts = 1
5 ts = 0
8 ts = 1
10 ts = 0

(2) $realtime system function

The time number returned by $realtime is a real number, which is also based on the time scale.
Example: Application instance of $realtime system task:

`timescalel 1ns/1ns 
module realtime_tb; 
reg set; 
parameter p=2; 
	initial
		begin
			$monitor($realtime, ,"set=b%" ,set); 
			#p set=0;
			#p set=1;
		end
endmodule

Output result:

0 set=x
2 set=0
4 set=1

3.4 $finish and $stop

The system tasks $finish and $stop are used to control the simulation process, indicating the end of the simulation and the interruption of the simulation respectively. Its grammatical format:
$finish;
$fimsh(n);
$stop;
$stop(n);
Among them, n is the parameter of $finish and $stop, and n can take several values ​​of 0, 1 or 2, respectively expressing the following meanings , as shown in the table below.

insert image description here
The function of $finish is to exit the emulator and return to the main operating system, that is, to end the emulation process. The task $finish can take parameters, and output different feature information according to the values ​​of the parameters. If there is no parameter, the default parameter value of $finish is 1.
The role of the $stop task is to put the EDA tool (such as the emulator) into the pause mode, give an interactive command prompt in the emulation environment, and give the control right to the user. This task can take parameter expressions. Depending on the parameter value (0, 1 or 2), different information is output. The larger the parameter value, the more information is output.

instance of $finish

module finish_tb;
	integer a,b;
	initial 
		begin
			a = 2;
			b = 4;
			forever
			 begin
			 	#5 a = a + b;
			 	#5 b = a - 1;
			 end
		end
	initial #40 $finish; //程序执行到40个时间单位时退出仿真器
	initial 
		begin
			$monitor($time,"a = %d,b = %d",a,b);
		end
endmodule

Instances of $stop

module stop_tb;
	integer a,b;
	initial 
		begin
			a = 2;
			b = 4;
			forever
			 begin
			 	#5 a = a + b;
			 	#5 b = a - 1;
			 end
		end
	initial #40 $stop; //程序执行到40个时间单位时停止仿真
	initial 
		begin
			$monitor($time,"a = %d,b = %d",a,b);
		end
endmodule

3.5$readmemh和$readmem

In the Verilog program, there are two system tasks $readmemh and $readmem used to read data from the file into the memory. The two system tasks can be executed at any time during the simulation, and their grammatical format is as follows:
(1) $readmemb(“<file_name>”,<memory_name>);
(2) $readmemb(“<file_name>”, <memory_name>,<start_addr>);
(3) $readmemb(“<file_name>”,<memory_name>,<start_addr>,<finish_addr>);
(4) $readmemh(“<file_name>”,<memory_name>) ;
(5) $readmemh(“<file_name>”,<memory_name>,<start_addr>);
(6) $readmemh(“<file_name>”,<memory_name>,<start_addr>,<finish_addr>);

Example: $readmemh and $readmem system task instance

module read_mem_tb;
	reg [7:0] memory_b [0:7];
	reg [7:0] memory_h [0:31];
	integer i;
	initial
		begin
			$readmemb("init_b.txt",memory_b); //把数据文件init_b.txt读入存储器中的指定地址
			$readmemh("init_h.txt",memory_h);
			for(i = 0;i < 8; i=i+1)
				begin
					$display("memory_b[%0d] = %b",i.memory_b[i]);
					$display("memory_h[%0h] = %h",i.memory_h[i]);
				end
		end
endmodule

The files init_b.txt and init_h.txt contain initialization data. Use @<address> to specify the address in the data file. Among them, "init_b.txt" specifies that the binary data is written from the second address; and "init_h.txt" specifies that the hexadecimal data is written from the first address. A sample file is as follows:

init_b.txt:

@002 
11111111 01010101
00000000 10101010
@001
1111zzzz 00001111

init_h.txt:

@001
00000000000000000000000000000011
00000000000000000000000000000111
00000000000000000000000000001111
00000000000000000000000000011111

3.6$random

$random is a system function that generates random numbers. Each call to this function will return a 32-bit random number, which is a signed integer. Syntax format:
$random%<number>;
This system function provides a means to generate random numbers. When the function is called, it returns a 32bit random number. It is a signed integer.
The general usage of $random is: $ramdom%b, where b>0, it gives a random number in the range of (-b + 1):(b - 1).

Example:

`timescale 1ns/1ns 
module random_pulse(dout); 
output [9:0] dout;
reg dout;
integer delay1,delay2,k; 
initial
	begin
		#10 dout=0;
		for(k=0;k<100;k=k+1)
			begin
				delay1 = 20 * ({$random} % 6);
				delay2 = 20 * (1 +{$random} % 3); 
				#delay1 dout= 1 << ({$random}%10);
				$delay2 dout = 0;
			end
	end
endmodule

4. Signal time assignment statement

insert image description here

4.1 Syntax Description of Time Delay

The delay statement is used to control the execution time of each statement, so as to meet the user's timing requirements at a high speed. There are two types of grammatical formats for delay control in Verilog language:
(1) #<delay time> behavior statement;
(2) #<delay time>;
where the symbol "#" is the key character of delay control, <delay time> Can be a directly specified amount of delay time, given in units of simulation time. During simulation, all delays are defined in terms of time units.

The following is an example of an assignment statement with a delay.
#2 Sum = A ^ B; //#2 After specifying 2 time units, assign the value of A XOR B to Sum

According to the position of the time control part in the procedure assignment statement, the time control mode in the procedure assignment statement can be divided into external time control mode and internal time control mode.
(1) The external time control method is that the time control appears at the leftmost end of the assignment statement in the whole process, that is, the time control method on the left side of the assignment target variable. Its grammatical structure is as follows: when the simulation is executed, it is equivalent to the following statements
#5 a=b;
:

initial
	begin
		#5;
		a = b;
	end

(2) The internal time control mode is the time control mode in which the time control part in the procedure assignment statement can also appear between the "assignment operator" and "assignment expression". Its grammatical structure is as follows:
a=#5b;
the time control part "#5" appears in the middle of the assignment operator ":" and the assignment expression "b", so there is a time control in the internal time control mode in this procedural assignment statement. When it is executed, it is equivalent to the execution of the following statements:

initial
	begin
		temp=b; //先求b的值
		#5;
		a=temp;
    end

4.2 Descriptive form of time delay

The description form of time delay here refers to the description form of delay control, which is divided into four forms: serial delay control, parallel delay control, blocking delay control and non-blocking delay control. Taking the realization of two sets of signals with different waveforms as an example (as shown in the figure q0_out and q1_out), the description forms of four different time delays are illustrated.
insert image description here
(1) Serial delay control
Serial delay control is the most common signal delay control. It is composed of begin and end process blocks plus delay assignment statements. The delay assignment statement can be an external time control method or an internal time control method. After <delay time>, it can also be determined according to the situation whether to execute the corresponding behavior statement.
If there is a corresponding behavior statement behind <delay time>, the simulation process will not execute the operation specified by the behavior statement immediately after encountering this behavior statement with delay control, but will wait until "<delay time>' The specified amount of time elapses before the action specified by the behavior statement actually begins.

Example: Verilog serial delay control method design the signal in the above figure

`timescale 1ns/1ns 
module serial_delay(q0_out, q1_out); 
output q0_out, q1 _out;
reg q0_out, q1_out;
initial
	begin
		q0_out=1'b0;
		#50 q0_out=l'b1;
		#100 q0_out=1'b0;
		#100 q0_out=1'b1;
		#50 q0_out=1'b0;
		#100 q0_out=1'b1;
		#50 q0_out=1'b0;
		#50 q0_out=1'b1;
		#50 q0_out=1'b0; 
	end
initial
	begin
		q1_out=1'b0;
		#100 q1_out=1'b1;
		#100 q1_out=1'b0;
		#50 q1_out=1'b1;
		#100 q1_out=1'b0:
		#50 q1_out=1'b1;
		#100 q1_out=1'b0;
		#50 q1_out=1'b1:
		#50 q1_out=1'b0; 
	end
endmodule

(2) Parallel delay control The
parallel delay control method is composed of a fork-join process block plus a delay assignment statement, where the delay assignment statement is the same as the serial delay control method, which can be either an external time control method or an internal time control method Way. After <delay time>, it can also be determined according to the situation whether to execute the corresponding action statement.
There is a corresponding behavior statement behind <delay time>, and the simulation process will not immediately execute the operation specified by the behavior statement after encountering this behavior statement with delay control, but will wait until "<delay time>" The specified amount of time elapses before the action specified by the behavior statement actually begins. However, the difference between the parallel delay control mode and the serial delay control mode is that multiple delay statements in the parallel delay control mode are executed in parallel, and there is no need to wait for the execution of the previous statement to complete before starting to execute the current statement.

Example: Verilog Parallel Delay Control Design Example Waveform Diagram

`timescale 1ns/1ns 
module parallel_delay (q0_out,q1_out); 
output q0_out,q1_out; 
reg q0_out,q1_out; 
initial 
	fork
		q0_out=1'b0;
		#50 q0_out=1'b1;
		#150 q0_out=1'b0;
		#250 q0_out=1'b1;
		#300 q0_out=1'b0;
		#400 q0_out=1'b1;
		#450 q0_out=1'b0;
		#500 q0_out=1'b1;
		#600 q0_out=1'b0;
	join
initial 
	fork 
		ql_out=1'b0;
		#100 q1_out=1'b1;
		#200 q1_out=1'b0;
		#250 q1_out=1'b1;
		#350 q1_out=1'b0;
		#400 q1_out=1'b1;
		#500 q1_out=1'b0;
		#550 q1_out=1'b1;
		#600 q1_out=1'b0;
	join
endmodule

(3) Blocking delay control
The assignment operation identified by the assignment operator "=" is called "blocking process assignment". Blocking delay control is a situation with delay control on the basis of blocking process assignment, for example:

initial
	begin
		a = 0;
		a = #5 1;
		a = #10 0;
		a = #15 1;
	end

Each blocking assignment statement will be executed sequentially, and the second statement will not start executing until the assignment operation specified by the first statement is completed. Therefore, at the beginning of the simulation process, the value "0" is assigned to a, and the second assignment statement is executed after the completion of this assignment statement; after the completion of the first assignment statement, the value of "1" is assigned to a with a delay of 5 time units a; similarly, the third assignment statement is executed after a delay of 10 time units after the completion of the second assignment statement, and "0" is assigned to a; the last assignment statement is at the moment when the first three statements are all completed, and the delay 15 time units, assign "1" to a. The figure below shows the waveform of signal a in the above example. Both of the above two examples use blocking assignment statements.
insert image description here
4) Non-blocking delay control
The assignment operation identified by the assignment operator "<=" is called "non-blocking process assignment". Non-blocking delay control is the case with delay control on the basis of non-blocking process assignment. As shown in the following example:

initial 
	begin
		a <= 0;
		a <= #5 1;
		a <= #10 0;
		a <= #15 1;
	end

In the above example, each non-blocking assignment statement is executed in parallel. Although the execution statement is in the begin-end serial block, its execution method is consistent with the parallel delay control method. Four delayed assignments are executed at the same time at the beginning of the simulation process. statement. At the beginning of the simulation process, assign the value "0" to a; assign the value "1" to a when it is 5 time units away from the start of the simulation; assign "0" to it when it is 10 time units away from the start of the simulation The value is assigned to a; finally, at 15 time units from the start of the simulation, the value "1" is assigned to a. The figure below shows the waveform of signal a in the above example. insert image description here
Example: Design of Verilog non-blocking delay control mode

`timescale 1ns/1ns 
module non_blocking_delay(q0_out,q1_out);
output q0_out,q1_out;
reg q0_out,q1_out; 
initial
	begin
		q0_out <= 1'b0; 
		q0.out <= #50 1'b1; 
		q0_out <= #150 1'b0; 
		q0_out <= #250 1'b1;
		q0_out <= #300 1'b0; 
	 	q0_out <= #400 1'b1; 
		q0_out <= #450 1'b0;
	  	q0_out <= #500 1'b1;
	 	q0_out <= #600 1'b0;
	end
initial
	begin 
		q1_out <= 1'b0; 
		q1_out <= #100 1'b1; 
		q1_out <= #200 1'b0;
		q1_out <= #250 1'b1; 
		q1_out <= #350 1'b0; 
		q1_out <= #400 1'b1; 
		q1_out  <= #500 1'b0; 
		q1_out <= #550 1'b1; 
		q1_out <= #600 1'b0;
	end
endmodule

4.3 Edge trigger time control

The syntax format of edge trigger event control can be in the following four forms:
Form 1: @(<event expression>) action statement;
Form 2: @(<event expression>);
Form 3: @(<event expression 1 >or<event expression 2>or…or<event expression n>) behavior statement;
form 4: @(<event expression 1>or<event expression 2>or…or<event expression n>);

1. Event expressions
 can appear in three forms in event expressions:
   Form 1: <signal name>
   Form 2: posedge <signal name>
   Form 3: negedge <signal name>

 where "<signal name>" can be a scalar or vector of any data type.
 In form 1, "<signal name>" representing the trigger event executes the following statement when the specified signal changes logic, as in the following example: @(in) out=in; when the sensitive event In changes logic (
  including
 positive jump and negative jump), execute the corresponding assignment statement, and assign the value of in to out.

 In form 2, when the "posedge <signal name>" that represents the trigger event occurs a positive transition of the specified signal, the following statement is executed, as in the following example: @(posedge(n) out=in; when the sensitive event in
  is
 positive When jumping, execute the corresponding assignment statement and assign the value of in to out.

 In form 3, when the "negedge <signal name>" representing the trigger event has a negative transition in the specified signal, the following statement is executed, as in the following example: @(negedge in) out=in; when the sensitive event in has a
  negative
 transition When changing, execute the corresponding assignment statement and assign the value of in to out.

 During the process of a logical change (positive or negative transition) in the signal, the value of the signal changes from one of the four values ​​0, 1, x, and z to another value; while the signal undergoes a positive transition The process is the transition from the low level of the signal mountain to the high level, and the negative jump is the transition from the high level to the low level of the signal mountain. The following table shows the positive and negative transitions specified in Verilog HDL:

Positive transition Negative transition
0→x 1→x
0→z 1→z
0→1 1→0
x→1 x→0
z→1 z→0

2. Edge trigger syntax format
Form 1: @(<event expression>) Behavior statement;
 the list of sensitive events in this syntax format contains only one trigger event, and only after the specified trigger event occurs, the subsequent behavior statement to start execution. When such a behavior statement with event control is encountered in the simulation process, if the specified trigger event has not occurred, the simulation process will stay here and wait until the specified trigger event occurs before starting to execute the subsequent behavior statement , the simulation process continues downward.
Example: Clock Pulse Counter

module clk_counter(clk, count_out);
input clk;
output count_out;
reg[3:0] count_out;
initial
	count_out = 0;
always@(posedge clk)
	count_out = count_out + 1; //在clk的每个正跳变边沿count_out增加1
endmodule

Form 2: @(<event expression>); The
 sensitive event list in this syntax format only contains one trigger event, and there is no behavior statement to specify the operation to be performed when the trigger event occurs. The execution process of the event control statement in this format is similar to the case where there is no action statement in the delay control statement. The simulation process will enter the waiting state after encountering this event control statement, and will not end the waiting state until the specified trigger event occurs. , exit the execution of the event control statement and start the execution of the next statement.

Example: A module used to determine the positive level of the input clock, the duration of the negative level and the clock period

module clk_time(clk);
	input clk;
	time posedge_time,negegde_time;
	time high_last_time,low_last_time,last_time;
	initial
		begin
			@(posedge clk);
				posedge_time = $time;
			@(negedge clk);
				negedge_time = $time;
			@(negedge clk);
				last_time = $time - posedge_time;
				high_last_time = negedge_time - posedge_time;
				low_last_time = last_time - high_last_time;
			$display("The clk stay in High level for: %t",high_last_time);
			$display("The clk stay in Low level for: %t",low_last_time);
			$display("The clk stay in signal Period  for: %t",last_time);
		end
endmodule

Form 3: @(<event expression 1>or<event expression 2>…or<event expression n>) behavior statement; the
 "sensitive event list" in this syntax format specifies the "<event expression >" represents multiple triggering events, these "<event expressions>" must be combined with the keyword "or". As soon as any one of these trigger events occurs, execution of the behavior statement is initiated. When the simulation process encounters an edge-triggered event control statement in this format, if none of the trigger events occurs, the simulation process will enter a waiting state until one of the trigger events occurs before starting to execute the following Behavior statement, the simulation process continues downward.

Form 4: @(<event expression 1>or<event expression 2>or...or<event expression n>)
 is the same as the third syntax format, and multiple trigger events are specified in this syntax format. But there are no behavior statements in this format. In this case, the execution process of this statement is similar to the execution process of the second syntax format. After encountering this event control statement, the simulation process will enter a waiting state until the After any event occurs, the waiting state is ended, the event control statement is exited and the next statement after the event control statement is started to be executed.
Example: Exit the event control statement after the trigger event occurs

module display(a,b);
input a,b;
wire a,b;
always@(posedge a or negndge b);
	begin
		$display("One of a and b changed in time: %t",$time);
	end
endmodule

4.4 Level Sensitive Event Control

Level-sensitive time control is another event control method, which is different from edge-triggered event control. It starts the statement to be executed when the specified conditional expression is true. Level-sensitive timing controls are denoted by the keyword "wait".
The grammatical format of level trigger event control can be the following two types:
Form 1: wait (conditional expression) behavior statement;
Form 2: wait (conditional expression);
the first form of level-sensitive event control contains behavior Statement, which can be a serial block (begin-end) statement or a parallel block (fork-join) statement, or a single behavior statement. In this form of event control statement, the trigger condition for the execution of the behavior statement is: the value of the conditional expression is "true (logic 1)". If the value of the conditional expression is "true" when the simulation process executes this level-sensitive control statement, then the statement block is executed immediately, otherwise the statement block has to wait until the conditional expression becomes "true" before starting to execute.

For example:

wait(enable == 1)
begin
	d = a & b;
	d = d | c;
end

The function of the wait statement is to control the execution of the following begin-end statement block according to the true or false of the conditional expression. After the enable signal enable becomes high level, that is, when the statement of enable==1 is true, a and b are performed. , and or operation between c; if the enable signal enable does not become high level, the execution of the begin-end statement block needs to wait until enable becomes high level before starting to execute.


The second form of level-sensitive event control does not contain behavior statements. In this form of level-sensitive event control statement, if the value of the conditional expression is "true" when the simulation process executes the wait control statement, then the execution of the wait event control statement is immediately terminated, and the simulation process continues. ; and if the value of the conditional expression is "false" when the simulation process executes the wait control statement, the simulation process enters the waiting state and waits until the value of the conditional expression becomes "true" before exiting the waiting state and ending at the same time The execution of the wait statement, the simulation process continues. This form of level-sensitive timing control is often used to control the execution timing of individual statements in a serial block.

begin
	wait(enable == 1)
	d = a & b;
	d = d | c;
end

5. Tasks and functions

Tasks and functions are provided in the Verilog language, which can divide a large behavior-level design into smaller code segments, allowing designers to extract the same code that needs to be reused in multiple places and write it into tasks and functions. Can make the code more concise and understandable.

5.1 Tasks

  1. Task definition
    Syntax format of task definition:
    task<任务名>;
    端口和类型声明
    局部变量声明
    begin
    语句1;
    语句1;
    ......
    语句n;
    end
    endtask

The task definition is embedded between the keywords task and endtask, wherein the keyword task marks the beginning of a task definition structure, and endtask marks the end of a task definition structure. "<task name>" is the name of the defined task. The input input port list cannot appear after "<task name>".

Take reading memory data as an example to illustrate the operation of task definition

task read_memory;
	input[15:0] address;
	output[31:0] data;
	reg[3:0] counter;
	reg[7:0] temp[1:4];
	begin
		for(counter=1;counter<=4;counter=counter+1)
			temp[counter]=mem[address+counter-1];
		data={temp[1],temp[2],temp[3],temp[4]};
	end
endtask

Pay attention to the following when defining a task:
(1) The list of port names cannot be listed in the first line of "task" statement.
(2) There may be event control statements such as delay statements and sensitive event control statements in the task.
(3) A task may have no or one or more input, output, and bidirectional ports.
(4) A task may have no return value, or return one or more return values ​​through an output port or a bidirectional port.
(5) A task can call other tasks or functions, or call the task itself.
(6) Process blocks (initial or always process blocks) are not allowed within the task definition structure.
(7) The disable termination statement can appear in the task definition structure, and the execution of this statement will interrupt the task being executed. After the task is interrupted, the program flow will return to the place where the calling task continues to execute.

2. Task call
Task call is realized through "task call statement". The task call statement lists the parameter values ​​of the incoming task and the variable values ​​of the received results. The calling format of the task is as follows:
<任务名>(端口1,端口,...,端口n);
Example: use the commonly used method in test simulation to illustrate the calling of the task

module task_tb;
 reg[7:0] mem[127:0];
 reg[15:0] a;
 reg[31:0] b;
 initial
	begin
		a = 0;
		read_mem(a,b);//任务的第一次任务调用
		#10;
		a = 64;
		read_mem(a,b);
	end
task read_memory;
 input[15:0] address;
 output[31:0] data;
 reg[3:0] counter;
 reg[7:0] temp[1:4];
	begin
		for(counter=1;counter<=4;counter=counter+1)
			temp[counter]=mem[address+counter-1];
		data={temp[1],temp[2],temp[3],temp[4]};
	end
endtask

The use of tasks can make the program more concise and easy to understand. Take the actual traffic control lights as an example to illustrate the definition of tasks and the characteristics of calling them.

module traffic_lights(red, amber, green);
output red, amber, green; 
reg [2:1] order; 
reg clock, red, amber, green; 
parameter ON=1, OFF=0, RED_TICS=350,AMBER_TICS=30,GREEN_TICS=200;//产生时钟脉冲
always
	begin
		#100 clock = 0;
		#100 clock= 1;
end
//任务的定义,该任务用于实现交通灯的开启
task light; 
 output red; 
 output amber;
 output green;
 input [31:0] tic_time; 
 input [2:1] order; 
 	begin
		red = OFF;
		green = OFF; 
		amber = OFF; 
		case(order)
			2'1b01: red = ON;
			2'b10: green = ON;
			2'b11: amber = ON; 
		endcase
		repeat(tic_time)@(posedge clock); 
		red = OFF;
		green = OFF; 
		amber = OFF; 
	end
endtask
//任务调用,交通灯初始化
initial
	begin
		order = 2'b00;
		light(red, amber, green, 0, order);
	 end
 //任务调用,交通灯控制时序
always
	begin 
		order=2’b01;
		light(red, amber, green,RED_TICS,order);//调用开灯任务,开红灯
		order=2'b10;
		light(red, amber, green,GREEN_TICS,order);//调用开灯任务,开绿灯
		order=2'b11;
		light(red, amber, green,AMBER_TICS,order);//调用开灯任务,开黄灯
	end
endmodule

5.2 Functions

1. Definition of function
function<返回值类型或位宽><函数名>;
<输入参量与类型声明>
<局部变量声明>
begin
语句1;
语句;
...
语句n;
end
endfunction
The function definition is embedded between the keywords function and endfunction, where the keyword function marks the beginning of a function definition structure, and endfunction marks the end of a function definition structure. "<function name>" is the name given to the function being defined. This function name also represents an internal variable inside the function definition structure, and the return value after the function call is passed to the calling statement through this function name variable.

<return value type or bit width> is an optional item, it is used to describe the type or width of the data returned by the function call, it can have the following three forms: (1) "[msb Isb]": this
form Explain that the return data variable represented by the function name is a multi-bit register variable, and its bit width is specified by [msb:lsb], which is more than the function definition statement:

function[7:0] adder;

A function "adder" is defined, and its function name "adder" also represents an 8-bit wide register variable, the highest bit is the 7th bit, and the lowest bit is the 0th bit.
(2) "integer": This form indicates that the return variable represented by the function name is an integer variable.
(3) "real": This form indicates that the return variable represented by the function name is a real variable.

"<input parameter and type declaration>" is to describe the width and type of each input port of the function. In the function definition, there must be at least one input port (input) declaration, and no output port (output) declaration. The data type declaration statement is used to describe the width and type of the local variables used in the function. The syntax of this declaration statement is consistent with the syntax of the corresponding declaration statement when defining the module.
"<local variable description>" is a description of the width and type of the local variable inside the function.
The series of statements defined by the "begin and end" keywords are the same as the tasks, and are used to specify the operations to be performed when the function is called. When the function is called, these statements will be executed in a serial manner.

Example: count the number of "0" in the input data

function[3:0] out;
input[7:0] x;
reg[3:0] count;
integer i;
	begin
		count=0;
		for(i=0;i<=7;i=i+1;)
			if(x[i]==1'b0)
				count=count+1;
		out0=count;
	end
endmodule

Pay attention to the following points when defining functions
(1) Like tasks, function definition structures can only appear in modules, not in process blocks.
(2) A function must have at least one input port.
(3) The function cannot have any type of output port (output port) and bidirectional port (inout port).
(4) Any type of time control description cannot appear in the behavior statement part of the function definition structure, and the disable termination statement is not allowed.
(5) Same as the task definition, no process block can appear inside the function definition structure.
(6) Other functions can be called within a function, but the function cannot call other tasks.
(7) A list of port names cannot appear in the first line of the "function" statement.
(8) When the function is declared, a register type variable named function_identifier (function identifier) ​​is implicitly declared inside Verilog, and the output of the function will be passed back through this register type variable.

2. Function Calls
Function calls are made by using functions as operands in expressions. The calling format of the function is as follows:
<函数名>(<输入表达式1>,<输入表达式2>,...,<输入表达式n>);
Among them, the input expressions should correspond to the input ports described in the function definition structure one by one, and they represent the input data of each input port.
Pay attention to the following points when calling a function:
(1) The calling of a function cannot appear as a single statement, it can only appear in the calling statement as an operand.
(2) The function call can appear not only in the process block, but also in the assign continuous assignment statement.
(3) All local registers declared in the function definition are static, that is, the local registers in the function maintain their values ​​across multiple calls of the function.

module tryfact_tb; 
function[31:0] factorial;
	input[3:0] operand; 
	reg[3:0] index; 
	begin
		factorial = 1;
		for(index=1;index<=operand;index=index+1)
		factorial = index * factorial; 
	end
endfunction 
reg[31:0] result;
reg[3:O] n; 
initial
	begin
		result=1;
		for(n=1;n<=9;n=n+1)
			begin
				result = factorial(n);
				$display("n= %d result= %d", n, result);
			end
	end
endmodule

The above example is composed of function definition and initia|procedure block, which defines a function named factorial, which is a function for factorial operation, has a 4-bit input port, and returns a 32-bit register type value at the same time; In the initial block, two register variables are defined as 32-bit result and 4-bit n. The initial block performs factorial operation on 1 to 9, and prints out the result value. 
insert image description here

5.3 The difference between tasks and functions

insert image description here

6. Design of typical test vectors

6.1 Variable initialization

In the Verilog language, there are two ways to initialize variables, one is to use initialization variables, and the other is to directly assign and initialize variables when defining them. These two initialization tasks are not synthesizable and are mainly used in the simulation process.
(1) Initial initialization method
In most cases, the work of variable initialization in Testbench is completed through the initial process block, which can generate rich simulation incentives.
The initial statement is only executed once, that is, when the design is started to simulate execution (0 time) until the end of the process, it is specially used to initialize the input signal and generate a specific signal waveform. A Testbench can contain multiple initial procedure statement blocks, and all initia|procedures are executed at the same time. It should be noted that the variables in the initial statement must be of type reg.

Example: Test vector output using the initial initialization method

module counter(clk,cnt);
	output [3:0] cnt;
	reg clk;
	reg [3:0] temp;
	initial temp = 0;
	initial clk = 0;
endmodule

(2) Initialization when defining variables The syntax for initializing variables when defining variables is very simple, just use "=" to assign a value at the right end of the variable, such as: reg [7:0]
cnt=8'b00000000;
the 8-bit register variable cnt is initialized to 0.

6.2 Data signal test vector generation

There are two forms of data signal generation: one is that initialization and generation are performed in a single initial block; the other is that initialization is completed in the initial statement, and generation is completed in the always statement block. The former is suitable for irregular data sequences and requires a shorter length; the latter is suitable for data sequences with certain regularities and has no length limit.

Example: Generate a prime number sequence {1, 2, 3, 5, 7, 11, 13} with a bit width of 4, and repeat it twice, where the sample value interval is 4 simulation time units
. Since this sequence has no obvious rules, use The Initial statement is most appropriate.

`timescale 1ns/1ps 
module sequence_tb;
	reg [3:0] qout; 
	parameter sample_period = 4; 
	parameter queue_num = 2; 
	initial
		begin
			q_out =0;
			repeat( queue_num) 
				begin
					# sample_period q_out = 1;
					# sample_period q_out = 2;
					# sample_period q_out = 3;
					# sample_period q out = 5;
					# sample_period q_out = 7;
					# sample_period q_out= 11;
					# sample_period q_out = 13;
				end
	end
e ndmodule

insert image description here

6.3 Clock signal test vector generation

Example: Generate a clock signal with a duty cycle of 50%
insert image description here(1) The method based on the initial statement

module clk1(clk);
 output clk;
 parameter clk_period = 10;
 reg clk;
 initial
 	begin
 		clk = 0;
 		forever #(clk_period/2) clk = ~clk;
 	end
 endmodule

(2) Method based on always statement

module clk2(clk);
 output clk;
 parameter clk_period = 10;
 reg clk;
 initial clk = 0;
 always #(clk_period/2) clk = ~clk;
endmodule

6.4 Bus signal test vector generation

The bus is a common channel for data flow between computing components. In RTL-level descriptions, a bus refers to a shared vector driven by a logic unit, register, memory, circuit input, or other bus. The bus function model is a bus model that converts the physical interface timing operation into a higher abstract level interface, as shown in the figure:
insert image description here
In the bus, for each request end, there is an input to select and drive the corresponding request of the bus end. Selecting more than one requester creates a bus conflict, which can have different results depending on the type of bus. When there are multiple requesting ends sending requests, the corresponding operation is determined by the type of the bus. In the Verilog test, the bus test signal is usually described by the chip select signal, read (or write) enable signal, address signal and data signal in the form of a task task, and is completed by calling the bus signal test vector in the form of task corresponding bus functions.
The following takes the AHB bus write operation with a working frequency of 100MHz as an example to illustrate the method of generating bus signal test vectors in the task mode. The following figure is the timing diagram of the AHB bus write operation, in which the chip select and write enable signals are deactivated (active low) after the data write operation is completed.

insert image description here
Example: Generate a set of AHB bus function models with write operations

module bus_wr_tb; 
reg clk;
reg cs; 
reg wr;
reg [31:0] addr; 
reg [31:0] data:
initial
	begin
		cs=1'b1;
		wr=1'b1;
		#30;
		bus_wr(32'h1100008a, 32’h11113000); 
		bus_wr(32’h1100009a, 32'h11113001);
		bus_wr(32'h110000aa, 32'h11113002); 
		bus_wr(32'h110000ba, 32’h11113003); 
		bus_wr(32'h110000ca, 32’h11113004); 
		addr=32’bx; 
		data=32’bx;
end
initial clk=1;
always #5 clk=~clk;
task bus_wr;
 input [31:0] ADDR;
 input [31:0] DATA;
 begin
 	cs=1'b0;
 	wr=1'b0;
 	addr = ADDR;
 	data = DATA;
 	#30 cs=1'b1;
 	wr=1'b1;
 end
endtask
endmodule

7. User-defined component model UDP

Through UDP, a combinational logic circuit or sequential logic circuit can be encapsulated in a UDP, and this UDP can be used as a basic gate element. It should be noted that UDP cannot be synthesized and can only be used for simulation.

7.1Definition and call of UDP

The definition format of UDP is as follows:

primitive <元件名称>(<输出端口名>,<输入端口名1 >,<输入端口名2>,...,<输入端口名n>)
 输出端口类型声明(output);
 输入端口类型声明(input);
 输出端口寄存器变量说明(reg);
 元件初始状态说明(initial); 
 table
	<table表项1>;
	<table表项2>;
	...
	<table表项n>;
 endtable 
endprimitive

Compared with modules in Verilog, UDP has the following characteristics.
(1) There can only be one UDP output port, and it must be the first item in the port list. Only output ports can be defined as reg type.
(2) There can be multiple input terminals of UDP, the input ports of general sequence circuit UDP can be up to 9, and the input ports of combined circuit UDP can be up to 10.
(3) The bit width of all port variables must be 1 bit.
(4) In the table entry, only three states of 0, 1, and x can appear, and z will be considered as the state of x.

According to the basic logic functions contained in UDP, UDP can be divided into combination circuit UDP, sequential circuit UDP and hybrid circuit UDP. The differences between these types of UDP are mainly reflected in the description of table entries.
The calling method of UDP is similar to the calling method of modules in Verilog. Through location mapping, its syntax format is as follows:
UDP名 例化名(连接端口1信号名,连接端口2信号名,连接端口3信号名,...);

Example: Define a full adder simulation model using UDP

primitive summ(sum, cin, a, b);//本位和
 output sum;
 input a,b,cin;
	table
  	  //cin a b : sum
		 0 0 0 : 0;
		 0 0 1 : 1;
		 0 1 0 : 1;
		 0 1 1 : 0;
	 	 1 0 0 : 1;
		 1 0 1 : 0;
		 1 1 0 : 0;
	 	 1 1 1 : 1;
	endtable
endprimitive

primitive summ(sum, cin, a, b);//进位
 output cout;
 input a,b,cin;
	table
  	  //cin a b : sum
		 0 0 0 : 0;
		 0 0 1 : 0;
		 0 1 0 : 0;
		 0 1 1 : 1;
	 	 1 0 0 : 0;
		 1 0 1 : 1;
		 1 1 0 : 1;
	 	 1 1 1 : 1;
	endtable
endprimitive

7.2UDP Application Examples


(1) The function list of combinational circuit UDP component combinational logic circuit is similar to the truth table, which specifies different input values ​​and corresponding output values. Each row in the table is in the form of output, input1, input2,..., the order of arrangement and the list of ports In the same order, the input port of the combined circuit UDP can be up to 10. If there is no defined output for a combination of inputs, set the output for that case to x.

Example: 3 to 1 multiplexer

primitive mux3_1(Y,in0,in1,in2,s2,s1);
 input in0,in1,in2,s2,s1;
 output Y;
 table
 	//in0 in1 in2 s2 s1 : Y
 	   0   ?   ?  0  0  :  0;//当s2s1=00时,Y=in0
 	   1   ?   ?  0  0  :  1;
 	   ?   0   ?  0  1  :  0;//当s2s1=01时,Y=in1
 	   ?   1   ?  0  1  :  1;
 	   ?   ?   0  1  ?  :  0;//当s2s1=1?时,Y=in2
 	   ?   ?   1  1  ?  :  1;
 	   0   0   ?  0  ?  :  0;
 	   1   1   ?  0  ?  :  1;
 	   0   ?   0  ?  0  :  0;
 	   1   ?   1  ?  0  :  1;
 	   ?   0   0  ?  1  :  0;
 	   ?   1   1  ?  1  :  1;
 endtable
endprimitive

insert image description here
(2) Sequential circuit UDP component
UDP can also describe a sequential circuit with level-triggered and edge-triggered characteristics. A sequential circuit has an internal state sequence, and its internal state must be modeled with a register variable. The value of the register is the current state of the sequential circuit, and its next state is determined by the state transition table placed in the primitive function list. And the next state of the register is the output value of this sequential circuit UDP. Therefore, the sequential circuit UDP consists of two parts: a status register and a status list. The work of defining timing UDP is also divided into two parts—initializing the status register and describing the status list.
In the UDP description of the sequential circuit, 01, Ox, and xl represent the rising edge of the signal. A UDP development example of a rising edge D flip-flop is given below.

Example: Give the D flip-flop UDP description through Verilog, and call the UDP component in the module

primitive D_Edge(Q,Clk,Data);
 output Q;
 reg Q;
 input Data,Clk;
 initial Q = 0;
 table
   //Clk Data : Q(Stata): Q(next)
 	(01)   0  :    ?    :    0;
 	(01)   1  :    ?    :    1;
 	(0x)   1  :    1    :    1;
 	(0x)   0  :    0    :    0;
 	(?0)   ?  :    ?    :    -;// 忽略时钟负边沿
 	?   (??)  :    ?    :    -;// 忽略在稳定时钟上的数据变化
 endtable
endprimitive;

The entry (01) represents the transition from 0 to 1, the entry (0x) represents the transition from 0 to x, the entry (?0) represents the transition from any value (0, 1, or x) to 0, and the entry (?? ) means any conversion, and the output defaults to x. Assuming D_Edge is defined for UDP, it can now be used in modules just like basic gates.

(3) Mixed circuit UDP components
can mix level-triggered and edge-triggered items in the same table. In this case, edge changes are processed before level triggers, i.e. level trigger items override edge trigger items. A UDP description of a D flip-flop with asynchronous clear is given below.
Example: Use Verilog language to complete the UDP description of asynchronous clear D flip-flop

primitive D_Async_FF(0, Clk, Clr, Data); 
 output Q;
 reg Q; 
 input Clk, Data, Clr;
 table 
	//Clk Clr Data : (SQtate) : Q(next)
	  (01) 0    0  :     ?    :    0;
	  (01) 0    0  :     ?    :    0;
	  (0x) 0    1  :     1    :    1;
	  (0x) 0    0  :     0    :    0;
	  (?0) 0    ?  :     ?    :    -;
	  (??) 0    ?  :     ?    :    -;
	   ?   1    ?  :     ?    :    0;
	endtable
endprimitive

8. Delay modeling of basic gate-level components and modules

8.1 Gate-Level Delay Modeling

The gate-level delay can be divided into the following categories:
(1) "Rising delay": It means the gate transmission delay when the signal changes from "x" or "z" state to "1" state.
(2) "Falling delay": Indicates the gate transmission delay when the signal changes from "1", "x" or "z" state to "0" state.
(3) "Delay to indeterminate state": Indicates the gate transmission delay when the signal changes from "0", "1" or "z" state to "x" state.
(4) "Cut-off delay": Indicates the gate transmission delay when the signal changes from "0", "1" or "x" state to "z" state.

1. The basic delay expression form of gate-level delay
In the basic expression form of gate-level delay, there can be 0 to 3 delay values ​​in "delay". The following table shows the number of different delay values ​​specified , 4 expressions of delay.
insert image description here
(1) When "delay" does not specify a gate delay value, the default delay value is 0. This means that the "Rise Delay Value", "Fall Delay Value", "Off Delay Value" and "Delay to Unsteady Value" of the component instance are all 0.
In the example: notif1 U0(out, in, ctrl);
the gate-level delay value is "0", because there is no delay defined, the "rising delay value", "falling delay value", "cut-off delay" of the component instance UO Time value" and "Delay value to indeterminate state" are both 0.
(2) When "delay" contains only one delay value, the given delay value "d" will simultaneously represent the "rising delay value", "falling delay value", "cut-off delay value" of the component instance time value" and "delay value to indeterminate state". In the following example reference,
notif1 #20 U1(out, in, ctrl);
The gate-level delay value is "20", and only contains 1 delay value, indicating all types of gate-level delays of the component instance U1 Both are 20 units of time.
(3) When "delay" contains 2 delay values, the "rising delay value" of the component instance is specified by the given "d1"; the "falling delay value" is specified by the given "d2"; And the "cut-off delay value" and "delay value to indeterminate state" will be specified by the minimum value of "dl" and "d2". In the example:
notif1 #(10,20)U2(out, in,

(4) When "delay" contains 3 delay values, the "rising delay value" of the component instance is specified by the given "dA"; the "falling delay value" is specified by the given "dB";" The cut-off delay" is specified by a given "dC'; while its "delay to an indeterminate state" will be specified by the minimum of dA, dB, and dC. In the instance: notif1 #(10,20,30)U3
( out, in, ctrl);
the gate-level delay value is "00, 20, 30)", which contains three delay values ​​10, 20 and 30, which indicates that the component instance IJ3 has a "rise delay of 10 unit time ", 20 units of "falling delay" and 30 units of "cut-off delay", and its "delay to indeterminate state" will be specified by the minimum value of 10, 20
and 30, that is 10 units of time.

2. The minimum, typical, and maximum delay expressions of gate-level delay
In addition to the basic delay expression, the gate-level delay can also use the "minimum, typical, and maximum" delay expression. In this expression , each item in the gate-level delay will be represented by two values ​​of "minimum delay", "typical delay" and "maximum delay", and its syntax format is as follows:
#(d min:d_typ:d_max)

When the "minimum, typical, maximum" delay expression is used, delay" can contain 1 to 3 delay values, such as the following situations:

#(dA_min: dA_typ: dA_max)
#(dA_min: dA_typ: dA_max, dB_min: dB_typ: dB_max) #(dA_min :dA_typ:
dA_max, dB_min: dB_typ: dB_max, dC_min: dC_typ: dC_max)

In the example: and #(4:5:6) U1(out,i1,i2);
"delay" contains only 1 delay value, the minimum value is 4, the typical value is 5, and the maximum value is 6. The "rising delay value", "falling delay value", "cut-off delay value" and "delay value to indefinite state" of the component instance UI are as follows:

minimum delay rise delay=4 Fall Delay: 4 Delay to indefinite state = 4 Off Delay: 4
typical delay Rise Delay: 5 fall delay=5 delay to indeterminate Off Delay: 5
maximum delay rise delay=6 fall delay=6 delay to indeterminate OFF DELAY = 6

In the example: and #(3:4:5,5:6:7)U2(out, i1,i2);
"delay" contains 2 delay values, the minimum value of the first delay value is 3, typical The value is 4, the maximum value is 5, the minimum value of the second delay value is 5, the typical value is 6, and the maximum value is 7. The "rising delay value" of component instance U2 is specified by the first delay value, the falling delay value is specified by the second delay value, the "delay value to indeterminate state" and the "cut-off delay value" are both It is specified by the minimum value of the two delay values. The values ​​of each value are as follows:

minimum delay rise delay=3 fall delay=5 Delay to indefinite state=min(3,5) Turn off delay=min(3,5)
typical delay rise delay=4 fall delay=6 Delay to indefinite state=min(4,6) Turn off delay=min(4,6)
maximum delay rise delay=5 Falling delay=7 Delay to indefinite state=min(5,7) Turn off delay=min(5,7)

and # (2:3:4,3:4:5,4:5:6) U3(out,i1,i2);

"delay" contains three delay values. The minimum value of the first delay value is 2, the typical value is 3, and the maximum value is 4. The minimum value of the second delay value is 3, the typical value is 4, and the maximum value is 5, the minimum value of the third delay value is 4, the typical value is 5, and the maximum value is 6. The "rising delay value" of component instance U3 is specified by the first delay value, the "falling delay value" is specified by the second delay value, the "cut-off delay value" is specified by the third delay value, and Its "delay to indefinite state" is specified by the minimum of the three delay values. The value of each value is shown in the table below.

minimum delay rise delay=2 fall delay=3 Delay to indefinite state=min(2,3,4) off-delay=4
typical delay rise delay=3 fall delay=4 Delay to indefinite state=min(3,4,5) off-delay = 5
maximum delay rise delay=4 fall delay=5 Delay to indefinite state=min(4,5,6) OFF DELAY = 6

Example: Use Verilog to build the delay simulation module of module D.
Its gate-level implementation is as follows. The logic diagram of module D is as follows, which includes an AND gate with a delay time of 5 units of time and an OR gate with a delay time of 4 units of time. .
insert image description here
Module D code with delay:

module D(out,a,b,c);
 output out;
 input a,b,c;
 wire e;
 and #(5) a1(e,a,b); 
 or #(4) o1(out, e,c);
endmodule

Test stimulus module for module D with time delay:

module D_tb;
 reg A,B,C;
 wire OUT;
 D d1(.out(OUT), .a(A), .b(B), .c(C));
 initial
 	begin
 		     A=1'b0;B=1'b0;C=1'b0;
 		#10  A=1'b1;B=1'b1;C=1'b1;
 		#10  A=1'b1;B=1'b0;C=1'b0;
 		#20  $finish;
 	end
 endmodule

insert image description here

8.2 Module Delay Modeling

1. Delay Description Block Specify Block
The delay between the input and output pins of the module is called the module path delay. In Verilog, the path delay is assigned between the keywords specify and endspecify, and the statements between the keywords form the specify block (that is, the specified block). "specify" and "endspeclfy" are the start identifier and end identifier of the delay specification block, respectively.

The Specify block contains the following operational statements:
(1) Define all path delays through the block
(2) Set timing checks in the circuit
(2) Define specparam constants

insert image description here
Example: Take the above figure as an example, use the specify block to describe the path delay of the M module on the way. code show as below:

module M(out,a,b,c,d);
 input a,b,c,d;
 output out;
 wire e,f;
 assign out=(a&b)|(c&b);//逻辑功能
 specify //包含路径延迟语句的specify块
 	(a=>out)=9;
 	(b=>out)=9;
 	(c=>out)=11;
 	(d=>out)=11;
 endspecify;
 endmodule

2. Path delay description method
(1) Parallel connection
Each path delay statement has a source domain or a target domain. In the path delay statement in the example above, a, b, c, and d are in the source domain location, and out is the destination domain.
In the specify block, use the symbol "=>" to describe the parallel connection, and its syntax format is as follows:
(<source_field>=><destlnation_field>)=<delay_value>;
where <delay_value> can contain 1 to 3 delay values, and can also use the "minimum, typical, maximum" delay expression. In the case that the delay value is composed of multiple values, a pair of brackets should be added outside the delay value, such as
(a=>out)=(8,9,10);
it means the input a to output b The delay minimum, typical and maximum delays are 8, 9, 10 time units respectively.

In a parallel connection, each bit in the source field is connected with the corresponding bit in the destination field. If the source and destination fields are vectors, they must have the same number of bits, or a mismatch will occur. Thus, a parallel connection accounts for the delay between each bit of the source domain and each bit of the destination domain.
The diagram below shows how the bits between the source and target domains are connected in parallel. Simultaneous example gives the Verilog description of parallel connection.

insert image description here
(2) Full connection
In the specify block, the symbol "*>" is used to represent the full connection, and its grammatical format is as follows:
(<source_field>*><destination_field>)=<delay_value>;
In the full connection, each bit in the source domain is connected to each bit in the target domain. If source and destination are vectors, they do not have to have the same number of bits. A full connection describes the delay between every bit in the source and every bit in the destination, as shown in the following diagram:

insert image description here
3. The spacparam declaration statement
spacparam is used to define the parameters in the specify block, as shown in the following example using the specify block example of the spacparam statement

module parallel_connected(out, a, b); 
 input a, b;
 output out; 
 wire out; 
 assign out=a&b; //逻辑功能
 //在指定块内部定义参数
specify
 specparam a_to_out = 9; 
 specparam b_to_out = 11;
 (a => out) = a_to_out;
 (b => out) = b_to_out; 
endspecify
endmodule

The format and function of the specparam statement are similar to the parameter specification statement, but they are different:
(1) The specparam statement can only appear in the delay specification block (specify block); while the parameter statement cannot appear in the delay specification block appear within.
(2) The parameters defined by the specparam statement can only be delay parameters; while the parameters defined by the parameter statement can be constant parameters of any data type.
(3) The delay parameters defined by the specparam statement can only be used in the delay description block; while the parameters defined by the parameter statement can be used anywhere in the module.
The specify parameter is provided in the module to facilitate the assignment of the delay. It is recommended to use the specify parameter instead of the value to indicate the delay. In this way, if the timing specification of the circuit changes, the user only needs to change the value of the specify parameter instead of modifying the delay value of each path one by one.

8.3 System Tasks Related to Timing Checks

insert image description here
insert image description here
insert image description here

9. Compile prepared statements

Compilation preprocessing is an integral part of the Verilog compilation system, which means that the compilation system will preprocess some special commands, and then perform the usual compilation process together with the preprocessing results and the source program. Certain identifiers beginning with "`" (backtick) are compiled prepared statements. When compiling in the verilog language, specific compiler directives are valid throughout the compilation process (the compilation process can span multiple files), until other different compiler directives are encountered. Commonly used compiler preprocessing statements are as follows:
insert image description here

9.1 Macro definition

The `define command is a macro definition command, which can increase the readability and maintainability of the Verilog HDL code by representing a string with a specified identifier, and find out where the parameters or functions are incorrect or not allowed.
The `define directive is like the #define directive in C language, which can be defined inside or outside the module. When the compiler encounters this statement during compilation, it will replace the macro text with the macro name.
The declaration syntax format of `define is as follows:
`define <macro_name><Text>
For the declared syntax, the application format in the code is as follows, do not miss the "`" before the macro name.
`macro_name
eg:
`define MAX_BUS_SIZE 32
...
reg [`MAX_BUS_SIZE-1:0] AddReg;

Once a `define directive is compiled, it remains in effect throughout the compilation. For example, MAX_BUS_SIZE can be used by multiple files by `define directive in another file.
The `undef directive cancels a previously defined macro. For example:
`define WORD 16 //Create a text macro to replace
...
wire[`WORD:1] Bus;
...
`undef WORD //After the `undef compilation instruction, the macro definition of WORD no longer exists

Regarding macro definition instructions, there are the following 8 rules that need attention.
(1) The name of the macro definition can be in uppercase or lowercase, but be careful not to overlap with the variable name.
(2) Like all compiler directives, macro definitions are still valid when they exceed the boundaries of a single file (for other source files in the project), unless they are overwritten by the following `define, `undef or `resetall directives, otherwise `define Not limited by scope.
(3) When a macro is defined with a variable, the variable can be used in the body of the macro, and can be replaced by an actual variable expression when using the macro.
(4) By escaping the middle newline character with a backslash "\", the macro definition can span several lines, and the new line is part of the macro text.
(5) There is no need to add a semicolon at the end of the macro definition line to indicate the end.
(6) Language tokens that cannot be separated from the macro body include comments, numbers, character strings, reserved keywords, and operators.
(7) Compiler directives are not allowed as macro names.
(8) The text of a macro definition can also be an expression.

There is a difference between `define and parameter. Both `define and parameter can be used to complete text replacement, but they are essentially different. The former is preprocessed before compilation, while the latter is replaced during normal compilation. In addition, `define and parameter have the following two points of difference.
(1) The scope is different: parameter acts on the declared file; `define is valid from the time the compiler reads this instruction to the end of compilation, or it can be applied to the entire project when it encounters the `undef command to make it invalid. If you want the parameter to act on the entire project, you can write the declaration statement in a separate file, and use `include to make each file include the declaration file.
`define can also be written anywhere in the code, while parameter must be defined before application. Usually the compiler can define the compilation order, or start compiling from the lowest module, so it is enough to write at the lowest level.
(2) The transfer function is different: parameter can be used as a parameter transfer during module instantiation to realize parameterized calls: the `define statement does not have this function. The `define statement can define expressions, while parameter can only be used to define variables.
insert image description here

9.2 File inclusion processing

The so-called "file inclusion processing" means that a source file can include the entire content of another source file, that is, include another file into this file. The Verilog language provides the `include command to implement the "file inclusion" operation. Its general form is: include "filename".
insert image description here
insert image description here

9.3 Simulation Time Scaling

insert image description here
In this command, the time unit parameter is used to define the base unit of simulation time and delay time in the module. The time precision parameter is used to declare the accuracy of the simulation time of the module, and this parameter is used to round the delay time value (before simulation), so this parameter can also be called the rounding precision. If there are multiple `timescale commands in the same program design, the smallest time precision value is used to determine the time unit of the simulation. In addition, the time precision must be at least as precise as the time unit, and the time precision value cannot be greater than the time unit value.
In the `timescale command, the number used to describe the time unit and time precision parameter value must be an integer, the valid number is 1, 10, 100, and the unit is second (s), millisecond (ms), microsecond (us), Nanosecond (ns), picosecond (p division, millipicosecond <fs). The following example illustrates the usage of `timescale command.
Example Simulation time scale example (1) All time values ​​in
`timescale 1ns/1ps module represent integer multiples of 1ns.
This is because in the `timescale command, the defined time unit is 1ns. Because the `timescale command defines the time precision as 1ps, the delay time in the block can be expressed as a real number with three decimal places.
(2) `timescale 10us/100ns
In this example, the time values ​​in the module are all integer multiples of 10us. Because the time unit defined by `timesacle command is 10us. The minimum resolution of the delay time is one tenth of a microsecond (100ns), that is, the delay time can be expressed as a real number with one decimal place.
insert image description here

9.4 Conditional Compilation Commands

insert image description here

9.5 Other Statements

insert image description here
insert image description here
insert image description here

10. Introduction to Verilog test methods

In the field of integrated circuit testing, commonly used testing methods include complete testing, random testing and automatic testing.
(1) Complete testing method For complex designs, it is often an important method to check whether the verification work is completed by checking the coverage of the code. Code coverage indicates how much of the functionality described by the Verilog code has been verified during simulation. Usually code coverage includes the following:
·Statement coverage
·Path coverage
·State machine coverage
·Trigger coverage
·Expression coverage
(2) The random test method
provides multiple system commands for random testing in Verilog , the system function of random test is usually used to simulate the real application situation, such as the frame synchronization search circuit commonly used in the communication field needs to detect a special code pattern fixedly inserted by the sending end from the received data stream, and the data itself also has This pattern may be included, in which case the randomized test is closer to the real application. One of the most commonly used system commands is the random number generation system task $random.
(3) Automatic testing method
In the field of integrated circuit testing, comprehensive and accurate testing can ensure the normal operation of large-scale integrated circuits, and the only way to ensure that a design can be fully tested is to automate tasks. Usually by creating a check table, using the corresponding number of sampled values. After modifying the source code, all test programs are automatically re-executed. But be aware that there may be truncation errors using automated tests.

Source: Teacher Cai Jueping's Verilog course

Guess you like

Origin blog.csdn.net/KIDS333/article/details/127101378