0
0
VerilogHow-ToIntermediate · 4 min read

How to Optimize Verilog Code for FPGA: Best Practices

To optimize Verilog code for FPGA, focus on writing synchronous logic with proper clock domains, use resource-efficient coding styles like finite state machines, and avoid combinational loops. Also, leverage FPGA-specific features such as block RAMs and DSP slices by coding with hardware mapping in mind.
📐

Syntax

Optimized Verilog code for FPGA typically uses synchronous logic with always @(posedge clk) blocks to ensure predictable timing. Use reg for storage elements and wire for combinational signals. Finite State Machines (FSMs) are coded with clear state definitions and transitions to control complex behavior efficiently.

verilog
module simple_fsm(
    input wire clk,
    input wire rst,
    input wire in,
    output reg out
);

    typedef enum reg [1:0] {IDLE=2'b00, STATE1=2'b01, STATE2=2'b10} state_t;
    state_t state, next_state;

    // State register
    always @(posedge clk or posedge rst) begin
        if (rst)
            state <= IDLE;
        else
            state <= next_state;
    end

    // Next state logic
    always @(*) begin
        case(state)
            IDLE: next_state = in ? STATE1 : IDLE;
            STATE1: next_state = in ? STATE2 : IDLE;
            STATE2: next_state = IDLE;
            default: next_state = IDLE;
        endcase
    end

    // Output logic
    always @(posedge clk) begin
        out <= (state == STATE2);
    end

endmodule
💻

Example

This example shows a simple FSM with three states optimized for FPGA. It uses synchronous resets and clear state transitions, which help the FPGA tools map the design efficiently to hardware resources.

verilog
module counter(
    input wire clk,
    input wire rst,
    output reg [3:0] count
);

    always @(posedge clk or posedge rst) begin
        if (rst)
            count <= 4'b0000;
        else
            count <= count + 1;
    end

endmodule
⚠️

Common Pitfalls

Common mistakes include using asynchronous resets unnecessarily, creating combinational loops, and writing overly complex combinational logic that increases delay. Avoid inferring latches by ensuring all branches in always blocks assign values. Also, do not use blocking assignments (=) in sequential logic; use non-blocking (<=) instead.

verilog
/* Wrong: infers latch and uses blocking assignment in sequential logic */
always @(posedge clk) begin
    if (enable)
        out = data_in; // blocking assignment
end

/* Correct: no latch, non-blocking assignment */
always @(posedge clk) begin
    if (enable)
        out <= data_in;
    else
        out <= 0;
end
📊

Quick Reference

  • Use synchronous resets and clock edges.
  • Prefer non-blocking assignments in sequential logic.
  • Write clear FSMs for control logic.
  • Avoid combinational loops and inferred latches.
  • Leverage FPGA primitives like block RAM and DSPs by coding accordingly.
  • Keep combinational logic simple and balanced.

Key Takeaways

Write synchronous logic using non-blocking assignments to ensure predictable FPGA timing.
Use finite state machines to organize control logic efficiently.
Avoid combinational loops and inferred latches by covering all conditions in always blocks.
Leverage FPGA-specific resources by coding with hardware mapping in mind.
Keep combinational logic simple to reduce delay and resource usage.