|
In this chapter, we are going to overall look on
verilog code structure. You will learn about
initial and always blocks, understand where to use ‘reg’
and ‘wire’ data types. Also, you will understand how HDL (Hardware
Description Language) defers from a software language. I will use a
counter as example for this chapter.
Consider a 4-bit asynchronous counter; block diagram using flip-flops is
as follows. This is a simple counter without reset or load options.

Now look at this code in Verilog.
|
// Asynchronous
counter using Verilog
// By HarshaPerla for electrosofts.com
module counter(
clk, count );
input clk;
output[3:0]
count;
reg[3:0]
count;
wire clk;
initial
count =
4'b0;
always @(
negedge clk
)
count[0]
<= ~count[0];
always @(
negedge count[0]
)
count[1]
<= ~count[1];
always @(
negedge count[1]
)
count[2]
<= ~count[2];
always @(
negedge count[2]
)
count[3]
<= ~count[3];
endmodule
|
Now you know why the keywords module, input and output are used. Let us
have a detailed look at reg and wire data
types. reg is used
where the data assigned to it is to be stored until the next assignment.
But wire is used if only connection is needed to some other signal. That
is, if you have to pull-in some signal from another module through module
instantiation you have to use wire, because there is a possibility of
changing the state of the same signal from both the sides of the module;
that is impossible. Keep following points when you select a
reg or wire.
-
Left hand side of a continuous assignment statement
should be a wire. (Signal will always be connected, no need to store
it.)
-
Left hand side of assignment operations should have a
reg(or similar kinds like integer) type of signal.
(Value need to be retained in the signal here.)
-
All the inputs to a module should be wire.
-
Output of a module can be of any type.
-
All inout signals should
be wire in both the sides of port. (See the diagram bellow: module in is
instantiated inside module out.)
We will
learn about more signal types and usage in the next chapters. Going back
to the counter code, we have one initial block and four always blocks.
Like this you may have any number of blocks in-between module and endmodule. Initial and always blocks are explained bellow.
initial block:
initial block executes when simulation
starts. Inside an initial block there may be one or more statements. If
there is only one statement, we can write it as follows:
If there are more than statements are there to execute, we
can use begin.....end or fork.....join statements to combine them.
Within begin....end, statements will executes one after another
sequentially. Within fork...join, all the statements will executes
simultaneously. More details about begin-end and fork-join you will learn
in next chapter.
initial
begin
a = 1;
c = a;
end
// Both a and c will become 1.
|
Initial block may contain if conditions, loops,
assignment statements etc., but not continuous assignments. You can have
any number of initial blocks in a module. All initial blocks execute
simultaneously.
Initial blocks are very useful in testbenches. Consider a code for
counter. Actually in a chip, initially counter will have a unpredicted
value. But after sometimes, value will increase to its maximum value,
where all the bits are zero. Then it will start counting through 0 to
maximum value. But in the simulation, initially value will be 'x', and if
we don't initialize, it will remain 'x' only. So, we can use initial
blocks to initialize all the signals to a valid value.
Always Block:
Always blocks
execute always. We can use delay or sensitivity list to control the
execution of always block. Syntax is as follows:
always @ (
sencitivity_list )
statements;
Sensitivity list
may contain signal names as follows:
always @ ( signal1
or signal2 )
statement;
Above statement
will execute whenever signal1 or signal2 changes. we can have 1 to any
number of signals in sensitivity list.
always @ (
posedge signal )
statement;
Above statement
executes at the positive edge of signal. Similarly we can use negedge for
negative edge of the signal.
Without sensitivity
list, always block will look like this:
always
#5 clk = ~clk;
This will toggle
the signal clk for every 5 nanoseconds.
More about always block, you will learn after studying some examples.
Blocking and Non-blocking Assignments:
If a set of
statements has to be executed one after another, we have to use blocking
statements. Commonly, we will use blocking statements in combinational
logics. Blocking assignments are done using '=', as in a = b;.
Nonblocking assignments
are commonly used to implement sequential logic. Non blocking statements
without delay in between will execute simultaneously. If you use
non-blocking assignments, expressions will be evaluated depending on the
delay added in-between and will be assigned simultaneously. Consider these
code:
always @( posedge clk )
begin
a <= b;
b <= a;
end
/*
Value of a and b will be swapped.
Commonly used in sequential logics
*/
|
always@
( b )
begin
a = b;
b = a;
end
/*
Value of b will be copied to both a and b. Commonly used in
combinational logic
*/ |
In the next
chapter, you will learn these things more clearly. Now, look back at the
counter code. Now you can realize how the counter is representation of the
circuit.
module counter_tb;
reg clk;
wire[3:0] count;
counter my_counter( .clk(clk), .count( count ) );
initial
begin
clk = 0;
#200 $finish;
end
always
begin
#2 clk = ~clk;
end
always @( posedge clk)
$display("Count = %b", count );
endmodule
|
You can test these codes and see the output.
Prev. : Verilog Operators
Verilog: Table of Contents
Ch: 1. 2.
3. 4.
5. 6.
7. 8.
9. 10.
11. 12. 13.
Next: begin-end and fork-join |