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

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;
        
        always@(posedge clock) begin 
            // this assignment will overflow
            state <= go;
            // this assignment will not
            State <= go;
        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!
            MAJ m (.a(a),
                .b(b),
                .m(m));
        endmodule

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;
        always@(*) begin 
            memory = forgotten ^ forgotten;
        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;
        
        always@(*) begin 
            case(state) begin
                A: begin 
                    if (reset) begin 
                        countdown = 6;
                    end
                end
                B: begin 
                    countdown = 7;
                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;
        always@(*) begin 
            countdown = DEFAULT;
            case(state) begin
                A: begin 
                    if (reset) begin 
                        countdown = 6;
                    end
                end
                B: begin 
                    countdown = 7;
                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!