Verilog Tutorial

Download as pdf or txt
Download as pdf or txt
You are on page 1of 27

ECE 451 Verilog Tutorial

James Barnes
([email protected])

Outline















HDL overview, design flow


Hierarchical design and design for verification
Numbers, data types and operators
Builtin primitives (NAND, NOR,)
Control Flow (if-else,)
Continuous and Procedural Assignments
Behavioral coding vs coding for synthesis
Simulator behavior and avoiding problems
Unintended latches
How to avoid mismatch
Some simple examples
Misc system tasks, parameters, defines
Behavioral modelling - delays
FSM example

What is /is-not Covered







We will learn verilog primarily through examples.


Emphasis is on features used in writing
synthesizable verilog.
A few other topics will be covered, but only briefly.
You will need to continue learning verilog to become
familiar with all its features.

HDL Overview




Two main HDLs (verilog, VHDL)


HDLs resemble programming languages (C/C++) but model hardware
concurrency (which sometimes leads to unexpected program flow).
HDL models fall into two types
Behavioral models. These used for:
High-level models of complex systems.
Testbenches (but SystemC becoming more prevalent)
Blackboxing analogish components (PLLs, CDRs, memories)
Behavioral models are not synthesizable.
Sythesizable models (RTL style). This is a coding style that a
synthesis tool, for example synopsys, can map to circuit blocks.
If you put non-synthesizable constructs in your code, the synthesis
tool may silently ignore them, which will lead to a behavior
mismatch between the simulation and the synthesized circuit.

Synthesis tool flow (ASICs)


HDL

HDL Compile Parser


(translation)

Checks syntax, converts HDL to structure, builds


directed graphs. Results in high-level equation netlist

Hierarchical Flattening

Flattens design where except for custom blocks (RAMS,)

Boolean Flattening

Minimize levels of logic, remove intermediate variables,


remove structure. Usually improves speed (expresso)

Boolean Structuring

Creates structure, increases levels of logic, looks for


shared terms. Usually improves area (misII)

Mapping to Technology

Rules Based Optimization

Maps design to target ASIC gate library

Optimizes design for speed using actual gates and


constraints (fanin, fanout).

FPGA synthesis

FPGAs contain a number of pre-assembled complex


logic blocks.
Counters, arithmetic blocks, RAMs, muxes, lookup tables, flops
AND-OR gate arrays of uncommitted logic which
can be programmed to produce a wide range of
logic functions
Your verilog must be written in a form such that the
synthesis tool will recognize the functions
Rules/restrictions are similar to ASIC synthesis.

Verilog supports hierarchical design




Hierarchical advantages
Manage complexity
Promote design reuse
Allow parallel development
Hierarchical features in verilog
modules
ports (connection to modules)

Structure of a module

module full_adder(ci,a,b,sum,cout);
// port declarations
input a,b,ci;
output sum,cout;
// type declarations.
wire a, b, ci, sum,cout;
// assignments
assign sum = a ^ b ^ ci;
assign cout = (a & b) | (a & ci) | (b & ci);
endmodule

Syntax notes
Statements end with ;
Compound statements (see later) are delimited by begin end (like { } in C).
Port directionality and width declared.
Variable types must be declared, as in other programming languages.

Instantiation of a module 4 bit adder slice


module adder4(xsum, xcout, xa, xb, xci);
input [3:0] xa, xb;
input
xci;
output [3:0] xsum;
output
xcout;
wire [3:0]
xa, xb, xsum;
wire
xci, xcout;
wire [2:0]
cout_int; // Internal signal
full_adder a0( .sum
(xsum[0]),
.cout
(cout_int[0]),
.a
(xa[0]),
.b
(xb[0]),
.ci
(xci));
full_adder a1( .sum
(xsum[1]),
.cout
(cout_int[1]),
.a
(xa[1]),
.b
(xb[1]),
.ci
(cout_int[0]));
full_adder a2( .sum
(xsum[2]),
.cout
(cout_int[2]),
.a
(xa[2]),
.b
(xb[2]),
.ci
(cout_int[1]));
full_adder a3( .cout
(xcout),
.sum
(xsum[3]),
.a
(xa[3]),
.b
(xb[3]),
.ci
(cout_int[2]));
endmodule // adder4

adder4 testbench first version


`timescale 1ns/1ps
module tb; // No ports needed!
reg xci;
reg [3:0] xa, xb;
wire [3:0] xsum;
wire
xcout;
// Instantiate 4 bit adder
adder4 my_adder(// Outputs
.xsum(xsum[3:0]), .xcout(xcout),
// Inputs
.xa(xa[3:0]), .xb(xb[3:0]), .xci(xci));
// Stimulus
initial
begin
xa =
#5 xa =
#5 xa =
// yada

4'h0; xb = 4'h0; xci = 1'b0;


4'h0; xb = 4'h0; xci = 1'b1; // #5 -> wait 5ns, then execute stmt
4'h0; xb = 4'h1; xci = 1'b0;
yada yada

end // initial begin


endmodule // tb

How to test?



Looking at waves or logfiles is tedious and error-prone.


Better to build independent verification components which can
stimulate and check the results of the block under test. Selfchecking test.
Must use independent means for implementing block
functionality. In this case, can use simulators built-in
arithmetic operators to check adder.
Other approaches:
System C verification library.
Vendor-supplied verification IP: bus functional models,
0-in components,

A better test jig

top

Checker
module

Stimulus
block

Inputs

Device
under
test
(adder4)

Verification components
can be written in behavioral
verilog, systemC, Verisity
E language
adder4 must be written in
synthesizable verilog
(RTL)

Outputs

adder4 stimulus module


`timescale 1ns/1ps
module stimulus(xa, xb, xci);
output [3:0] xa, xb;
output
xci;
reg [3:0] xa, xb;
reg
xci;
integer
i, j, k;
// Stimulus generate all input combinations
initial
begin
for (i=0; i<16; i=i+1)
for (j=0; j<16; j=j+1)
for (k=0; k<2; k=k+1)
begin
xa = i; xb = j; xci = k;
#5;
end
end // initial begin
endmodule // stimulus

adder4 checker
`timescale 1ns/1ps
module check_adder(xci, xa, xb, xcout, xsum);
input [3:0] xa, xb;
input
xci;
input [3:0] xsum;
input
xcout;
wire [4:0]
wire
reg [4:0]
reg [4:0]
reg

xa, xb, xsum;


xci, xcout;
in_result;
out_result;
error;

always @(xa or xb or xci or xcout or xsum)


begin // Predicted xsum using Verilog's addition
in_result = xa + xb + {3'b0,xci};
out_result = {xcout,xsum};
end
always @(in_result or out_result)
begin // Compare predicted with actual
error <= (in_result != out_result);
if (error)
$display($time, " Error: xa=%h, xb=%h, xci=%b, xsum = %h, xcout = %b",
xa, xb, xci, xsum, xcout);
end
endmodule // check_adder

adder4 top-level test jig


module top;
wire [3:0] xa, xb, xsum;
wire
xci, xcout;
// Instantiate 4 bit adder
adder4 my_adder(// Outputs
.xsum(xsum[3:0]), .xcout(xcout),
// Inputs
.xa(xa[3:0]), .xb(xb[3:0]), .xci(xci));
// Instantiation checker
check_adder my_checker(// Inputs
.xa(xa[3:0]), .xb(xb[3:0]), .xci(xci),
.xsum(xsum[3:0]), .xcout(xcout));
// Instantiate stimulus block
stimulus my_stim(// Outputs
.xa
(xa[3:0]),
.xb
(xb[3:0]),
.xci(xci));
endmodule // top

Number representations


Sized numbers
1b1, 4b1010, 4b0x0z binary
X=unknown, z=high impedance
Other radixes
3o4 octal
4hE - hex
4d11 - decimal
The number in front of the represents the bit width of number
when expressed as a binary, regardless of the radix used
Verilog performs arithmetic on sized numbers using 2s
complement arithmetic.
If size parameter is omitted, defaults to max width (>-32b wide)

Integers
At least 32b wide (simulator-dependent)
Signed, i.e. can write -10

Real
27.3, 4.02e+03
Internal rep as 64b integer

Operators
Type

Width of
Result

Symbol

Arithmetic

>Ops width

+ - * / %

Logical

1 bit

! && ||

Bitwise

Ops width

~ & | ^ ~^

Relational

1 bit

> < >= <=

Equality

1 bit

== != === !==

Reduction

1 bit

& ~& | ~| ^ ~^

Shift

Ops width

>> <<

Concatenate,
replicate

> Ops width

{ , , }

Conditional

Ops width

? :

4b0110 4b0111
yields 5b11111

Note: use ? for


dont cares

Note: vacant
positions zerofilled.

{{ }}

Some data types




wire
Used to represent connections between blocks
No memory value assigned in continuous assignment statement.
Right-hand side can be of reg (see below) or net type.
wire is most commonly used member of net class

Others are wand, wor, tri, triand, trior, trireg.

Used for combinational logic


Limited conditional assignment language features

reg

Has memory but doesn't NECESSARILY imply a hardware register.


Assigned in procedural assignment block.
Right-hand side can be of reg or net type.
Blocking vs non-blocking assignments (more on this later)
Richer set of conditional assigments
real, realtime
For floating point numbers, but represented internally as 64b integers
Supports scientific as well as real (XXX.XX) notation
Used mainly for behavioral modelling.

Scope

Variables (wire, reg,) have local scope (within


module). No global variables in verilog.

Hierarchical Reference to Signals




Signals down within a hierarchy can be referenced


as:
a.b.c.<sig_name>
where a,b,c are module instance names and
sig_name = signal name

Example: at the level of module top, internal signal


cout_int[1] within instance my_adder of module
adder4 can be referenced by
my_adder.cout_int[1]

Cannot assign (change) a signal thru hierarchical


reference, only test. Would only be used in test.

10

Example wire assignments


wire a;
assign a = 1b0; // a is assigned a constant value for the duration of sim

// Define a 4b wide bus


wire [3:0] a = 4h0; // Declaration and assignment on one line.
wire a,b,c,f;
assign f = (~a) & b & c | a & (~b) & c; // Two level logic

wire in1, in0, sel;


wire f = sel ? in1 : in0; // Mux with conditional assignment

wire in1, in0, sel, f;


assign f = sel & in1 | (~sel) & in0; // Another mux
wire [15:0] a, b;
assign {b[7:0],b[15:8]} = {a[15:8],a[7:0]}; // Byte swap

Verilog Built-in Logic Primitives







Can also build logic functions by instantiating logic primitives


AND(), OR(), NAND(), NOR(), XOR(), XNOR(), BUF(),
NOT()
Port connections via an ordered list instead of by name.
Allows variable number of inputs for most gates (AND,).
Output is always first port
Some gates, such as BUF() can drive more than one output
from single input. For these, input is last port.
Tristate gates BUFIF1(), BUFIF0() tristate their outputs when a
control port is 0 or 1 respectively.
Primitives may be instantiated as named or un-named gates
These primitives are not widely used in synthesized designs.

11

Verilog Primitive Example


module mux2_1 (a, b, out, outbar, sel);
input a, b, sel;
output out, outbar;
wire out1, out2, selb;
and a1 (out1, a, sel);
not i1 (selb, sel);
and a2 (out2, b , selb);
or o1 (out, out1, out2);
assign outbar = ~out;
endmodule

out1

sel

out
outbar

selb
b

out2

Operator Examples
wire [3:0] a = 4b0110;
wire [3:0] b = 4b0101;

wire [3:0] s = a & b; // Yields s = 4b0100 (bitwise AND)


wire

t = a && b;

wire

u = |a; // Reduction op | yields u = 1b1; short for (a > 0)

wire

v = ~|a; // Reduction op ~| yields v = 1b0; short for (a == 0)

wire

w = &a; // Reduction op & yields w = 1b0; This tests for all ones

wire

a_gt_b = (a > b); // Yields a_gt_b = 1b1;

// Yields t = 1b1 (logical AND)

Operators can also be used with reg data type

12

RTL control flow constructs




Conditional assignment (? : ), used with continuous assignment

Used in procedural assignments:


if-else
Compound statements in branches delimited by begin end
if-else can result in a priority encoder (slow).
case, casex, casez
casex allows use of dont cares (ex: 4b0?0? will be matched by
4b0001, 4b0101, 4b0100, 4b000X, 4b000Z, )
casez is similar except that dont care positions (?) matched only by
0,1,Z (X will not match).
Any type of case may result in a priority encoder, since the first match
will cause the match to terminate. Mutually-exclusive match conditions
will prevent priority encoding.
for loop. Synthesis tool will unroll.
Others (while, repeat) which I hardly ever used.

forever is a simulator control construct and not synthesizable.

Procedural Assignments


Used to assign reg, integer, real data types

Two types of procedural blocks


initial block

Triggers (starts execution) once at Time = 0.


Synthesis tools ignore initial blocks. Should NOT
be found in synthesizable verilog.

always

@() block

Triggers any time a variable in sensitivity list has a


value change.
Can be used to create combinational or sequential
logic.

13

INITIAL block example (NOT synthesizable)


`timescale 1ns/1ps
module clk_gen(clk);
// One way to create a clock with default frequency 100 MHz.
// Not synthesizable Only for a test component
parameter clk_half_period = 5; // Parameterize the period so we can change it.
output clk;
reg

clk;

initial
begin
clk = 0;
forever
clk <= #clk_half_period ~clk; // This loop continues until something
// terminates the simulation
end
initial
begin
$monitor ($time, " clk=%b",clk);
#1000 $finish; // OK, enough already. Stop the simulation
end
endmodule // clk_gen

ALWAYS block for combinational logic




ALWAYS blocks often used for combinational logic because of richer


set of control statements which compared with continuous
assignments (wire).

module mux(ina, inb, inc, ind, sel, out);


input [3:0] ina, inb, inc, ind;
input [1:0] sel;
output [3:0] out;
wire [3:0] ina, inb, inc, ind;
wire [1:0] sel;
reg [3:0]
out;
// Sensitivity list of the always block must include all inputs to always block
always @(ina or inb or inc or ind or sel) // () contains the sensitivity list
case (sel)
2'b00: out = ina;
2'b01: out = inb;
2'b10: out = inc;
2'b11: out = ind;
endcase // case(sel)
endmodule // mux
Statements

within always block are executed sequentially. In this case, because the
case conditions are mutually exclusive, order doesnt matter

14

ALWAYS block for combinational logic


accidental inferred latch


Omitting a clause in a control construct (case, if-else)


can lead to an unintended inferred latch. This is
almost always an error.

module mux(ina, inb, inc, ind, sel, out);


input [3:0] ina, inb, inc, ind;
input [1:0] sel;
output [3:0] out;
wire [3:0] ina, inb, inc, ind;
wire [1:0] sel;
reg [3:0]
out;
always @(ina or inb or inc or ind or sel)
case (sel)
2'b00: out = ina;
2'b01: out = inb;
2'b10: out = inc;
2'b10: out = ind; // TYPO. This line not executed and 2b11 case missing.
endcase // case(sel)
endmodule // mux


No new value for sel=2b11. Simulation will use previous value and synthesis
tool will interpret that as an inferred latch. Some lint tools will catch this

Another way to accidentally infer a latch




Forgetting an item in the sensitivity list. The always block only


executes when an item in the sensitivity list changes

module mux(ina, inb, inc, ind, sel, out);


input [3:0] ina, inb, inc, ind;
input [1:0] sel;
output [3:0] out;
wire [3:0] ina, inb, inc, ind;
wire [1:0] sel;
reg [3:0]
out;
always @(ina or inb or inc or ind or sel)
case (sel)
2'b00: out = ina;
2'b01: out = inb;
2'b10: out = inc;
2'b11: out = ind; // This line will NOT execute when ind changes
endcase // case(sel)
endmodule // mux

Since out is a reg type, it will hold its previous value when ind changes.
Because this is such a common error, later versions of verilog allow a shorthand
notation always @(*) which will be filled in with the appropriate variables

15

A way to avoid an accidental inferred latch




Precede all assignments with a default value (and


include all items in the sensitivity list)

module mux(ina, inb, inc, ind, sel, out);


input [3:0] ina, inb, inc, ind;
input [1:0] sel;
output [3:0] out;
wire [3:0] ina, inb, inc, ind;
wire [1:0] sel;
reg [3:0]
out;
always @(ina or inb or inc or ind or sel)
begin
out = 4h0;
// Default value
case (sel)
2'b00: out = ina; // This is a three input mux
2'b01: out = inb;
2'b10: out = inc;
endcase // case(sel)
end
endmodule // mux
Simulator

knows what value to assign to out even if no case


condition is satisfied.

Another way to avoid an accidental inferred


latch
 Fully specify all conditional branches and assign all
signals from branches (and include all items in sensitivity list)
module mux(ina, inb, inc, ind, sel, out);
input [3:0] ina, inb, inc, ind;
input [1:0] sel;
output [3:0] out;
wire [3:0] ina, inb, inc, ind;
wire [1:0] sel;
reg [3:0]
out;
always @(ina or inb or inc or ind or sel)
begin
case (sel)
2'b00: out = ina; // This is a three input mux
2'b01: out = inb;
2'b10: out = inc;
default: out = 4h0; // Alternately 2b11: out = 4h0;
endcase // case(sel)
end
endmodule // mux

out

will always be assigned a value.

16

Unintended priority encoder (or maybe this


is what you wanted)

reg out;
wire ina, inb, a, b;
always @(ina or inb or a or b)
begin
// if statements are not mutually exclusive
if (a) out = ina;
else if (b) out = inb;
else out = 1'b0;
end

ina
inb
1

out

1
1b0

0
0

Avoid priority by mutually-exclusive if-else


conditions or case construct
reg out;
wire ina, inb, inc, a, b;
wire [1:0] sel = {a,b};
always @(ina or inb or inc or sel)
begin // The if statements are mutually exclusive
if (sel == 2b11) out = 1b0;
else if (sel == 2b10) out = ina;
else if ((sel == 2b01) out = inb;
else if ((sel == 2b00) out = inc;
end

1b0
ina
inb
inc

3
2

out

All inputs have


equal priority

0
2

sel

17

Another combinational block what do you


get?
reg [63:0] sum;
reg cout;
wire [63:0] ina, inb;
always @(ina or inb)
{cout,sum} = ina + inb; // + is the addition operator

Synopsis will produce whatever is required to meet timing


If you are in a fast process and specify relaxed timing,
synopsis will produce the smallest solution, probably a
ripple-carry adder.
If timing is tight, you will get a full 64b carry-lookahead
adder! Synopsis has a library of prepackaged logic
functions The Designware Library which can
implement many common functions
You can create huge amounts of logic with a few simple
statements, e.g. multiply and divide. Be careful .

Yet another combinational block what do


you get?
reg [63:0] count_ns, count_r;
wire enable_count;
wire rst, clk;
wire counter_rollover = &count_r;
always @(enable_count or count_r)
count_ns = enable_count ? count_r + 64h1 : 64`h0;
always @ (posedge clk)
count_r <= rst ? 64h0 : count_ns;

This will produce a synchronous counter

18

ALWAYS block must be used for sequential


logic (Latches and Flip-Flops)
module transparent_latch(in, clk, rst, out);
input in, clk, rst;
output out;
wire in, clk, rst
reg
out;
// Normally, you would never write this as a synthesized block
// (except by accident) because most timing tools
// don't work well with transparent latches.
always @(in or clk or rst)
// Async reset. Normally avoided like the plague on ASIC designs.
if (rst)
out = 1'b0;
else
if (clk)
out = in;
// No else clause for this if => the latch
// must hold its value when clk is low and rst is not asserted
endmodule // transparent_latch

Inferred D-flop

Synchronous reset
// Infer an 8b wide D-flop bank w/ synchronous rst
always @(posedge clk)
out <= rst ? 8'h00 : din;

Asynchronous reset
// Infer an 8b wide D-flop bank w/ asynchronous rst
always @(posedge clk or posedge rst)
if (rst)
out <= 8h00;
else
out <= din;

These examples use non-blocking assignment <= , discussed on next


slide.

19

Blocking vs Non-blocking assignments




= within procedural block is a blocking assignment

Blocking assignments within an always block are

completed in order, execute only after the


previous statement is completed. If there is a
delay in the previous statement (see later), the
next statement will wait for the delay.
<= within a procedural block is a non-blocking
assignment. Non-blocking assignments do not wait
for the previous statement to complete. They can be
used to model concurrent operations.
RTL does not use delays. A more important
distinction is the order the simulator evaluates these
two assignments

Verilog Evaluation Queues

Each Verilog simulation time step is divided into a number of queues, which are
evaluated in order. The important ones are:

Time 0:

Q1 (in any order) :
Evaluate RHS of all non-blocking assignments
Evaluate RHS and change LHS of all blocking assignments
Evaluate RHS and change LHS of all continuous assignments
Evaluate inputs and change outputs of all primitives
Evaluate and print output from $display and $write

Q2 (in any order) :
Change LHS of all non-blocking assignments

(Remaining queues)
Evaluate and print output from $monitor and $strobe
Time 1:
...
Ref: Sutherland, 1996 International Cadence Users Conference.

20

Avoiding races in simulation (the right way)


D

clk
D

Q
clk

Combinational
Logic cloud

Q
clk

Rule:
code combinational logic with
continuous assignments
(wire) or blocking procedural
assignments (=)
code sequential logic using
non-blocking procedural
assignment (<=)
If the assignment rules are
followed, on the rising clk edge the
old values from the upper flops will
be propagated through the combo
logic cloud to the input to the lower
flop. Only then will the flops be
evaluated. NO delays need (or
should) be put into the modules.

Simple example D flop with load and


synchronous reset
module dff1 (d, ld, rst, clk, q);
input d, ld, rst, clk;
output q;
wire d, ld, rst, clk, q_ns;
reg
q;
/* continuous assignment */
assign q_ns = 1'b0; // WHAT GOES HERE??
/* procedural assignment. always block fires on positive edge of clock */
always @(posedge clk)?
// <= is non-blocking assignment. Synchronous reset.
q <= rst ? 1'b0 : q_ns;
endmodule // dff1

21

Another example T flop

module tff (t, rst, clk, q);


input t, rst, clk;
output q;
wire t, rst, clk, q_ns;
reg q;
/* continuous assignment */
assign q_ns = (t ^ q); // ^ = XOR
/* procedural assignment. always block fires on positive edge of clock */
always @(posedge clk)
// <= is non-blocking assignment. Synchronous reset.
q <= rst ? 1'b0 : q_ns;
endmodule;

Mismatch between pre-synthesis model and


synthesis output (gate model and silicon)

Synthesis tools ignore some behavioral constructs,


such as
INITIAL blocks not realizable in hardware
Delays inserted into model to fix race conditions.
Synthesis tools have a difficult time creating fixed
delays. In general, delays are ignored.
If your module depends on these constructs to
simulate properly, there will be a mismatch between
pre-synthesis and post-synthesis models (and
silicon). This is a VERY BAD thing.
How to avoid this

22

Guidelines for avoiding model mismatch




INITIAL blocks replace with synthesizable hardware such as


reset circuits, busses to set registers,
Dont insert delays to fix races understand the simulator order
of evaluation. At each timestep, the simulator has an evaluation
queue.
Continuous assignments (wire) and blocking procedural
assignments (=) are evaluated first. Both the RHS and LHS
are evaluated.
The RHS of non-blocking procedural assignments (<=) are
also evaluated early.
The LHS of non-blocking procedural assignments are
updated last.
Note that in non-synthesized modules such as verification
components, you can and should use any legal construct.

More rules to avoid problems

Do not mix blocking and non-blocking assignments within one


always block (some synthesis tools will flag this as an error).
Do not assign the same variable in two separate always
blocks.
Some suggested practices:
Only define one module per file and make the file name be
the same as the module name with the .v extension (or
whatever the convention used in the design lab).
An exception to this would be if you have a large collection
of small blocks which will be shared among a group of
designers. In that case, you can create one library file
containing all the blocks.

23

Some system tasks







$display, $monitor
$display prints one line per call. $monitor prints any time a variable
in its list changes.
$stop, $finish
$stop stops sim, but it can be restarted from that point.
$finish ends sim. In many cases, the sim will stop on its own
without a $finish, but if it contains an infinite loop (such as a clock
generator), $finish is needed.
Generic IO tasks $fopen, $fwrite, Used for saving simulation
data.The development environment usually handles this behind the
scenes.
$random generates a random number.
$readmemb, $readmemh
For initializing memory arrays from a file

parameters and `define statements

`define used for readability and compilation control


Ex `define WORD_SIZE 32
`defines are dangerous because they are just a macro
substitution by preprocessor. Can be re-defined by other
modules. If using, safest to re-define in each module where
used (but then you may get compiler warnings).
parameter is safer because
can be sized (Ex parameter [3:0] maxval = 4hF;)
Scoped only within module. Can be overridden only
when module instantiated.
parameter recommended for FSM state mnemonics

24

parameter example

module par_reg(q, d, clk, rst);


parameter width = 8; // Default width
input [(width-1):0] d;
input
clk, rst;
output [(width-1):0] q;
reg [(width-1):0] d;
always @(posedge clk)
q <= rst ? {width{1'b0}} : d;
endmodule // par_reg

parameters can be overridden at


instantiation
module test_par_reg;
reg [15:0] d;
wire [15:0] q;
wire
clk, rst;
// Instantiate a 16b reg
par_reg #(.width(16))
my_par_reg(.q(q[(width-1):0]), .d(d[(width-1):0]), .clk(clk), .rst(rst));
clk_gen cgen(.clk (clk));
initial
begin
d=16'h0000;
rst=1;
#6 rst=0;
d=16'h5555;
#10
d=16'hAAAA;
#10
d=16'hFF00;
#10
d=16'h00FF;
end // initial begin
initial
$display($time, " d=%h, q=%h",d,q);
endmodule // test_par_reg

25

Behavioral Modeling -- Delays

Delays are never required (or recommended) in


synthesizable verilog but can be useful in behavioral
modules such as stimulus blocks,
Any block which uses delay should have a timescale
statement.
Delays can be attached to net types (wire) or
incorporated in procedural block assignments.

Some delay usage examples


Consult a verilog reference before using delays there are subtle differences in how delays
are evaluated depending on how they are put into the code, which we cant cover here..
wire a, b;
wire #5 gate_out; // This is a net delay
assign gate_out = ~(a & b); // gate_out will change 5 units after a or b
wire a, b;
wire gate_out;
assign #5 gate_out = ~(a & b); // gate_out will change 5 units after a or b

wire a, b;
reg gate_out;
always @(a or b)
gate_out <= #5 ~(a & b); // gate_out will change 5 units after a or b

wire in, clk, rst;


reg out;
always @(posedge clk)
out <= #5 rst ? 1b0 : in; // models clock to output delay

26

State machine example

27

You might also like