System Verilog III
System Verilog III
• As you verify your design. you need to write a great deal of code.
• System Verilog has a lot of constructs to make the language look more like C.
• SystemVerilog adopts many operators and statements from C and C++.
You can declare a loop variable inside a for-loop that then restricts the scope
of the loop variable and can prevent some coding bugs.
The auto-increment + + and auto-decrement - - operators are available in
both pre- and post-forms.
If you have a label on a begin or fork statement, you can put the same label
on the matching end or join statement. This makes it easier to match the start
and finish of a block.
You can also put a label on other SystemVerilog end statements such as
endmodule, endtask, endfunction, and others that you will learn in this
course.
2
3
• There are new statements that 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.
• The loop in the shown code reads commands from a file using the file I/O system
tasks. If the command is just a blank line, the code does a continue, skipping any
further processing of the command. If the command is "done," the code does a
break to terminate the loop.
4
• Functions have only input arguments, and the function returns a value, and the
value must be used, as in an assignment statement. Sometimes the function is
void, i.e. it returns no value.
• Tasks may have input and output arguments. The task itself does not return a
value, but the value of its arguments may change.
• The most important difference is that a task can consume time, whereas a
function cannot.
A function cannot have a delay, #100, a blocking statement such as @
(posedge clock) or wait (ready), or call a task.
SystemVerilog relaxes this rule a little in that a function can call a task, but
only in a thread spawned with the fork ... join_none statement, which is
described later.
Hint: 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.
• In SystemVerilog, if you want to call a function and ignore its return value, cast
the result to void, as follows:
void' ($fscanf (file, "%d", i));
5
• In Systemverilog, using begin and end for tasks and functions is optional. Tasks
have endtask, and functions have endfunction.
• The task / endtask and function / endfunction keywords are enough to define the
routine boundaries.
6
• SystemVerilog allows you to declare task and function arguments more cleanly
and with less repetition.
• The first task requires you to declare some arguments twice: once for the
direction. and once for the type.
• In the second task, we use the less verbose C-style.
• You can take even more shortcuts with declaring routine arguments. The
direction and type default to "input logic“.
task T3(a, b, output bit [15:0] u, v);
The arguments a and b are input logic, 1-bit wide.
The arguments u and v are 16-bit output bit types.
Now that you know this, don't depend on the defaults, as your code will be
infested with subtle and hard to find bugs. Always declare the type and
direction for every routine argument.
7
• In System Verilog, 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.
You can 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, which is an expensive operation.
The second benefit of ref arguments is that a task can modify a variable
and is instantly seen by the calling function.
• The above code shows the const modifier. As a result, the contents of the array
cannot be modified. If you try to change the contents. the compiler prints an
error.
8
• As your testbench grows in sophistication, you may want to add additional
controls to your code but not break existing code.
• For the function in the previous slide, you might want to print a checksum of just
the middle values of the array. 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.
• The code shown here, adds low and high arguments to the print_checksum
function so that you can print a checksum of a range of values.
9
• 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 argument, as in the shown code.
10
• 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.
• The default type for the first argument is a single-bit input.
• task sticky(int a, b); The two arguments are input integers.
• As you are writing the task, you may realize that you need access to an array, and
so you add a new array argument, and use the ref type so that it does not have to
be copied.
• Your routine header now looks like this:
task sticky(ref int array [50] , int a, b); // What direction are these?
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, and thus would
not realize that you were using the wrong direction.
• If any argument to your routine is something other than the default input type,
specify the direction for all arguments as follows.
task sticky(ref int array [50] , input int a, b); // Be explicit
11
• Verilog had a primitive way to end a routine; after you executed the last
statement in a routine, it returned to the calling code.
• System Verilog adds the return statement to make it easier for you to control the
flow in your routines.
• The task in the shown code needs to return early because of error checking.
Otherwise, it would have to put the rest of the task in an else clause. which
would cause more indentation and be more difficult to read.
12
• 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. (first code)
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.
• An alternative way is to pass the array by reference. The easiest way is to pass
the array into the function as a ref argument, as shown in the second code.
• The last way for a function to return an array is to wrap the array inside a class,
and return a handle to an object (will be explained later).
13
• SystemVerilog has several constructs to allow you to unambiguously specify time values
in your system.
• When you want to specify a delay in your code you write #1, for example. It means a
delay of one time unit. But what is the time unit ? If not otherwise specified, the time
unit is one second. So #1 means delay for one second. Any fractions will be rounded, so
#0.4 will be considered as 0 delay, but #0.6 will be considered as 1 second delay.
• But what if you want to specify the timing more precisely ?
14
• You can overwrite the default time unit using timeunit declaration.
• You can also specify the minimum time that you can express, instead of being always
one time unit. For example if you specify a time unit of 1 ns, and you want to use
fractions of the nano second, you can use the timeprecision declaration.
• timeprecision can be the same as the timeunit or a smaller unit. It can also be 10n of the
smaller unit, provided that it keeps smaller than the timeunit.
• For example, timeunit may be 1 ns, and timeprecision is 1 ps, 10ps, 100ps, or 1000ps,
but not 10000ps.
• The value of timeprecision is also used while displaying time values using $display and
%t. The value displayed is expressed using the value of timeprecision.
• This is shown in the above example. 1ns is displayed as 1000, as the displaying is
expressed using the value specified by timepresision which is 1ps.
• The question now is, can we display using other units different than the value of
timeprecision? The answer is yes.
15
• The question now is, can we display using other units different than the value of
timeprecision? The answer is yes using $timeformat.
• Syntax: $timeformat(<code>,<precision>,<suffix string>,<minimum width>)
<code>: (0: s, -3: ms, -6: s, -9: ns, -12: ps, -15: fs)
<precision>: represents the number of fractional digits
<suffix_string>: a string written after thenumerical time value
<minimum width>: the minimum space on which the value is written.
16
• Instead of writing timeunit, and timeprecision explicitly, you can alternatively use the
'timescale unit/precision compiler directive.
• Another alternative way is to write “-timescale=1ns/1ps” in the command line that calls
the simulator.
17