Tasks, Functions, and Testbench - 2020 (Autosaved)
Tasks, Functions, and Testbench - 2020 (Autosaved)
Testbench
Lab Workbook
Introduction
• Verilog lets you define sub-programs using tasks and functions. They
are used to improve the readability and to exploit re-usability code.
• Functions are equivalent to combinatorial logic and cannot be used to
replace code that contains event or delay control operators (as used
in a sequential logic).
• Tasks are more general than functions, and may contain timing
controls.
• Testbench is a program or model written in any language for the
purposes of exercising and verifying the functional correctness of a
hardware model during the simulation.
Introduction
• Verilog is primarily a means for hardware modeling (simulation), the
language contains various resources for formatting, reading, storing,
allocating dynamically, comparing, and writing simulation data,
including input stimulus and output results.
• In this lab, you will learn:
• how to write tasks, functions, and testbenches.
• about the components of a testbench, and language constructs available to
verify the correctness of the underlying hardware model.
• Please refer to the PlanAhead tutorial on how to use the PlanAhead
tool for creating projects and verifying digital circuits.
Objectives
• Develop tasks for modeling a combinatorial circuit
• Develop functions for modeling a combinatorial circuit
• Develop a testbench to test and validate a design under test
Hàm (Function) và Tác vụ (Task)
• Được sử dụng nhiều lần trong thiết kế.
• Mục đích tránh lặp lại những đoạn code giống nhau.
• Task và Function phải được định nghĩa trong một module và có giá trị
cục bộ với module đó.
• Chỉ gồm các câu lệnh mức hành vi.
• Không được chứa các lệnh initial và always.
• Để gọi Task và Function từ module khác cần khai báo tất cả các biến
trong danh sách cổng của chúng.
Tasks
• A task is like a procedure which provides the ability to execute
common pieces of code from several different places in a model. A
• task can contain timing controls, and it can call other tasks and
functions.
• A task is defined, within a module definition, as:
task task_id;
[declarations]
procedure statements
endtask
Tasks
module HAS_TASK;
• A task can have zero, parameter MAXBITS = 8;
one, or more task REVERSE_BITS; // task definition starts here
arguments. Values are input [MAXBITS-1 : 0] DIN;
output [MAXBITS-1 : 0] DOUT;
passed to and from a integer k;
task through arguments. begin
for (k=0; k < MAXBITS; k = k +1)
• The arguments can be DOUT[MAXBITS-k] = DIN[k];
input, output, or inout. end
endtask // task definition ends here
Here is an example of a reg [MAXBITS-1:0] REG_X, NEW_REG;
task definition and always @ (REG_X)
REVERSE_BITS(REG_X,NEW_REG); // task being
usage. called
endmodule
Tasks
• Task được dùng trong tất cả các ngôn ngữ lập trình, thường được gọi
là các thủ tục hoặc chương trình con.
• Các dòng lệnh nằm trong đoạn task…endtask.
• Dữ liệu được chuyển đến task, xử lý và trả lại kết quả.
• Khi cần dùng phải gọi task với dữ liệu in và out. Có thể gọi task nhiều
lần để tránh lặp lại code.
• Tasks được định nghĩa trong module dùng nó. Có thể định nghĩa tasks
trong file riêng biệt và dùng chỉ dẫn biên dịch include để gọi task.
Tasks
• tasks có thể gồm các trễ thời gian như : posedge, negedge, # delay và wait.
• tasks có thể có nhiều inputs và outputs.
• Các biến khai báo trong task là biến nội bộ. Thứ tự khai báo các biến xẽ xác định
cách thức các biến chuyển đến task. Khi dùng biến nội bộ, output về cơ bản chỉ
được gán giá trị khi kết thúc thực hiện task.
• Khi không dùng biến nội bộ, task có thể dùng các biến toàn cục.
• Task có thể gọi một task khác hoặc function.
• Task được dùng trong cả mô tả mạch dãy.
• Task phải được gọi bằng một câu lệnh, không được dùng task trong một biểu
thức như function.
Cấu trúc Task
• Task được kích hoạt bằng một sự kiện (event)
hoặc điều khiển thời gian.
• Các tham số qua Task sẽ giữ nguyên kiểu dữ
liệu. Do đó một tham số kiểu wire qua task
như input sẽ không thể thay đổi giá trị của nó
thông qua lệnh gán (gán thủ tục)
• Các tham số input của task có thể là kiểu net,
reg nhưng bất kỳ tham số inout hoặc output
đều phải là kiểu reg (vế trái của phép gán thủ
tục).
Tasks
Cú pháp
• Một task bắt đầu bằng từ khoá task và kết thúc bằng module task_global();
từ khoá endtask.
• Inputs và outputs được khai báo sau từ khoá task.
• Các biến nội bộ được khai báo sau khi khai báo input reg [7:0] temp_out;
và output.
reg [7:0] temp_in;
• Ví dụ
Cú pháp begin
myfunction = ((a+b) + (c-d));
end
• Một function bắt đầu bằng từ khoá
endfunction
fumction và kết thúc bằng từ khoá
endmodule
endfunction.
/==================================
• inputs được khai báo sau từ khoá module function_calling(a, b, c, d, e, f);
function.
input a, b, c, d, e ;
output f;
wire f;
`include "myfunction.v"
endmodule
Example for function
Example for function
module parity(p,a,En);
input[7:0]a;
input En;
output p;
reg p;
always @(posedge En)
begin
p=n1(a)%2; //Use n1 & generate the parity bit.
$display("t=%0d, a = %b, en = %b, p = %b ",$time,a,en,p);
end
function integer n1; //A function to count the number of 1 bits in a byte
input[7:0]a;
integer i;
for(i=0;i!=8;i=i+1)
begin
if(i==0) n1=0;
if(a[i]) n1=n1+1;
end
endfunction
endmodule
Tasks and functions
Exercise
• Write a function called add_two_values which will take two 4-bit
parameters, add them, and return a 5-bit sum. Write a module, called
add_two_values_function, with two 4-bit input ports and one 5-bit
output port and calls the function. Simulate the design with the
provided testbench, add_two_values_function_tb.v, and verify the
functionality.
• Write a task called calc_ones which will take an 8-bit number, and
calculate and return number of ones. Write a module, called
calc_ones_function, with one 8-bit input port and one 3-bit output
port and calls the function. Simulate the design with the provided
testbench, calc_ones_function_tb.v, and verify the functionality.
Testbench
• The major components of a testbench are:
• `timescale declaration: Specify the time unit for all delays
• Module, which defines the testbench top-level structure. A testbench
usually does not have ports
• Internal signals, which will drive the stimuli into the UUT and monitor the
response from the UUT. Signal to drive and monitor
• UUT instantiation
• Stimuli generation: Write statements to create stimulus and procedural
block
• Response monitoring and comparing
• Self-testing statements that will report values, error, and warnings
• $display, $write, $strobe, and/or $monitor system tasks
Art of Writing TestBenches
http://www.asic-world.com/verilog/art_testbench_writing.html
Art of Writing TestBenches
• Introduction
• Example - Counter
• Code for Counter
• Test Plan
• Test Cases
• Writing a TestBench
• Test Bench
• Test Bench with Clock generator
• Test Bench continues...
• Adding Reset Logic
• Code of reset logic
• Adding test case logic
• Test Case 1 - Asserting/ De-asserting reset
• Test Case 2 - Assert/ De-assert enable after reset is applied.
• Test Case 3 - Assert/De-assert enable and reset randomly.
• Adding compare Logic
Introduction
• Writing a testbench is as complex as writing the RTL code itself
• Typically 60-70% of time needed for any ASIC is spent on
verification/validation/testing
• For writing testbenches it is important to have the design
specification of "design under test" or simply DUT.
• Specs need to be understood clearly and a test plan, which basically
documents the test bench architecture and the test scenarios (test
cases) in detail, needs to be made.
Example - Counter
Let's assume that we have to verify a simple 4-bit up counter, which
increments its count whenever enable is high, and resets to zero when
reset is asserted high. Reset is synchronous to clock.
✓Code for Counter
✓Test Plan
✓Test Cases
Code for Counter
//-----------------------------------------------------
// Design Name : counter
// File Name : counter.v
// Function : 4 bit up counter
// Coder : Deepak
//-----------------------------------------------------
module counter (clk, reset, enable, count);
input clk, reset, enable;
output [3:0] count;
reg [3:0] count;
always @ (posedge clk)
if (reset == 1'b1) begin
count <= 0;
end else if ( enable == 1'b1) begin
count <= count + 1;
end
endmodule
Test Plan
• Write a self-checking test bench • DUT is instantiated in the
• Testbench environment will look testbench
something like the figure below. • Testbench will contain a clock
generator, reset generator,
enable logic generator and
compare logic, which basically
calculates the expected count
value of counter and compares it
with the output of counter.
Test case
• Reset Test : We can start with reset de-asserted, followed by asserting
reset for few clock ticks and deasserting the reset, See if counter sets
its output to zero.
• Enable Test : Assert/deassert enable after reset is applied.
• Random Assert/deassert of enable and reset.
• We can add some more test cases; but we are not here to test the
counter, rather to learn how to write test benches.
Writing a TestBench
• First step:
Second problem with the approach that we have taken till now is that we need to manually check the
waveform and also the simulator output on the screen to see if the DUT is working correctly. Part IV
shows how to automate this.
Adding compare Logic
• To make any testbench self
checking/automated, first we
need to develop a model that
mimics the DUT in functionality.
• In our example, it's going to be
very easy, but at times if the DUT
is complex, then to mimic it will
be very complex and will require
a lot of innovative techniques to
make self-checking work.
Add the checker logic
• Once we have the logic to mimic the DUT functionality, we need to
add the checker logic, which at any given point keeps checking the
expected value with the actual value. Whenever there is any error, it
prints out the expected and actual value, and also terminates the
simulation by triggering the event "terminate_sim".
1 always @ (posedge clk)
2 if (count_compare ! = count) begin
3 $display ("DUT Error at time %d", $time);
4 $display (" Expected value %d, Got Value %d", count_compare, count);
5 #5 -> terminate_sim;
6 end
4-bit up counter test bench
event reset_done;
module counter_tb; Initial
reg clk, reset, enable; forever begin
wire [3:0] count; @ (reset_enable);
@ (negedge clk)
reg dut_error; $display ("Applying reset");
counter U0 ( reset = 1;
.clk (clk), @ (negedge clk)
.reset (reset), reset = 0;
$display ("Came out of Reset");
.enable (enable), -> reset_done;
.count (count) end
); initial begin
event reset_enable; #10 -> reset_enable;
event terminate_sim; @ (reset_done);
@ (negedge clk);
initial enable = 1;
repeat (5)
begin begin
$display ("##############################"); @ (negedge clk);
clk = 0; end
enable = 0;
reset = 0; #5 -> terminate_sim;
enable = 0; end
dut_error = 0;
end reg [3:0] count_compare;
always always @ (posedge clk)
#5 clk = !clk; if (reset == 1'b1)
initial count_compare <= 0;
else if ( enable == 1'b1)
begin count_compare <= count_compare + 1;
$dumpfile ("counter.vcd");
$dumpvars;
End always @ (negedge clk)
if (count_compare != count) begin
initial $display ("DUT ERROR AT TIME%d",$time);
@ (terminate_sim) begin $display ("Expected value %d, Got Value %d", count_compare, count);
$display ("Terminating simulation"); dut_error = 1;
#5 -> terminate_sim;
if (dut_error == 0) begin end
$display ("Simulation Result : PASSED");
end endmodule
else begin
$display ("Simulation Result : FAILED");
end
$display ("########################################");
#1 $finish;
end