You are on page 1of 13

CLAB2

Combinational and Sequential Designs


using Verilog
ENGN3213
Digital Systems and Microprocessors

Copyright 2012-13, The Australian National University

ANU College of Engineering and Computer Science

ENGN 3213 Digital Systems and Microprocessors

TABLE OF CONTENTS
1. INTRODUCTION ................................................................................................................................ 3
2. AIMS OF THE LAB ............................................................................................................................. 3
3. COMBINATIONAL DESIGNS ............................................................................................................. 4
3.1. Designing the 1-bit Full Adder ...................................................................................................... 4
3.1.1. Writing Verilog Code .............................................................................................................. 4
3.1.2. Simulating the code ............................................................................................................... 6
3.2. Designing the 4-bit Full Adder [5 marks] ...................................................................................... 7
4. TIMING HAZARDS AND THE SYNCHRONISER .............................................................................. 8
4.1. A seemingly banal combinational circuit ...................................................................................... 8
4.1.1. The ideal circuit [1 mark] ....................................................................................................... 8
4.1.2. The non-ideal circuit and a timing hazard [2 marks].............................................................. 9
4.1.3. The synchroniser [2 marks] ................................................................................................... 9
5. SEQUENTIAL DESIGNS .................................................................................................................. 10
5.1. Binary Counters [5 marks] .......................................................................................................... 11
5.2. Clock divider and heartbeat generator [5 marks] ....................................................................... 11

ANU College of Engineering and Computer Science

Page

2/13

ENGN 3213 Digital Systems and Microprocessors

1. INTRODUCTION
During this lab we will go through some coding examples in Verilog (a type of Hardware Description
Language or HDL) and simulate them using Icarus Verilog + GTKwave. Icarus Verilog is a commandline application which can compile Verilog code (.v files) and deliver simulation results based on the
conditions specified in a test-bench file.
It is strongly recommended that you watch the introductory video on using Icarus Verilog and
GTKwave which can be downloaded from the course website. You should also make sure that
you can run Icarus + GTKwave (and a suitable text editor) on your own computer so that you
can do some coding exercises at home. See the Software Info document (on Wattle) for further
details on individual softwares.
A common error made by students is to consider Verilog as a programming language, while it is not.
As well as peculiar ways of executing statements which are different to anything you may have
encountered in previous programming experiences, the key fact about other hardware description
languages is that they are meant to simplify the description of viable electronic designs.
Therefore, the general composition of a design must be clear in your head before you start
writing code. The risk of writing abstract code is to create designs which may (or may not)
work in simulations but are not realisable in practice (and remember that the ultimate goal of this
exercise is to create functioning hardware!).
The Lab revolves around three distinct activities:
First you will learn how to design the now well-known full adder (1-bit) using Verilog instead
of schematics and you will understand how to structure a test bench file which will be used
to run simulations. The lab demonstrator will take you through the process. Then, you will
design a 4-bit adder with minimal assistance to put your understanding to the test.
Later, you will run an example of timing hazards in an asynchronous circuit and appreciate
the importance of a synchroniser, a very simple (yet very useful) sequential circuit.
Finally, this lab will introduce you to your first sequential logic design: a counter and
heartbeat generator.
Make sure that you save all files you create during Clabs (e.g., to a USB drive) for re-use at
home and in future Clabs/Hlabs.

2. AIMS OF THE LAB


To get some supervised, hands-on experience in writing Verilog code
To practice simulating designs using Icarus Verilog and viewing results using GTKwave
Acquire experience with specific coding skills for combinational designs, sequential designs
and test benches
Appreciate the power of hierarchical design through module instantiation
Understand and visualise the concept of timing hazards and synchronisation
Understand counters as a basic but important application of sequential logic

ANU College of Engineering and Computer Science

Page

3/13

ENGN 3213 Digital Systems and Microprocessors

3. COMBINATIONAL DESIGNS
Combinational designs concern circuits for which the time dimension is not significant and the
behaviour can be expressed entirely in terms of a truth table. From the lectures, we summarise the
following "good practice" rules for coding combinational designs in Verilog:
Value assignments inside the design module must be done with all inputs on the RHS, and all
outputs on the LHS
No feedback loops permitted (this follows from the previous rule)
Use the always @(*) sensitivity list for procedural blocks
Use only blocking assignments "="
If statements must be complete, i.e., all variables changed within an if statement should
have a value assigned to them regardless of whether the conditional clause is true or false
(use of else or resort to default value assignments).

3.1. Designing the 1-bit Full Adder


You should know this particular design quite well by now. The logic-based schematic of the 1-bit full
adder is shown in Figure 1 below (this description has one less gate than the one shown in Hlab1).

Figure 1: logic gate description of the 1-bit adder

3.1.1. Writing Verilog Code


Start up the text editor. Under Windows, Notepad++ can be used (do not use the standard Windows
Notepad application), while under Linux you may use either Gedit or Nano. The instructions in the
rest of the document refer to the Windows OS as it is the one we use in R103. For Linux instructions
refer to the Software Info guide from the course website.
Create two new files and save them as fulladder.v and TB_fulladder.v.
Copy the code below (Code 1 and Code 2) as the content of the files.

ANU College of Engineering and Computer Science

Page

4/13

ENGN 3213 Digital Systems and Microprocessors


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------//fulladder.v
//note this is equivalent but not exactly the same as Figure 1
module fulladder( input wire a,
input wire b,
input wire cin,
output reg s,
output reg cout);
always @ ( * )
//note sensitivity list
begin
s = a ^ b ^ cin;
//note blocking assignments, ^ is XOR
cout = (a & b) | (a & cin) | (b & cin);
//note AND, OR symbols; brackets usage
end
endmodule
------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Code 1: Verilog code for the 1-bit adder


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------//TB_fulladder.v
//note this file has no timescale specification (default 1 sec)
module TB_fulladder;
reg a;
//note in TB inputs to the UUT are reg, outputs are wire
reg b;
reg cin;
wire s;
wire cout;
fulladder UUT(
.a(a),
.b(b),
.cin(cin),
.s(s),
.cout(cout));

//instantiation of full adder (UUT: Unit Under Test)

initial begin
$dumpfile("fulladder.vcd"); //note name of the file containing the sim results
$dumpvars;
end
initial begin: stopat
#10; $finish;
//specifies duration of simulation
end
initial begin
a = 1'b0;
b = 1'b0;
cin = 1'b0;

//input initialisation

#1a=1'b1;
b=1'b0;
cin=1'b0;

//at every 1 time step (#1) input values are changed

#1a=1'b0;
b=1'b1;
cin=1'b0;

//note notation 1'b0 to specify single-bit binary signal

#1a=1'b1;
b=1'b1;
cin=1'b0;
#1a=1'b0;
b=1'b0;
cin=1'b1;
#1a=1'b1;
b=1'b0;
cin=1'b1;
#1a=1'b0;
b=1'b1;
cin=1'b1;
#1a=1'b1;
b=1'b1;
cin=1'b1;
end
endmodule
------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Code 2: Verilog test bench code for the 1-bit adder

ANU College of Engineering and Computer Science

Page

5/13

ENGN 3213 Digital Systems and Microprocessors

Before proceeding further, pay attention to a quick run through of the main characteristic of the files
by your demonstrator.
3.1.2. Simulating the code
To simulate the code follow the steps listed below:
STEP 1: Open a command prompt window (from the Windows Start menu, select Run, then type cmd)
STEP 2: Navigate to the disk drive containing the code files using the dos command
<drive letter>: (check the drive letter associated with your USB or network drive in My
computer); then use the command cd <path> to navigate to the folder containing your
Verilog files. The command prompt will list the name of the current folder. See Figure 2 for a
screenshot.

Figure 2: Navigating to the right location using the command prompt

STEP 3: Launch the Icarus compiler by typing the command


iverilog [target file name] [TB file name]
If the compilation is successful, the program will return to the command prompt without
showing any output on screen. The file a.out will be created inside the current folder.
STEP 4: Launch the Icarus simulator program vvp by typing
vvp a.out
the program will return the text "VCD info: dumpfile fulladder.vcd opened for output"
STEP 5: Launch GTKwave to view the traces by typing
gtkwave fulladder.vcd
You can see the operations for steps 3-5 summarised in Figure 3.

Figure 3: Command-line operation of Icarus Verilog

ANU College of Engineering and Computer Science

Page

6/13

ENGN 3213 Digital Systems and Microprocessors

STEP 6: As the GTKwave window appears, take some time to try and figure out how to view the
traces (you can drag and drop). Your output should look something like Figure 4. Check that
the system is indeed performing as intended.

Figure 4: A screenshot of GTKwave displaying the simulation results for the 1-bit adder

NOTE: if you make changes to the source file and wish to run the compiler again, make sure you
delete the old output files first by typing del a.out <target file name>.vcd (e.g.,
fulladder.vcd in this example). This will ensure that the old output files are deleted and if anything
fails during compilation you will not make the mistake of opening the old output files while
thinking they relate to new code.

3.2. Designing the 4-bit Full Adder [5 marks]


Next, you will use Verilog to code a 4-bit adder, i.e., a combinational design which takes two 4-bit
inputs and returns their sum, as well as a carry out bit which takes the value 1 when the sum
overflows (we can assume the carry in bit to be 0 for this exercise). We will do this by instantiating 4
copies of the 1-bit adder which you have already coded and connecting them suitably to achieve
the result we like (for appropriate adder-to-adder interconnections you can take inspiration from the
8-bit adder from Clab1.

Together with your Lab partner, you should (indicative time: approximately 30 min):
1.

Create a new Verilog file fulladder4.v which will describe your module fulladder4
TIP: put it in the same folder as fulladder.v otherwise the instantiation of the fulladder module may fail.
If the compiler still complains that it can't find fulladder.v, you may need to add the line of code
`include "fulladder.v"
as the first line of your fulladder4.v code.

2. Define suitable input and output busses (vectors of wires/regs) for the fulladder4 module
TIP: remember the definition syntax (two examples are shown below)
input wire [<bus size, e.g., 1:0>] a;
output reg [<bus size, e.g., 1:0>] c;
Also remember that the reg type is only for data that receives a value assignment inside a
procedural block (always). While inputs are almost always of type wire, outputs need not always
be of type reg, they can be wire also (in this example, it really depends on your coding choices).

ANU College of Engineering and Computer Science

Page

7/13

ENGN 3213 Digital Systems and Microprocessors

3. Instantiate 4 copies of the 1-bit full adder


TIP: the code below will remind you of how to create an instance of fulladder
fulladder <name of instance>(
.a(<name of wire connected to a>),
.b(<name of wire connected to b>),
.cin(<name of wire connected to cin>),
.s(<name of wire connected to s>),
.cout(<name of wire connected to cout>));
The name after the . symbol is the name of the input/output port in the instantiated code (in this
case, inside fulladder.v we have a reference to a, b, cin, cout, and s), while in brackets is the
name of the wire connected to that port in the higher level module (here, fulladder4)

4. Correctly interconnect the 4 instances


TIP: You can define wire and reg entities that are not inputs or outputs
The input/output ports of an instantiated module can only be connected to a wire, not to a reg

5.

If you wish to use a procedural block (always) in your code (it is not necessary here), make
sure you write correct value assignments to your reg variables.

6. Write a testbench file TB_fulladder4.v (you can take inspiration from Code 2).
TIP: as you set the values for your test bench inputs to fulladder4, make sure you use an expression
that is consistent with the new port size. a = 1'b0, for example, would be incorrect.

7. Run your compiler and simulation (as we did in Section 3.1.2.), review the results in
GTKwave and show your demonstrator once you are satisfied that all is working correctly.

4. TIMING HAZARDS AND THE SYNCHRONISER


4.1. A seemingly banal combinational circuit
Open the lab resources from the course website and obtain the file synchronizer.zip. It contains four
files nosync.v, nosync_haz.v, sync_haz.v, and TB.v. Unzip them in a subfolder of your choice on your
working drive. The files refer to the design shown in Figure 5.

Figure 5: the simple circuit used throughout Section 4

4.1.1. The ideal circuit [1 mark]


Using Icarus, simulate the circuit nosync.v using the test bench TB.v (yes, I realise we did not keep to
the good file naming practice in this instance, but I promise it is the last time you will see this happen)
and have a look at the output traces in GTKwave (the dump file name is "sync_example.vcd"). Note
what happens to the output z as the input x varies (don't look at q, it is not significant here). Make a
note of your explanation of what you see. Is that surprising?

ANU College of Engineering and Computer Science

Page

8/13

ENGN 3213 Digital Systems and Microprocessors

4.1.2. The non-ideal circuit and a timing hazard [2 marks]


Now have a look at the following Verilog code for nosync_haz.v (Code 3).
--------------------------------------------------------------------------------------------//nosync_haz.v
`timescale 1ns / 1ps
module sync_example( input wire x,
input wire clk,
//NOTE clock not required here
output reg z,
output reg q);
//NOTE output q not required here
wire not1;
wire not2;
assign #0.5 not1=~x;
assign #0.5 not2=~not1;

//NOTE the #0.5 unit [ns] delay

always @ ( * )
begin
#0.5 z=x ^ not2;
q=1'bx;
//q assigned non-significant value
end
endmodule
---------------------------------------------------------------------------------------------

Code 3: Verilog code for nonsync_haz.v

The #0.5 you see in certain value assignments means that a delay of 0.5 time steps has been
introduced (NOTE: this is only meaningful in simulations, not in implementation). In this case it means
we associate a delay of 0.5 nanoseconds with the change of logic level for each gate. Simulate the
circuit using the test bench file TB.v (same as before) and look at the traces for x and z. Are they
different from before? Try and interpret them and make a note of your explanation of what you see.
The situation you are looking at is called a timing hazard. Delays in the switching of individual
components can determine an incorrect reading (albeit only temporarily) at the output.
4.1.3. The synchroniser [2 marks]
Finally, have a look at the file sync_haz.v. Here, a synchroniser has been added to the same circuit
as before. A synchroniser (in the very primitive form implemented here) can be interpreted as a
*
positive-edge triggered flip-flop , i.e. a digital circuit that will read the value at its input only as the
rising edge of a trigger signal is encountered and that value is stored until the next rising edge. Figure
6 below shows a general D-type flip flop and its truth table (where x indicates a "don't care" case)

D
Q
CLK
Figure 6: D-type flip flop truth table and block description. D input, Q output, CLK trigger signal (clock)

This is actually a tricky proposition which warrants further discussion on metastability. See Wakerly Ch 8.9 for a
detailed discussion of why a single flip-flop as a synchroniser may not be a great idea in some cases.

ANU College of Engineering and Computer Science

Page

9/13

ENGN 3213 Digital Systems and Microprocessors

Here is the code for this example (Code 4). Look at the syntax for the added synchroniser, which
reads the value of the combinational circuit only as it encounters a rising clock edge (the clock is set
at 100kHz, i.e. a change of level every 5ns, this is specified in the test bench file which you may want
to examine if you are interested).
--------------------------------------------------------------------------------------------//sync_haz.v
`timescale 1ns / 1ps
module sync_example( input wire x,
input wire clk,
output reg z,
output reg q);
wire not1;
wire not2;
assign #0.5 not1=~x;
assign #0.5 not2=~not1;
always @ ( * )
begin
#0.5 z=x ^ not2;
end
always @ (posedge clk)
//synchroniser triggered by rising edge
begin
q<=z;
end
endmodule
---------------------------------------------------------------------------------------------

Code 4: Verilog code for sync_haz.v

Simulate the circuit using the usual test bench TB.v and display all traces (input x, clock, outputs z
and q). What does the presence of the synchroniser do to the glitches? Can you explain what you
observe? Make a note of your reasoning and discuss your understanding with your demonstrator.
What you can appreciate from this example is that for asynchronous designs, i.e. designs which react
instantly to a change of input, it is critical to remove all timing hazards in order to ensure appropriate
operation. In a synchronous design the hazards we have encountered so far may not constitute a
problem, as long as the time between consecutive rising edges of the clock is long enough to allow
the signals to settle to their final value.
All sequential logic systems which we will study in this course will be synchronous, i.e., the
flow of information within the system is timed by steps dictated by a system clock.
Signals from the outside world are rarely (if ever) synchronous. In many applications, a synchroniser
is therefore necessary to ensure that we can use those signals in our sequential designs.

5. SEQUENTIAL DESIGNS
Sequential designs concern circuits for which the time dimension is important. The value of the output
of a sequential system is not only a function of its current inputs, but also of its past inputs, that is,
sequential circuits display a sort of memory behaviour. In synchronous designs (which are the ones
we learn about in this course), time-keeping is provided by a clock signal (most often a square wave
signal which regularly alternates between logic levels 0 and 1). The clock, with its rising edges (or
ANU College of Engineering and Computer Science

Page

10/13

ENGN 3213 Digital Systems and Microprocessors

falling edges, depending on the type of design), dictates the rate at which information flows through
the system.
From the lectures, we summarise the following "good practice" rules for coding sequential designs in
Verilog:
Use an edge sensitivity list with a single clock entry, e.g., always @(posedge sysclk)
Use only non-blocking assignments "<="
The clock triggers the execution of procedural blocks but it does not appear inside the block
If statements do not need to be complete
Feedback loops are fine
Any external signals must be synchronised (as discussed in the previous section)

5.1. Binary Counters [5 marks]


Counters are at the core of many useful sequential circuits including clock dividers, sequencers, delay

generators etc. The following code (Code 5) shows the general format of an 8-bit up-counter with
reset in Verilog. The reset will usually come from an external signal such as a push button.
--------------------------------------------------------------------------------------------module counter8(input wire clk,
input wire reset,
output reg [7:0] count);
always @(posedge clk)
begin
if(reset) count <= 8'h00;
else count <= count + 1'b1;
end
endmodule
---------------------------------------------------------------------------------------------

Code 5: Verilog code for an 8-bit counter

The counter simply overflows when it reaches 8hff (binary 11111111). Note that a 1b1 is added to
count and not a 1. This is good coding practice: "1" could be interpreted as a 32-bit integer and cause
problems during synthesis when added to an 8-bit value. Once you are comfortable with the code
above, proceed to the exercise below.
Together with your lab partner, you should (indicative time 30 min):
1. Draw a schematic of the counter with reset (hint there are many ways to do this, one option
involves the following: 8 D-type flip flops and an 8-bit adder)
2. Write a testbench and simulate Code 5. Set the simulation length in such a way that you can
see the entire 256 clock cycle count range.
3. Use the above as a template to create Verilog code for a down-counter that is reset to 8'hff.
Simulate your design and show your results to the demonstrator.

5.2. Clock divider and heartbeat generator [5 marks]


The clock divider and the heartbeat generator are simple extensions of the counter. With a clock
divider, we can generate a slower clock from the much faster system clock. See Code 6 for an
example. Note that you may require a large number of bits in the count register to produce a slow
27
clock from a 50MHz clock (e.g., with 27 bits the output beat changes value every 2 =134217728
clock cycles if I have calculated this correctly the slow clock should have a frequency of ~0.186Hz).

up-counter means that the count starts from 8'b0000000 and increases at each step, up to a maximum of
8'b11111111.

ANU College of Engineering and Computer Science

Page

11/13

ENGN 3213 Digital Systems and Microprocessors


--------------------------------------------------------------------------------------------module clockdiv(input wire clk,
input wire reset,
output reg beat);
reg [7:0] count;
always @(posedge clk)
begin
if(reset)
begin
beat <= 1'b0;
count <= 8'h00;
end
else if(&count)
begin
beat <= ~beat;
count <= count + 1'b1;
end
else
count <= count + 1'b1;
end
endmodule
---------------------------------------------------------------------------------------------

Code 6: Verilog code for a sample 8-bit clock divider

Another way of specifying a specific count would be to replace the second if statement in Code 6
with something along the lines of
if(count == <chosen target count value, e.g., 8'hab> ) beat <= ~beat;
/*you also need to reset the count*/

This would allow you to create beat lengths of any arbitrary duration.
A heartbeat generator is a similar circuit but instead of creating a slower clock with a 50% duty cycle
(as in the above clock dividers), a single blip of 1 clock cycle duration is generated at a specified
time or at regular intervals (a bit like a heart beat signal). Heartbeat generators are quite useful as
event sequencers, i.e., when used to trigger the start of some other circuit event with precise timing.

Figure 7: Clock (black), Clock divider (blue) and Heartbeat generator (red) outputs

Now back to you for some more Verilog practice.

ANU College of Engineering and Computer Science

Page

12/13

ENGN 3213 Digital Systems and Microprocessors

Together with your Lab partner, you should (indicative time 30-45 min):
1. Change the code in Code 6 to produce a slow clock with a frequency of exactly 1kHz. Verify it
in a simulation.
2. Create a new module from Code 6 and call it heartbeat.v. Remove the if else chain and
replace it with a case statement, which will allow you to generate a single pulse of length 1
clock cycle when the count reaches the following values: 23, 34, 188. This can be used as an
event sequencer. Verify your design in a simulation.
3. Show your results to the Lab demonstrator
HINTS: (a) the test bench for module clockdiv, called TB_clockdiv.v, is provided (runs only for the first
50 microseconds to avoid blowing up the VCD file size).
(b) if you need information on the syntax of the case statement or an example you can refer
to your notes or the Verilog reference in the appendix of the old reading brick (on Wattle).
(c) you may wish to use the default case to bring the output back to 0.

ANU College of Engineering and Computer Science

Page

13/13