1 Simulation Verification Overview
Simulation testing is an essential step in the FPGA design process. Especially today, as the scale and design complexity of FPGAs continue to increase, the easy work of drawing a simple schematic or writing a few lines of code to debug on the board is gone forever. The workload of a formal design that needs to be spent on verification may often account for about 70% of the entire development process . Verification is usually divided into simulation verification and board-level verification. Before the design is initially completed or even about to be debugged on the board, it is a very effective and feasible means to simulate the actual application through EDA simulation tools for verification. It can find various bugs of varying sizes in the design as early as possible , avoiding rework at the last step of the design. Therefore, the importance of simulation in the entire verification is evident.
When we talk about simulation, we usually mention the concept of testbench . The so-called testbench is a test platform. In detail, it is to add stimulus to the design to be verified and observe whether its output response meets the design requirements. As shown in the figure, the testbench is to simulate various peripheral devices connected to the design to be verified.
Graph Design and Verification
When beginners first come into contact with the concept of simulation, they may think that simulation is simply to use some waveform generators that come with some development software to generate some stimuli, and then observe the final waveform output. However, for large-scale designs, it is unrealistic to use waveforms to generate stimuli, and the workload of observing waveforms can be imagined. For example, for a 16 -bit input bus, it can have 65536 combinations. If one input is randomly generated each time, wouldn’t it be exhausting to use waveforms? In addition, the observation of output results, corresponding to 65536 kinds of inputs and 65536 kinds of outputs, it must be dazzling to look at the waveform. Therefore, the testbench should have more efficient testing methods. For FPGA simulation, it is possible to use waveform input to generate stimuli, and it is also possible to observe the waveform output to verify the test results. The waveform may be the most intuitive testing method, but it is by no means the only method.
As shown in the figure, the test result of the design can be judged not only by observing and comparing waveforms, but also by flexibly using script commands to print useful output information to the terminal or generate text for observation, or by writing a piece of code to let them automatically compare the output results. In short, the design of testbench is diverse, and its syntax is also very casual. It is not as particular as RTL- level design code. It is based on behavioral-level syntax, and many advanced syntaxes can be used in scripts. Because it does not need to be implemented in hardware, it is a script running on a PC, so it can be made easier and more flexible than RTL level. However, there are many things that designers need to pay attention to when using Verilog verification scripts. It is a language based on hardware language but serves software testing, so it often wanders between parallelism and sequence, which makes people puzzled. However, as long as these key points are mastered, it can be well used for our testing.
Figure Verification Output
2 Testbench Design
Testbench writing is actually not as mysterious as imagined. The author simply summarizes it into 3 steps.
①Instantiate the top-level interface of the design under test.
②Add stimulus to the input interface of the design under test.
③ Determine whether the output response of the tested design meets the design requirements.
Relatively speaking, the last step is more complicated. Sometimes it is not necessarily just a simple output observation, but may also require feedback of some input values to the design to be tested.
The purpose of instantiation is to connect the design to be tested with the testbench , which is the same concept as the instantiation inside the FPGA . So how to instantiate? The following is a simple example to illustrate.
// Design to be tested
module fpga_design(
clk,rst_n,a,b,c,d
);
input clk;
input rst_n;
input a,b,c;
output d;
always @(posedgeclk or negedgerst_n) begin
if(!rst_n) d <= 1'b0;
else d <= a & b & c;
end
endmodule
For the above design to be tested, the instantiation in the testbench should convert the input to reg , because the input value of the design to be tested is determined by the testbench . The corresponding output should be converted to wire , because the output value of the design to be tested is not determined by the testbench . If it is an inout port, it is also a wire type in the instantiation , and it is the same when used in the testbench as it is in the RTL code design.
// Instantiate the design to be tested
reg clk;
reg rst_n;
reg a,b,c;
wire d;
fpga_design(
.clk(clk),
.rst_n(rst_n),
.a(a),
.b(b),
.c(c),
.d(d)
);
For the generation of stimulus, only the most basic clock signal and reset signal are mentioned. There are many ways to generate clock signals, and both initial and always statements are acceptable. The following lists two typical generation methods for your reference.
// Clock generation
parameter PERIOD = 20; // Define the clock period to be 20ns , and "timescale 1ns/1ps" has been defined
initial begin
clk = 0;
forever
#(PERIOD/2) clk = ~clk;
end
// Clock generation
parameter PERIOD = 20; // Define the clock period to be 20ns , and "timescale 1ns/1ps" has been defined
always begin
#(PERIOD/2) clk = 0;
#(PERIOD/2) clk = 1;
end
The generation of a reset signal is also very simple. The more common approach is to encapsulate it into a task and call it directly when a reset is needed.
// Reset generation
initial begin
reset_task(100); // Reset 100ns , "timescale 1ns/1ps" has been defined
…
end
task reset_task;
input[15:0] reset_time; // Reset time
begin
reset = 0;
#reset_time;
reset = 1;
end
endtask
As for how to observe and process the test response , I will not go into too much detail here . If you pick up any book on Verilog syntax, you will find the corresponding behavioral level syntax section, and you can learn it just by following the instructions.
For this simple design, there are three inputs a , b and c , and their AND result d outputs the latest result every clock cycle. Therefore, we can foresee that if we want to fully cover the test branches of this design, we need to generate 8 different test items, that is, change the values of a , b and c respectively , and observe whether their output results meet expectations. The writing of the test script is shown below.
// Reset generation
timescale 1ns/1ps
module tb_fpga_design;
// Instantiate the design to be tested
reg clk;
reg rst_n;
reg a,b,c;
wire d;
fpga_design(
.clk(clk),
.rst_n(rst_n),
.a(a),
.b(b),
.c(c),
.d(d)
);
initial begin
reset_task(100); // Reset 100ns , "timescale 1ns/1ps" has been defined
@(posedgeclk); #2;
a = 1'b0;
b = 1'b0;
c = 1'b0;
@(posedgeclk); #2;
a = 1'b0;
b = 1'b0;
c = 1'b1;
@(posedgeclk); #2;
a = 1'b0;
b = 1'b1;
c = 1'b0;
@(posedgeclk); #2;
a = 1'b0;
b = 1'b1;
c = 1'b1;
@(posedgeclk); #2;
a = 1'b1;
b = 1'b0;
c = 1'b0;
@(posedgeclk); #2;
a = 1'b1;
b = 1'b0;
c = 1'b1;
@(posedgeclk); #2;
a = 1'b1;
b = 1'b1;
c = 1'b0;
@(posedgeclk); #2;
a = 1'b1;
b = 1'b1;
c = 1'b1;
@(posedgeclk); #2;
$stop;
end
task reset_task;
input[15:0] reset_time; // Reset time
begin
reset = 0;
#reset_time;
reset = 1;
end
endtask
// Clock generation
parameter PERIOD = 20; // Define the clock period to be 20ns , and "timescale 1ns/1ps" has been defined
always begin
#(PERIOD/2) clk = 0;
#(PERIOD/2) clk = 1;
end
endmodule
Use this script to simulate the design and observe the output results to see if the output is consistent with the expected results under 8 different design input conditions. If it is consistent, you can continue with the subsequent design process to complete the design. If it is not consistent, there must be a problem in the design. You need to find the cause of the problem and modify the design until the simulation results reach the expected results.
3. Create simulation files
Take zstar_ex01 as an example, as shown in the figure, select Project Manager -> Simulation Sources -> sim_1 , right-click the pop-up menu, and select Add Sources… .
Figure 1. New Simulation File Menu
Then use the default options Add or create simulation sources as shown .
Figure Select File Type
Click the Create File button as shown .
Figure 1. New design file
As shown in the figure, set the created file type ( File type ) to Verilog ; the file name ( File name ) to sim_zstar ; and the file location ( File location ) to the default <Local to Project> .
Figure 1. Setting the new file name and path
Finally, click the Finish button to complete the creation of the simulation test file.
Then the module port definition interface pops up, you can just click OK without making any settings.
Graph module port definition
As shown in the figure, double-click the sim_zstar.v file just created under Simulation Sources . There is only a Verilog module framework in it, waiting to be filled with content.
Figure 1. The created simulation test source file
As shown in the figure, write the simulation test script of this instance into this file.
Figure simulation test script
4 Functional simulation
Next, let's see how to use the simulation tool provided by Vivado to implement functional simulation. As shown in the figure, click Project Manager -> Simulation -> Simulation Settings , and in the pop-up tab, set the target simulator ( Target Simulator ) to Vivado Simulator , the simulation language ( Simulation language ) to Mixed , the simulation set ( Simulation set ) to sim_1 , and the simulation top module name ( Simulation top module name ) to sim_zstar .
Figure Simulation Settings
As shown in the figure, click Run Simulation , and then click Run Behavioral Simulation in the pop-up menu to perform functional simulation.
Figure 1.1.1 Functional simulation of running
The pop-up simulation interface is shown in the figure. The three windows from left to right are the module and hierarchy display window, the signal display window and the waveform window.
Figure simulation interface
As shown, click the Run All button.
Figure Simulation Run Button
At this point, as shown in the figure, the simulation is running.
Figure simulation is running
As shown in the figure, you can click the Float button in the upper right corner of the waveform interface to separate the waveform interface from Vivado , so that the waveform can be displayed full screen and more waveform details can be seen.
Figure Floating Simulation Waveform Interface
As shown in the figure, after the simulation is completed, we can click the Zoom Fit button to zoom all simulation waveforms into the visual interface.
Figure 1 Overview of Simulation Waveform
As shown in the figure, this is the waveform of the project simulation. The beep signal is always a 1Hz PWM signal with a 10% duty cycle .
Figure 2 Simulation waveform
This content is originally created by EEWORLD forum user ove . If you want to reprint or use it for commercial purposes, you must obtain the author's consent and indicate the source