This section covers some of the more advanced Verilog features. System tasks and functions are defined as part of the IEEE Verilog standard [Verilog LRM14].
The following code illustrates the display system tasks [Verilog LRM 14.1]:
module test_display; // display system tasks:
initial begin $display ("string, variables, or expression");
/* format specifications work like printf in C:
%d=decimal %b=binary %s=string %h=hex %o=octal
%c=character %m=hierarchical name %v=strength %t=time format
%e=scientific %f=decimal %g=shortest
examples: %d uses default width %0d uses minimum width
%7.3g uses 7 spaces with 3 digits after decimal point */
// $displayb, $displayh, $displayo print in b, h, o formats
// $write, $strobe, $monitor also have b, h, o versions
$write("write"); // as $display, but without newline at end of line
$strobe("strobe"); // as $display, values at end of simulation cycle
$monitor(v); // disp. @change of v (except v= $time,$stime,$realtime)
$monitoron; $monitoroff; // toggle monitor mode on/off
end endmodule
The following example illustrates the file I/O system tasks [Verilog LRM 14.2]:
module file_1; integer f1, ch; initial begin f1 = $fopen("f1.out");
if(f1==0) $stop(2); if(f1==2)$display("f1 open");
ch = f1|1; $fdisplay(ch,"Hello"); $fclose(f1); end endmodule
> vlog file_1.v
> vsim -c file_1
# Loading work.file_1
VSIM 1> run 10
# f1 open
# Hello
VSIM 2> q
> more f1.out
Hello
>
The $fopen system
task returns a 32-bit unsigned integer called a multichannel descriptor
( f1 in this example) unique to each file. The multichannel
descriptor contains 32 flags, one for each of 32 possible channels or files
(subject to limitations of the operating system). Channel 0 is the standard
output (normally the screen), which is always open. The first call to $fopen
opens channel 1 and sets bit 1 of the multichannel descriptor. Subsequent
calls set higher bits. The file I/O system tasks: $fdisplay
, $fwrite , $fmonitor , and $fstrobe
; correspond to their display counterparts. The first parameter for the
file system tasks is a multichannel descriptor that may have multiple bits
set. Thus, the preceding example writes the string "Hello"
to the screen and to file1.out . The task $fclose closes a
file and allows the channel to be reused.
The file I/O tasks $readmemb
and $readmemh read a text file into a memory. The file may
contain only spaces, new lines, tabs, form feeds, comments, addresses, and
binary (for $readmemb ) or hex (for $readmemh
) numbers, as in the following example:
mem.dat
@2 1010_1111 @4 0101_1111 1010_1111 // @address in hex
x1x1_zzzz 1111_0000 /* x or z is OK */
module load; reg [7:0] mem[0:7]; integer i; initial begin
$readmemb("mem.dat", mem, 1, 6); // start_address=1, end_address=6
for (i= 0; i<8; i=i+1) $display("mem[%0d] %b", i, mem[i]);
end endmodule
> vsim -c load
# Loading work.load
VSIM 1> run 10
# ** Warning: $readmem (memory mem) file mem.dat line 2:
# More patterns than index range (hex 1:6)
# Time: 0 ns Iteration: 0 Instance:/
# mem[0] xxxxxxxx
# mem[1] xxxxxxxx
# mem[2] 10101111
# mem[3] xxxxxxxx
# mem[4] 01011111
# mem[5] 10101111
# mem[6] x1x1zzzz
# mem[7] xxxxxxxx
VSIM 2> q
>
There are two timescale
tasks, $printtimescale and $timeformat [Verilog
LRM 14.3]. The $timeformat specifies the %t format
specification for the display and file I/O system tasks as well as the time
unit for delays entered interactively and from files. Here are examples
of the timescale tasks:
// timescale tasks: module a; initial $printtimescale(b.c1); endmodule module b; c c1 (); endmodule `timescale 10 ns / 1 fs module c_dat; endmodule `timescale 1 ms / 1 ns module Ttime; initial $timeformat(-9, 5, " ns", 10); endmodule /* $timeformat [ ( n, p, suffix , min_field_width ) ] ; units = 1 second ** (-n), n = 0->15, e.g. for n = 9, units = ns p = digits after decimal point for %t e.g. p = 5 gives 0.00000 suffix for %t (despite timescale directive) min_field_width is number of character positions for %t */
The simulation control
tasks are $stop and $finis h [Verilog LRM 14.4]:
module test_simulation_control; // simulation control system tasks: initial begin $stop; // enter interactive mode (default parameter 1) $finish(2); // graceful exit with optional parameter as follows: // 0 = nothing 1 = time and location 2 = time, location, and statistics end endmodule
The timing-check tasks [Verilog LRM 14.5] are used in specify blocks. The following code and comments illustrate the definitions and use of timing-check system tasks. The arguments to the tasks are defined and explained in Table 11.11.
reference_event |
||
data_event |
||
limit |
||
threshold |
||
notifier |
flags a timing violation (before -> after): x->0, 0->1, 1->0, z->z |
|
module timing_checks (data, clock, clock_1,clock_2); input data,clock,clock_1,clock_2; reg tSU,tH,tHIGH,tP,tSK,tR; specify // timing check system tasks: /* $setup (data_event, reference_event, limit [, notifier]); violation = (T_reference_event)-(T_data_event) < limit */ $setup(data, posedge clock, tSU); /* $hold (reference_event, data_event, limit [, notifier]); violation = (time_of_data_event)-(time_of_reference_event) < limit */ $hold(posedge clock, data, tH); /* $setuphold (reference_event, data_event, setup_limit, hold_limit [, notifier]); parameter_restriction = setup_limit + hold_limit > 0 */ $setuphold(posedge clock, data, tSU, tH); /* $width (reference_event, limit, threshold [, notifier]); violation = threshold < (T_data_event) - (T_reference_event) < limit reference_event = edge data_event = opposite_edge_of_reference_event */ $width(posedge clock, tHIGH); /* $period (reference_event, limit [, notifier]); violation = (T_data_event) - (T_reference_event) < limit reference_event = edge data_event = same_edge_of_reference event */ $period(posedge clock, tP); /* $skew (reference_event, data_event, limit [, notifier]); violation = (T_data_event) - (T_reference_event) > limit */ $skew(posedge clock_1, posedge clock_2, tSK); /* $recovery (reference_event, data_event, limit, [, notifier]); violation = (T_data_event) - (T_reference_event) < limit */ $recovery(posedge clock, posedge clock_2, tR); /* $nochange (reference_event, data_event, start_edge_offset, end_edge_offset [, notifier]); reference_event = posedge | negedge violation = change while reference high (posedge)/low (negedge) +ve start_edge_offset moves start of window later +ve end_edge_offset moves end of window later */ $nochange (posedge clock, data, 0, 0); endspecify endmodule
You can use edge specifiers as parameters for the timing-check events (except for the reference event in $nochange):
edge_control_specifier ::= edge [edge_descriptor {, edge_descriptor}]
edge_descriptor ::= 01 | 0x | 10 | 1x | x0 | x1
For example, 'edge
[01, 0x, x1] clock' is equivalent to 'posedge clock'
. Edge transitions with 'z' are treated the same as transitions
with 'x' .
Here is a D flip-flop model that uses timing checks and a notifier register. The register, notifier, is changed when a timing-check task detects a violation and the last entry in the table then sets the flip-flop output to unknown.
primitive dff_udp(q, clock, data, notifier); output q; reg q; input clock, data, notifier; table // clock data notifier:state: q r 0 ? : ? : 0 ; r 1 ? : ? : 1 ; n ? ? : ? : - ; ? * ? : ? : - ; ? ? * : ? : x ; endtable // notifier endprimitive `timescale 100 fs / 1 fs module dff(q, clock, data); output q; input clock, data; reg notifier; dff_udp(q1, clock, data, notifier); buf(q, q1); specify specparam tSU = 5, tH = 1, tPW = 20, tPLH = 4:5:6, tPHL = 4:5:6; (clock *> q) = (tPLH, tPHL); $setup(data, posedge clock, tSU, notifier); // setup: data to clock $hold(posedge clock, data, tH, notifier); // hold: clock to data $period(posedge clock, tPW, notifier); // clock: period endspecify endmodule
The PLA modeling
tasks model two-level logic [Verilog LRM 14.6]. As an example, the following
eqntott logic equations can be implemented using a PLA:
b1 = a1 & a2; b2 = a3 & a4 & a5 ; b3 = a5 & a6 & a7;
The following module loads
a PLA model for the equations above (in AND logic) using the array format
(the array format allows only '1' or '0' in the
PLA memory, or personality array). The file array.dat
is similar to the espresso input plane format.
array.dat
1100000
0011100
0000111
module pla_1 (a1,a2,a3,a4,a5,a6,a7,b1,b2,b3);
input a1, a2, a3, a4, a5, a6, a7 ; output b1, b2, b3;
reg [1:7] mem[1:3]; reg b1, b2, b3;
initial begin
$readmemb("array.dat", mem);
#1; b1=1; b2=1; b3=1;
$async$and$array(mem,{a1,a2,a3,a4,a5,a6,a7},{b1,b2,b3});
end
initial $monitor("%4g",$time,,b1,,b2,,b3);
endmodule
The next example illustrates
the use of the plane format, which allows '1' , '0'
, as well as '?' or 'z' (either may be used for
don't care) in the personality array.
b1 = a1 & !a2; b2 = a3; b3 = !a1 & !a3; b4 = 1;
module pla_2; reg [1:3] a, mem[1:4]; reg [1:4] b;
initial begin
$async$and$plane(mem,{a[1],a[2],a[3]},{b[1],b[2],b[3],b[4]});
mem[1] = 3'b10?; mem[2] = 3'b??1; mem[3] = 3'b0?0; mem[4] = 3'b???;
#10 a = 3'b111; #10 $displayb(a, " -> ", b);
#10 a = 3'b000; #10 $displayb(a, " -> ", b);
#10 a = 3'bxxx; #10 $displayb(a, " -> ", b);
#10 a = 3'b101; #10 $displayb(a, " -> ", b);
end endmodule
111 -> 0101
000 -> 0011
xxx -> xxx1
101 -> 1101
The stochastic analysis tasks model queues [Verilog LRM 14.7]. Each of the tasks return a status as shown in Table 11.12.
TABLE 11.12 Status values for the stochastic analysis tasks. | |
|---|---|
The following module illustrates the interface and parameters for these tasks:
module stochastic; initial begin // stochastic analysis system tasks: /* $q_initialize (q_id, q_type, max_length, status) ; q_id is an integer that uniquely identifies the queue q_type 1=FIFO 2=LIFO max_length is an integer defining the maximum number of entries */ $q_initialize (q_id, q_type, max_length, status) ; /* $q_add (q_id, job_id, inform_id, status) ; job_id = integer input inform_id = user-defined integer input for queue entry */ $q_add (q_id, job_id, inform_id, status) ; /* $q_remove (q_id, job_id, inform_id, status) ; */ $q_remove (q_id, job_id, inform_id, status) ; /* $q_full (q_id, status) ; status = 0 = queue is not full, status = 1 = queue full */ $q_full (q_id, status) ; /* $q_exam (q_id, q_stat_code, q_stat_value, status) ; q_stat_code is input request as follows: 1=current queue length 2=mean inter-arrival time 3=max. queue length 4=shortest wait time ever 5=longest wait time for jobs still in queue 6=ave. wait time in queue q_stat_value is output containing requested value */ $q_exam (q_id, q_stat_code, q_stat_value, status) ; end endmodule
The simulation time functions return the time as follows [Verilog LRM 14.8]:
module test_time; initial begin // simulation time system functions: $time ; // returns 64-bit integer scaled to timescale unit of invoking module $stime ; // returns 32-bit integer scaled to timescale unit of invoking module $realtime ; // returns real scaled to timescale unit of invoking module end endmodule
The conversion functions for reals handle real numbers [Verilog LRM 14.9]:
module test_convert; // conversion functions for reals:
integer i; real r; reg [63:0] bits;
initial begin #1 r=256;#1 i = $rtoi(r);
#1; r = $itor(2 * i) ; #1 bits = $realtobits(2.0 * r) ;
#1; r = $bitstoreal(bits) ; end
initial $monitor("%3f",$time,,i,,r,,bits); /*
$rtoi converts reals to integers w/truncation e.g. 123.45 -> 123
$itor converts integers to reals e.g. 123 -> 123.0
$realtobits converts reals to 64-bit vector
$bitstoreal converts bit pattern to real
Real numbers in these functions conform to IEEE Std 754. Conversion rounds to the nearest valid number. */
endmodule
# 0.000000 x 0 x
# 1.000000 x 256 x
# 2.000000 256 256 x
# 3.000000 256 512 x
# 4.000000 256 512 4652218415073722368
# 5.000000 256 1024 4652218415073722368
Here is an example using the conversion functions in port connections:
module test_real;wire [63:0]a; driver d (a); receiver r (a);
initial $monitor("%3g",$time,,a,,d.r1,,r.r2); endmodule
module driver (real_net);
output real_net; real r1; wire [64:1] real_net = $realtobits(r1);
initial #1 r1 = 123.456; endmodule
module receiver (real_net);
input real_net; wire [64:1] real_net; real r2;
initial assign r2 = $bitstoreal(real_net);
endmodule
# 0 0 0 0
# 1 4638387860618067575 123.456 123.456
The probability distribution functions are as follows [Verilog LRM 14.10]:
module probability; // probability distribution functions:
/* $random [ ( seed ) ] returns random 32-bit signed integer
seed = register, integer, or time */
reg [23:0] r1,r2; integer r3,r4,r5,r6,r7,r8,r9;
integer seed, start, \end , mean, standard_deviation;
integer degree_of_freedom, k_stage;
initial begin seed=1; start=0; \end =6; mean=5;
standard_deviation=2; degree_of_freedom=2; k_stage=1; #1;
r1 = $random % 60; // random -59 to 59
r2 = {$random} % 60; // positive value 0-59
r3=$dist_uniform (seed, start, \end ) ;
r4=$dist_normal (seed, mean, standard_deviation) ;
r5=$dist_exponential (seed, mean) ;
r6=$dist_poisson (seed, mean) ;
r7=$dist_chi_square (seed, degree_of_freedom) ;
r8=$dist_t (seed, degree_of_freedom) ;
r9=$dist_erlang (seed, k_stage, mean) ; end
initial #2 $display ("%3f",$time,,r1,,r2,,r3,,r4,,r5);
initial begin #3; $display ("%3f",$time,,r6,,r7,,r8,,r9); end
/* All parameters are integer values.
Each function returns a pseudo-random number
e.g. $dist_uniform returns uniformly distributed random numbers
mean, degree_of_freedom, k_stage
(exponential, poisson, chi-square, t, erlang) > 0.
seed = inout integer initialized by user, updated by function
start, end ($dist_uniform) = integer bounding return values */
endmodule
2.000000 8 57 0 4 9
3.000000 7 3 0 2
The C language Programming Language Interface ( PLI) allows you to access the internal Verilog data structure [Verilog LRM17-23, A-E]. For example, you can use the PLI to implement the following extensions to a Verilog simulator:
There are three generations of PLI routines (see Appendix B for an example):
'tf_' .
'acc_' and access delay
and logic values. There is some overlap between the ACC routines and TF
routines.
'vpi_'
and are a superset of the TF and ACC routines.