Module 4 Pt1
Module 4 Pt1
Two new statements help with loops. First, if you are in a loop, but want to skip over the rest of the statements and do
the next iteration, use continue. If you want to leave the loop immediately, use break
If you have a SystemVerilog task that does not consume time, you should make it a void function, which is a function
that does not return a value. Now it can be called from any task or function. For maximum flexibility, any debug routine
should be a void function rather than a task so that it can be called from any task or function
In SystemVerilog, if you want to call a function and ignore its return value, cast the result to void
SystemVerilog makes several small improvements to tasks and functions to make them look more like C or C++
routines. In general, a routine definition or call with no arguments does not need the empty parentheses ().
Routine Arguments:
SystemVerilog and Verilog-2001 allow you to declare task and function arguments more cleanly and with less
repetition. The following Verilog task requires you to declare some arguments twice: once for the direction and once for
the type
With System Verilog, you can use the less verbose C-style, shown in Sample 3.7. Note that you should use the universal
input type of logic.
Argument Direction
The direction and type default to "input logic" are sticky, so you don't have to repeat these for similar
arguments.
The arguments a and b are input logic, 1-bit wide. The arguments u and v are 16-bit output bit types.
Verilog had a simple way to handle arguments: an input or inout was copied to a local variable at the start of the routine,
whereas an output or inout was copied when the routine exited. No memories could be passed into a Veri log routine,
except scalarscan.
In System Veri log, you can specify that an argument is passed by reference, rather than copying its value. This
argument type, ref, has several benefits over input, output, and inout. First, you can now pass an array into a routine.
System Verilog allows you to pass array arguments without the ref direction, but the array is copied onto the stack
Sample 3.10 also shows the const modifier. As a result, the array a points to the array in the routine call, but the contents
of the array cannot be modified. If you try to change the contents the compiler prints an error.
Always use ref when passing arrays to a routine for best performance. If you don't want the routine to change the array
values, use the const ref type, which causes the compiler to check that the routine does not modify the array.
The second benefit of ref arguments is that a task can modify a variable and is instantly seen by the calling function.
This is useful when you have several threads executing concurrently and want a simple way to pass information.
In Sample 3.11, the thread2 block in the initial block can access the data from memory as soon as bus.enable is asserted,
even though the bus_read task does not return until the bus transaction completes, which could be several cycles later.
The data argument is passed as ref, and as a result, the @data statement triggers as soon as data changes in the task. If
data was declared as output, the @data statement would not trigger until the end of the bus transaction.
Default Value for an Argument
As your testbench grows in sophistication, you may want to add additional controls to your code but not break existing
code. However, you don't want to go back and rewrite every call to add extra arguments. In SystemVerilog you can
specify a default value that is used if you leave out an argument in the call. Sample 3.12 adds low and high arguments to
the print_checksum function so that you can print a checksum of a range of values.
You can call this function in the following ways, as shown in Sample 3.13. Note that the first call is compatible with both versions
of the print_checksum routine
Passing Arguments by Name
in the SystemVerilog the arguments to a task or function are called "ports," just like the connections for a module. If you
have a task or function with many arguments, some with default values, and you only want to set a few of those
arguments, you can specify a subset by specifying the name of the routine argument with a port-like syntax.
The most common coding mistake that you are likely to make with a routine is forgetting that the argument type is
sticky with respect to the previous argument and that the default type for the first argument is a single-bit input.
The two arguments are input integers. After declaring the integers, if an array must be accessed, error will be generated
as they are of different types. Hence declare them as ref to refer to the array.
What argument types are a and b? They take the direction of the previous argument: ref. Using ref for a simple variable
such as an int is not usually needed, but you would not get even a warning from the compiler. To prevent errors, be
explicit in declaration.
Verilog routines could only return a simple value such as a bit, integer or vector. If you wanted to compute and return an
array, there was no simple way. In SystemVerilog, a function can return an array, using several techniques.
The first way is to define a type for the array and then use that in the function declaration
One problem with the preceding code is that the function init creates an array, which is copied into the array f5. If the
array was large, this could be a large performance problem.
The alternative is to pass the routine by reference. The easiest way is to pass the array into the function as a ref argument
When Verilog was created in the 1980s, it was tightly tied to describing hardware. As a result, all objects in the
language were statically allocated. In particular, routine arguments and local variables were stored in a fixed location,
rather than pushing them on a stack like other programming languages
Automatic Storage
In Verilog-I 995, if you tried to call a task from multiple places in your testbench, the local variables shared common,
static storage, and so the different threads stepped on each other's values.
In Verilog-2001 you can specify that tasks, functions, and modules use automatic storage, which causes the simulator to
use the stack for local variables.
In SystemVerilog, routines still use static storage by default, for both modules and program blocks. You should always
make program blocks (and their routines) use automatic storage by putting the automatic keyword in the program
statement.
You can call this task multiple times concurrently, as the addr and expect_data arguments are stored separately for each
call. Without the automatic modifier, if you called wait_for_mem a second time while the first was still waiting, the
second call would overwrite the two arguments.
Variable Initialization
A similar problem occurs when you try to initialize a local variable in a declaration, as it is actually initialized before the
start of simulation. The general solution is to avoid initializing a variable in a declaration to anything other than a
constant. Use a separate assignment statement to give you better control over when initialization is done.
The task in Sample 3.23 looks at the bus after five cycles and then creates a local variable and attempts to initialize it to
the current value of the address bus.
The bug is that the variable local_addr is statically allocated and so it is actually initialized at the start of simulation not
when the begin ... end block is entered. Once again the solution is to declare the program as automatic
Additionally you can avoid this by never initializing a variable in the declaration
Time Values
SystemVerilog has several new constructs to allow you to unambiguously specify time values
Time Literals
SystemVerilog allows you to unambiguously specify a time value plus units. Your code can use delays such as 0.1ns or
20ps. Just remember to use timeunit and timeprecision or 'timescale.
You can make your code even more time aware by using the classic Verilog $timeformat (), $time, and $realtime
system tasks. The four arguments to $timeformat are the scaling factor (-9 for nanoseconds, -12 for picoseconds), the
number of digits to the right of the decimal point, a string to print after the time value, and the minimum field width.
There are several steps needed to verify a design: generate stimulus, capture responses, determine correctness, and
measure progress
Testbench wraps around the design, sending in stimulus and capturing the design's response. The testbench
forms the "real world" around the design, mimicking the entire environment.
For example, a processor model needs to connect to various buses and devices, which are modeled in the
testbench as bus functional models.
--A networking device connects to multiple input and output data streams that are modeled based on standard
protocols.
--A video chip connects to buses that send in commands, and then forms images that are written into memory
models.
The key concept is that the testbench simulates everything not in the design under test
Separating the Testbench and Design