Debugging Programs
The ‘best’ way to debug a Verilog program is by writing a testbench and plodding through the simulatated waveforms. However, it is often worthwhile to check for some common mistakes first. Here are a few that I found useful to look out for.
These have been divided into general Verilog programming errors, and some specific to the FPGA setup used in EECS 270.
Table of Contents
General
- Ensure Objects/nets are Correctly Sized
- Ensure Port Widths Match Connections
- Ensure Correct Port Connections when Instantiating Modules
- Check for Uninitialised/Misspelt Variables
- Check for Unintended Latches
- Check for Incorrect Variable Names
EECS 270 Specific
General
Ensure Objects/nets are Correctly Sized
If the hardware structures you are using do not have enough bits to store all the information you want them to, that can cause issues.
For example, if your state machine has 4 states, but you only have a 1 bit state register (reg state
), you’re likely to run into trouble when you enter states 2 and beyond (state <= 2
will overflow).
This also occurs when you do not specify sizes for a reg or wire (or explicitly declare a wire)—its size defaults to 1 without warning.
parameter ready = 2'b0
parameter steady = 2'b1
parameter go = 2'b2
parameter stop = 2'b3
reg state;
reg [1:0] State;
wire [1:0] Next_State;
@(posedge clock) begin
always// this assignment will overflow
<= go;
state // this assignment will not
<= go;
State end
// This assignment will implicitly declare a 1-bit wire next_state and overflow
assign next_state = Stop;
// This will not overflow, as the 2-bit wire bus has been declared
assign Next_state = Stop;
Ensure Port Widths Match Connections
Mismatched Port widths can lead to errors. Sometimes these are only listed as warnings by the compiler, be on the lookout for those!
Ensure Correct Port Connections when Instantiating Modules
Verilog compilers can be very lenient at times. For example, the following code may not even trigger a warning:
module MAJ(a, b, c, m);
input a, b, c;
output m;
and a1(ab, a, b);
and a2(ac, a, c);
and a3(bc, b, c);
or o(m, ab, ac, bc);
endmodule
module Testbench();
reg a;
reg b;
reg c;
wire m;
// there is no connection for port c!
(.a(a),
MAJ m (b),
.b(m));
.mendmodule
This should be checked for, especially for modules with many ports.
Check for Uninitialised/Misspelt Variables
Oftentimes, your might declare a variable and forget to use it. Or misspell it. This can cause issues especially when it is used later, as an undefined (z
) or invalid (x
) value might be propagated throughout your design. Such an error might not be detected when flashing your design onto an FPGA, as the variable might initialise to a 0
or 1
depending on the specific hardware being used.
wire forgotten;
reg memory;
@(*) begin
always= forgotten ^ forgotten;
memory end
The above example would assign forgotten
a value of z
during simulation. Consequently, memory would be assigned an x
, as the ^
operation cannot be performed validly. Examining internal signals using a waveform viewer (or $display
statement) is the best way to detect such issues when an x
is observed in the simulation output.
Check For Latches
Compilers usually point out when a latch is created, sometimes as warnings. Usually, they are undesirable, as they are level sensitive and prone to glitches caused by race conditions.
In Verilog, latches are often created unintentionally in behavioral models (using always
blocks) when not all outputs are specified for every possible input condition. This is a common pitfall, especially when writing combinational logic.
For instance, if an always
block meant for combinational logic does not assign a value to a register in one of the branches of an if
statement, synthesis tools may infer a latch to hold the value of the register when the condition is not met.
reg [3:0] countdown;
@(*) begin
alwayscase(state) begin
A: begin
if (reset) begin
= 6;
countdown end
end
B: begin
= 7;
countdown end
...end
end
In the above block, countdown
isn’t assigned any value in state A when reset isn’t true. Hence, the synthesiser would likely create a latch. This can cause issues as it may hold onto its previous value whenever reset
is false, even when in a state B
. Not fun.
reg [3:0] countdown;
parameter DEFAULT = 0;
@(*) begin
always= DEFAULT;
countdown case(state) begin
A: begin
if (reset) begin
= 6;
countdown end
end
B: begin
= 7;
countdown end
...end
end
We can avoid this by assigning countdown
a default value. The latest assignment in an always
block takes precedence, so in states A
and B
, countdown
will be updated.
Ensure Variables Are Named Correctly
Some common issues: - Variable names are mispelled. In this case, Verilog creates an implicit wire by default, and throws a warning (not an error). - Variable names start with a number (if you really want it to start with one, put a before it). - Variable names clashes with module/input/output names (please don’t do this in general).
EECS 270 Specific {#270}
Ensure Correct Top Level Module
When compiling and synthesising code, Quartus Prime links the top level module to the FPGA board. If that hasn’t been set correctly, your code won’t interface with the board as you’d like.
Check the hierarchy/files dropdown near the top left of the Quartus Prime window to confirm.
Also confirm whether all the correct files have been added to your project by examining the ‘files’ dropdown.
Ensure the FPGA works
What better way to ruin one’s day than a 3-hour battle with a faulty FPGA board. Double check your program by uploading it to LabsLand!