Base_of_Verilog
Base_of_Verilog
August 2023
List of Figures
1 Diagram 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2 Sequence of execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3 Diagram 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4 Diagram 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
5 Operators in Verilog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1
Contents
1 Introduction 3
1.1 What is it About? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2 Levels of Abstraction 4
2.1 Behavioral Level . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Register-Transfer Level . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3 Gate Level . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5 Basics of Verilog 10
5.1 Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.2 Control Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
5.4 Variable Assigments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6 Blocks 14
6.1 Initial Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
6.2 Always Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
6.3 Assign Statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
7 Test Benches 15
9 References 19
2
1 Introduction
1.1 What is it About?
This document is just a revision (for the author) of the most basics Verilog subjects.
This explanations comes from the author experiences and the resources in the references.
Some parts of this text are from ASIC World.
3
2 Levels of Abstraction
Verilog allow us to design a device in three dierent levels of implementations:
1. Behavioral Level.
2. Register-Transfer Level.
3. Gate Level.
4
3 Wire and Reg Data Types
As a HDL language Verilog doesn’t have data types, but wire and reg are more like
drivers. A driver is a data type which can drive a load. Basically, in a physical circuit, a
driver would be anything that electrons can move through/into.
1. Driver that can store a value (example: ip-op).
2. Driver that can not store value, but connects two points (example: wire).
3.1 Wire
Wire connects two points, and thus does not have any driving strength. In the gure
below, in wire is a wire which connects the AND gate input to the driving source, clk wire
connects the clock to the ip-op input, d wire connects the AND gate output to the ip-
op D input.
Figure 1: Diagram 1
There is something else about wire which sometimes confuses. wire data types can be
used for connecting the output port to the actual driver. Below is the code which when
synthesized gives a AND gate as output, as we know a AND gate can drive a load.
1 module wire_example (a , b , y ) ;
2 input a , b ;
3 output y ;
4
5 wire a , b , y ;
6
7 assign y = a & b ;
8 endmodule
What this implies is that wire is used for designing combinational logic, as we all know
that this kind of logic can not store a value. As you can see from the example above,
a wire can be assigned a value by an assign statement. Default data type is wire: this
means that if you declare a variable without specifying reg or wire, it will be a 1-bit wide
wire.
3.2 Registers
Now, coming to reg data type, reg can store value and drive strength. Something that
we need to know about reg is that it can be used for modeling both combinational and
sequential logic. Reg data type can be driven from initial and always block.
Reg data type as Combinational element:
5
1 module reg _combo _examp le (a , b , y ) ;
2 input a , b ;
3 output y ;
4
5 reg y ;
6 wire a , b ;
7
8 always @ ( a or b )
9 begin
10 y = a & b;
11 end
12
13 endmodule
This gives the same output as that of the assign statement, with the only dierence that
y is declared as reg. There are distinct advantages to have reg modeled as combinational
element; reg type is useful when a ”case” statement is required. To model a sequential
element using reg, we need to have edge sensitive variables in the sensitivity list of the
always block.
Reg data type as Sequential element:
1 module reg_seq_example ( clk , reset , d , q ) ;
2 input clk , reset , d ;
3 output q ;
4
5 reg q ;
6 wire clk , reset , d ;
7
8 always @ ( posedge clk or posedge reset )
9 if ( reset ) begin
10 q <= 1 ' b0 ;
11 end else begin
12 q <= d ;
13 end
14
15 endmodule
There is a dierence in the way we assign to reg when modeling combinational logic: in
this logic we use blocking assignments while modeling sequential logic we use nonblocking
ones.
6
4 Blocking and Nonblocking in Verilog
4.1 Blocking Statements
A blocking statement must be executed before the execution of the statements that
follow it in a sequential block. In the example below the rst time statement to get
executed is a = b followed by out d = 0.
7
Example - Blocking:
1 module blocking ( clk , a , c ) ;
2 input clk ;
3 input a ;
4 output c ;
5
6 wire clk ;
7 wire a ;
8 reg c ;
9 reg b ;
10
11 always @ ( posedge clk )
12 begin
13 b = a;
14 c = b;
15 end
16
17 endmodule
Synthesis Output:
Figure 3: Diagram 2
8
Example - Nonblocking:
1 module nonblocking ( clk , a , c ) ;
2 input clk ;
3 input a ;
4 output c ;
5
6 wire clk ;
7 wire a ;
8 reg c ;
9 reg b ;
10
11 always @ ( posedge clk )
12 begin
13 b <= a ;
14 c <= b ;
15 end
16
17 endmodule
Synthesis Output:
Figure 4: Diagram 3
9
5 Basics of Verilog
Some operators and control statements.
5.1 Operators
10
appears to be the same as in C, Verilog is an HDL, so the descriptions should translate to
hardware. This means you’ve got to be careful when using control statements (otherwise
your designs might not be implementable in hardware).
If-else: If-else statements check a condition to decide whether or not to execute a portion
of code. If a condition is satised, the code is executed. Else, it runs this other portion
of code.
1 // begin and end act like curly braces in C / C ++.
2 if ( enable == 1 ' b1 ) begin
3 data = 10; // Decimal assigned
4 address = 16 ' hDEAD ; // Hexadecimal
5 wr_enable = 1 ' b1 ; // Binary
6 end else begin
7 data = 32 ' b0 ;
8 wr_enable = 1 ' b0 ;
9 address = address + 1;
10 end
One could use any operator in the condition checking, as in the case of C language. If
needed we can have nested if else statements; statements without else are also ok, but
they have their own problem, when modeling combinational logic, in case they result in
a Latch (this is not always true).
Case: Case statements are used where we have one variable which needs to be checked
for multiple values. like an address decoder, where the input is an address and it needs
to be checked for all the values that it can take. Instead of using multiple nested if-else
statements, one for each value we’re looking for, we use a single case statement: this is
similar to switch statements in languages like C++.
Case statements begin with the reserved word case and end with the reserved word
endcase (Verilog does not use brackets to delimit blocks of code). The cases, followed
with a colon and the statements you wish executed, are listed within these two delimiters.
It’s also a good idea to have a default case. Just like with a nite state machine (FSM), if
the Verilog machine enters into a non-covered statement, the machine hangs. Defaulting
the statement with a return to idle keeps us safe.
1 case ( address )
2 0 : $display ( " It is 11:40 PM " ) ;
3 1 : $display ( " I am feeling sleepy " ) ;
4 2 : $display ( " Let me skip this tutorial " ) ;
5 default : $display ( " Need to complete " ) ;
6 endcase
Looks like the address value was 3 and so I am still writing this tutorial.
Note: One thing that is common to if-else and case statement is that, if you don’t
cover all the cases (don’t have ’else’ in If-else or ’default’ in Case), and you are trying to
write a combinational statement, the synthesis tool will infer Latch.
While: A while statement executes the code within it repeatedly if the condition it is
assigned to check returns true. While loops are not normally used for models in real life,
but they are used in test benches. As with other statement blocks, they are delimited by
begin and end.
11
1 while ( free_time ) begin
2 $display ( " Continue with webpage development " ) ;
3 end
As long as free time variable is set, code within the begin and end will be executed.
i.e print ”Continue with web development”. Let’s looks at a stranger example, which uses
most of Verilog constructs. Well, you heard it right. Verilog has fewer reserved words
than VHDL, and in this few, we use even lesser for actual coding. So good of Verilog...
so right.
1 module counter ( clk , rst , enable , count ) ;
2 input clk , rst , enable ;
3 output [3:0] count ;
4 reg [3:0] count ;
5
6 always @ ( posedge clk or posedge rst )
7 if ( rst ) begin
8 count <= 0;
9 end else begin : COUNT
10 while ( enable ) begin
11 count <= count + 1;
12 disable COUNT ;
13 end
14 end
15
16 endmodule
The example above uses most of the constructs of Verilog. You’ll notice a new block
called always - this illustrates one of the key features of Verilog. Most software languages,
as we mentioned before, execute sequentially - that is, statement by statement. Verilog
programs, on the other hand, often have many statements executing in parallel. All blocks
marked always will run - simultaneously - when one or more of the conditions listed within
it is fullled.
In the example above, the always block will run when either rst or clk reaches a positive
edge - that is, when their value has risen from 0 to 1. You can have two or more always
blocks in a program going at the same time (not shown here, but commonly used).
We can disable a block of code, by using the reserve word disable. In the above
example, after each counter increment, the COUNT block of code (not shown here) is
disabled.
For Loop: For loops in Verilog are almost exactly like for loops in C or C++. The
only dierence is that the ++ and – operators are not supported in Verilog. Instead of
writing i++ as you would in C, you need to write out its full operational equivalent, i =
i + 1.
1 for ( i = 0; i < 16; i = i + 1) begin
2 $display ( " Current value of i is % d " , i ) ;
3 end
This code will print the numbers from 0 to 15 in order. Be careful when using for loops for
register transfer logic (RTL) and make sure your code is actually sanely implementable
in hardware... and that your loop is not innite.
12
Repeat: Repeat is similar to the for loop we just covered. Instead of explicitly
specifying a variable and incrementing it when we declare the for loop, we tell the program
how many times to run through the code, and no variables are incremented (unless we
want them to be, like in this example).
1 repeat (16) begin
2 $display ( " Current value of i is % d " , i ) ;
3 i = i + 1;
4 end
The output is exactly the same as in the previous for-loop program example. It is relatively
rare to use a repeat (or for-loop) in actual hardware implementation.
5.3 Summary
1. While, if-else, case(switch) statements are the same as in C language.
2. If-else and case statements require all the cases to be covered for combinational
logic.
4. Repeat is the same as the for-loop but without the incrementing variable.
3. There is a third block, which is used in test benches only: it is called Initial
statement.
13
6 Blocks
6.1 Initial Blocks
An initial block, as the name suggests, is executed only once when simulation starts.
This is useful in writing test benches. If we have multiple initial blocks, then all of them
are executed at the beginning of simulation.
1 initial begin
2 clk = 0;
3 reset = 0;
4 req_0 = 0;
5 req_1 = 0;
6 end
In the above example, at the beginning of simulation, (i.e. when time = 0), all the
variables inside the begin and end block are driven zero.
The above example is a 2:1 mux, with input a and b; sel is the select input and y is the
mux output. In any combinational logic, output changes whenever input changes. This
theory when applied to always blocks means that the code inside always blocks needs to
be executed whenever the input variables (or output controlling variables) change. These
variables are the ones included in the sensitive list, namely a, b and sel.
There are two types of sensitive list: level sensitive (for combinational circuits) and
edge sensitive (for ip-ops). The code below is the same 2:1 Mux but the output y is
now a ip-op output.
14
1 always @ ( posedge clk )
2 if ( reset == 0) begin
3 y <= 0;
4 end else if ( sel == 0) begin
5 y <= a ;
6 end else begin
7 y <= b ;
8 end
We normally have to reset ip-ops, thus every time the clock makes the transition from
0 to 1 (posedge), we check if reset is asserted (synchronous reset), then we go on with
normal logic. If we look closely we see that in the case of combinational logic we had
”=” for assignment, and for the sequential block we had the ”¡=” operator. Well, ”=” is
blocking assignment and ”¡=” is nonblocking assignment. ”=” executes code sequentially
inside a begin / end, whereas nonblocking ”¡=” executes in parallel.
We can have an always block without sensitive list, in this case we need to have a
delay as shown in the code below.
1 always begin
2 #5 clk = ~ clk ;
3 end
The above example is a tri-state buer. When enable is 1, data is driven to out, else out
is pulled to high-impedance. We can have nested conditional operators to construct mux,
decoders and encoders.
1 assign out = data ;
7 Test Benches
Ok, we have code written according to the design document, now what?
Well we need to test it to see if it works according to specs. Most of the time, it’s
the same we use to do in digital labs in college days: drive the inputs, match the outputs
with expected values. Let’s look at the arbiter testbench.
1 module arbiter (
2 clock ,
3 reset ,
4 req_0 ,
5 req_1 ,
6 gnt_0 ,
7 gnt_1
15
8 );
9
10 input clock , reset , req_0 , req_1 ;
11 output gnt_0 , gnt_1 ;
12
13 reg gnt_0 , gnt_1 ;
14
15 always @ ( posedge clock or posedge reset )
16 if ( reset ) begin
17 gnt_0 <= 0;
18 gnt_1 <= 0;
19 end else if ( req_0 ) begin
20 gnt_0 <= 1;
21 gnt_1 <= 0;
22 end else if ( req_1 ) begin
23 gnt_0 <= 0;
24 gnt_1 <= 1;
25 end
26
27 endmodule
28
29 // Testbench Code Goes here
30
31 module arbiter_tb ;
32
33 reg clock , reset , req0 , req1 ;
34 wire gnt0 , gnt1 ;
35
36 initial begin
37 $monitor ( " req0 =% b , req1 =% b , gnt0 =% b , gnt1 =% b " , req0 , req1 , gnt0 ,
gnt1 ) ;
38 clock = 0;
39 reset = 0;
40 req0 = 0;
41 req1 = 0;
42 #5 reset = 1;
43 #15 reset = 0;
44 #10 req0 = 1;
45 #10 req0 = 0;
46 #10 req1 = 1;
47 #10 req1 = 0;
48 #10 { req0 , req1 } = 2 ' b11 ;
49 #10 { req0 , req1 } = 2 ' b00 ;
50 #10 $finish ;
51 end
52
53 always begin
54 #5 clock = ! clock ;
55 end
56
57 arbiter U0 (
58 . clock ( clock ) ,
59 . reset ( reset ) ,
16
60 . req_0 ( req0 ) ,
61 . req_1 ( req1 ) ,
62 . gnt_0 ( gnt0 ) ,
63 . gnt_1 ( gnt1 )
64 ) ;
65
66 endmodule
It looks like we have declared all the arbiter inputs as reg and outputs as wire; well, that’s
true. We are doing this as test bench needs to drive inputs and needs to monitor outputs.
After we have declared all needed variables, we initialize all the inputs to known state:
we do that in the initial block. After initialization, we assert/de-assert reset, req0, req1
in the sequence we want to test the arbiter. Clock is generated with an always block.
After we are done with the testing, we need to stop the simulator. Well, we use $nish
to terminate simulation. $monitor is used to monitor the changes in the signal list and
print them in the format we want.
17
8.2 Testbench
1 `timescale 10 ns /1 ps
2 module counter_tb ;
3
4 reg clk_tb , rst_tb ;
5 wire [3:0] out_tb ;
6
7 initial
8 begin
9 clk_tb = 1 ' d0 ;
10 rst_tb = 1 ' d0 ;
11 #5 rst_tb = 1 ' d1 ;
12 #100 rst_tb = 1 ' d0 ;
13 #110 rst_tb = 1 ' d1 ;
14 end
15
16 always
17 begin
18 #5 clk_tb = ~ clk_tb ;
19 end
20
21 counter dut (. clk ( clk_tb ) , . rst ( rst_tb ) , . out ( out_tb ) ) ;
22 endmodule
18
9 References
1. Site: ASIC World, URL:https://www.asic-world.com/verilog/index.html
19