0% found this document useful (0 votes)
81 views55 pages

Tasks, Functions, and Testbench - 2020 (Autosaved)

Verilog allows defining sub-programs called tasks and functions to improve code readability and reusability. Tasks can contain timing controls and call other tasks/functions, while functions are equivalent to combinational logic. The document discusses tasks, functions, testbenches, and system tasks. It provides examples of defining and calling tasks, and using system tasks like $display for output. The objectives are to develop tasks/functions for combinational circuits and a testbench to test and validate designs.

Uploaded by

Khuyến Trần
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
81 views55 pages

Tasks, Functions, and Testbench - 2020 (Autosaved)

Verilog allows defining sub-programs called tasks and functions to improve code readability and reusability. Tasks can contain timing controls and call other tasks/functions, while functions are equivalent to combinational logic. The document discusses tasks, functions, testbenches, and system tasks. It provides examples of defining and calling tasks, and using system tasks like $display for output. The objectives are to develop tasks/functions for combinational circuits and a testbench to test and validate designs.

Uploaded by

Khuyến Trần
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 55

Tasks, Functions, and

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ụ

module simple_task(); task convert;


task convert; begin
input [7:0] temp_in; temp_out = (9/5) *( temp_in + 32);
output [7:0] temp_out;
end
begin
temp_out = (9/5) *( temp_in + 32); endtask
end
endtask endmodule
endmodule
Calling a Task
• Giả sử task trong ví dụ 1 được lưu trong file mytask.v. Ưu điểm của task trong một file riêng biệt
là có thể dùng task trong nhiều module.
• Ví dụ
module task_calling (temp_a, temp_b, temp_c, temp_d);
input [7:0] temp_a, temp_c;
output [7:0] temp_b, temp_d;
reg [7:0] temp_b, temp_d;
`include "mytask.v"
always @ (temp_a)
begin
convert (temp_a, temp_b);
end
always @ (temp_c)
begin
convert (temp_c, temp_d);
end
endmodule
Example of Task
module oness_counter;
reg [3:0]x;reg [2:0]y;
always@(x)onescounter(x,y);
task onescounter;
input [3:0]x; output[2:0]y; integer i;
begin
y=0;
for(i=0;i<=3;i=i+1)
if (x[i])
y= y+1;
end
endtask
initial x=3'b000;
always #3 x=x+2'b11;
initial $monitor(" t=%0d, y= %b, x = %b ",$time,y,x);
initial #30 $stop;
endmodule
Exercise
1. Write a task called add_two_values which will take two 4-bit parameters,
add them, and output a 4-bit sum and a carry. Write a module, called
add_two_values_task, that calls the task with the operands received via
input ports and outputs the result. Simulate the design with the provided
testbench, add_two_values_tb.v, and verify the functionality.
2. Write a task called calc_even_parity which will take an 8-bit number, and
compute and return parity. Write a module, called alc_even_parity_task,
which calls the task with the operand received via the input port and
outputs the result. Use the provided testbench,
calc_even_parity_task_tb.v, that displays the result using $display system
task. Simulate the design and verify the functionality.
System task
• Verilog HDL also provides few system tasks. The system task name is
preceded with a $.
• The system tasks are not synthesizable, i.e. they cannot be realized in real
hardware.
• $time - returns an integer that is current simulation time, scaled to current
module’s timescale
• $realtime - returns a real number that is current simulation time, scaled to
current module’s timescale
• $display - used for displaying formatted strings, expression or values of variables,
similar to printf in C.
• $monitor provides a mechanism to monitor a signal when its value changes.
• $stop – suspends the simulation flow and allows to work in interactive mode
• $finish – terminates the simulation
• $random – generates a 32-bit random number
System tasks
• All Verilog simulators support system tasks used for some routine
operations, like print, stop/interrupt simulation, monitor variables,
dumping signal values etc.
• System tasks look like $<command>
• $time - returns an integer that is current simulation time, scaled to current
module’s timescale
• $realtime - returns a real number that is current simulation time, scaled
to current module’s timescale
• $display - used for displaying formatted strings, expression or values of
variables, similar to printf in C.
System tasks
Ví dụ
System tasks
// Display the reg_val value in both hexa and decimal formats
$display(“reg_val = %h hex \t %d decimal", rval, rval);
// $write does the same as $dispplay, except for a newline
$write(“reg_val = %h hex \t %d decimal", rval, rval);
// One useful feature is hierirchy format - %m
$display(“This print comes from %m module”);
• The commonly used format specifiers are
• %b display in binary format
• %c display in ASCII character format
• %d display in decimal format
• %h display in hex format
• %o display in octal format
• %s display in string format
System tasks
$monitor
• $monitor provides a mechanism to monitor a signal when its value
changes.
• Only one $monitor statement can be active (the last one overrides
all the previous).
System tasks

• $stop – suspends the


simulation flow and allows to
work in interactive mode
• $finish – terminates the
simulation
• $random – generates a 32-bit
random number
Chỉ dẫn biên dịch
• Tất cả các chỉ dẫn biên dịch Verilog đều bát đầu bằng ký tự ( ` ) .
• Chỉ dẫn biên dịch dùng để điều khiển biên dịch trong Veriog.
• Một số chỉ dẫn biên dịch thông dụng:
• `define
• `ifdef ,`endif,
• `include
• `timescale
`define
• `define WORD_SIZE 64 // Text
substitution
• `define byte_reg reg[7:0] // Define a
frequently used text
• `define one 1’b1 // Improve
readability
• `define F $finish // Create a command
alias
• `define directive can also be used for
text substitution to improve code • READ sẽ được thay thế bằng 3’b0 khi
biên dịch
readability:
`include
• Contents of a Verilog file can be “included” in another
file using `include directive.
• This directive is typically used to include header files,
global or commonly used text macros, and tasks.
`timescale
• The delay values are measured in terms of simulator timesteps.
• `timescale (mapping from simulator timesteps to real time) can be assigned to
each module. The `timescale directive is used for this :
`timescale time_unit / time_precision
• time_unit– constant multiplier of time values
• time_precision – minimum step size during simulation, which determines rounding of
numerical values
• Allowed unit/precision values: {1 | 10 | 100, s | ms | us | ns | ps}
• Different units may be used for time units and precision (e.g. `timescale 10us /
100 ns ), but can only be 1, 10 or 100 units.
`timescale
• The reference_time_units is the value attributed to the delay (#) operator, and the
time_precision is the accuracy to which reported times are rounded during
simulations.
• `timescale directive defines timing of the module where it is defined. It remains in
force until overridden by the next such directive.
• Value of time precision shouldn’t be smaller then actually needed. With `timescale
1s/1ps, to advance 1 second, the time-wheel scans its queues 1012 times versus a
`timescale 1s/1ms, where it only scans the queues 103 times.
• The smallest precision of all the `timescale directives determines the time unit of
the simulation
`timescale
Functions
• Functions are declared within a parent module with the keywords function
and endfunction. Functions are used if all of the following conditions are
true:
• There are no delay, timing, or event control constructs that are present
• It returns a single value
• There is at least one input argument
• There are no output or inout argument
• There are no non-blocking assignments
• In short, functions may implement only combinatorial behavior, i.e. they
compute a value on the basis of the present value of the input arguments
and return a single value. They are used in the right hand side of an
assignment statement.
Example of a function definition and call
module HAS_FUNCTION(X_IN, REV_X);
parameter MAXBITS = 8;
output reg [MAXBITS – 1 : 0] REV_X;
input [MAXBITS – 1 : 0] X_IN;
function [MAXBITS – 1 : 0] REVERSE_BITS;
// function definition starts here
input [MAXBITS – 1 : 0] DIN;
integer k;
begin
for (k=0; k < MAXBITS; k = k +1)
REVERSE_BITS[MAXBITS-k] = DIN[k];
end
endfunction // function definition ends here
always @ (X_IN)
REV_X = REVERSE_BITS(X_IN); // function being called
endmodule
Function
Function trong Verilog HDL tương tự như task, chỉ khác nhau 1 số điểm như
function chỉ có 1 output và không có trễ.
• Functions được định nghĩa trong module sử dụng chúng. Có thể định nghĩa
function trong một file riêng biệt và dùng chỉ dẫn biên dịch include để dùng
hàm.
• functions không được bao gồm trễ như posedge, negedge, # delay, nghĩa là
function đươc thực hiện tại trễ ‘zero’.
• functions có thể có nhiều input nhưng chỉ có 1 output.
• Các biến khai báo trong function là các biến nội bộ của function đó. Thứ tự khai
báo các biến xẽ xác định cách thức các biến chuyển đến function. 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 function.
• Khi không dùng biến nội bộ, function có thể dùng các biến toàn cục.
• function được dùng để mô tả mạch tổ hợp. .
• functions có thể gọi một function khác nhưng không được gọi task.
Cấu trúc Function
module simple_function();
function myfunction;
Function input a, b, c, 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"

assign f = (myfunction (a,b,c,d)) ? e :0;

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:

• Building a dummy template which


basically declares inputs to DUT as reg
and outputs from DUT as wire

• instantiates the DUT as shown in the


code below. Note that there is no port
list for the test bench.
Writing a TestBench
• Test Bench with Clock
generator
Next step:
- Drive all the inputs to DUT to
some known state
- Add clock generator logic
Test Bench continues...
• $dumpfile: specify the file that
stores the waveform, that can be
used later using a waveform viewer
• $dumpvars: basically instructs the
Verilog compiler to start dumping
all the signals to "counter.vcd“
• $display is used for printing text or
variables to stdout (screen), \t is
for inserting tabs.
• $monitor: keeps track of changes $finish is used for terminating the simulation
to the variables that are in the list after #100 time units
(clk, reset, enable, count)
Adding Reset Logic
• 'events' in Verilog: events can be triggered, and
also monitored, to see if an event has occurred.
• The trigger event "reset_trigger": when this
event happens, reset logic asserts reset at
negative edge of clock and de-asserts on next
negative edge
• After de-asserting the reset, reset logic triggers
another event called "reset_done_trigger". This
trigger event can then be used somewhere else
in the testbench to sync up.
Adding test case logic
Three testcases
• Reset Test : We can start with reset de-asserted, followed by
asserting reset for few clock ticks and de-asserting the reset, See if
counter sets its output to zero.
• Enable Test : Assert/de-assert enable after reset is applied.
• Random Assert/de-assert of enable and reset.
Repeating it again: "There are many ways" to code a test case, it all
depends on the creativity of the Test bench designer. Let's take a
simple approach and then slowly build upon it.
Test Case 1 &2
Test Case 1 - Asserting/ De-asserting Test Case 2 - Assert/ De-assert enable
reset after reset is applied
• trigger the event reset_trigger after 10 • trigger the reset logic and wait for the reset
simulation units logic to complete its operation, before we
start driving the enable signal to logic 1
Test Case 3 - Assert/De-assert enable and
reset randomly
• assert the reset, and then randomly drive values on to enable and
reset signal

Do all this three test case exist in same file?


- No, if we try to have all three test cases on one file, then we end up
having race conditions due to three initial blocks driving reset and
enable signal
- Once test bench coding is done, test cases are coded separately and
included in testbench with `include directives
Event: terminate_sim
• even though test case execution is not The modified test case #2 would be like:
complete, simulation terminates
• To have better control, what we can do is
adding an event like "terminate_sim" and
execute $finish only when this event is
triggered
• We can trigger this event at the end of test
case execution

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

You might also like