Verilog is a hardware description language (HDL) used to model electronic systems. It is most commonly used in the design, verification, and implementation of digital logic chips at the register-transfer level of abstraction.
Verilog Anti-Patterns Overview
Verilog, as a hardware description language, has several common anti-patterns that can lead to simulation-synthesis mismatches, timing issues, and hard-to-debug hardware designs. Here are the most important anti-patterns to avoid when writing Verilog code.
Blocking Assignments in Sequential Logic
Always use non-blocking assignments (<=
) for sequential logic within an always @(posedge clk)
block. Using blocking assignments (=
) in sequential logic can lead to race conditions and simulation-synthesis mismatches.
Non-Blocking Assignments in Combinational Logic
Use blocking assignments (=
) for combinational logic within an always @(*)
block. Using non-blocking assignments (<=
) in combinational logic can lead to simulation-synthesis mismatches and incorrect behavior.
Incomplete Sensitivity Lists
Always use complete sensitivity lists or the automatic sensitivity list @(*)
for combinational logic. Incomplete sensitivity lists can lead to simulation-synthesis mismatches and incorrect behavior.
Latches Due to Incomplete Assignments
Always provide default assignments to all variables at the beginning of combinational always
blocks, and include a default case in case statements. Incomplete assignments can create unintended latches in your design.
Multiple Drivers for the Same Signal
Ensure that each signal has only one driver. Multiple drivers for the same signal can lead to simulation-synthesis mismatches and undefined behavior.
Mixing Blocking and Non-Blocking Assignments
Avoid mixing blocking and non-blocking assignments within the same always
block. Use non-blocking assignments consistently for sequential logic and blocking assignments consistently for combinational logic.
Asynchronous Reset Release
Synchronize asynchronous reset release to avoid metastability issues. While asynchronous reset assertion is often used for reliable system initialization, the release should be synchronized to the clock to prevent timing violations.
Combinational Loops
Avoid creating combinational loops in your design. Combinational loops can lead to oscillations, unstable behavior, and synthesis issues. Break loops using registers or redesign your logic.
Inferring Clock Gating
Avoid inferring clock gating using simple logic gates. Use enable signals within sequential blocks or instantiate proper clock gating cells provided by your technology library.
Using Initial Blocks for Hardware
Avoid using initial
blocks for hardware initialization, as they are generally not synthesizable for hardware (except for some specific cases like memory initialization). Use reset signals to initialize your design.
Ignoring Clock Domain Crossing
Always use proper synchronization techniques when crossing clock domains. For single-bit signals, use a double-flop synchronizer. For multi-bit signals, use handshaking protocols, gray coding, or asynchronous FIFOs.
Improper Use of Delays
Avoid using delays (#
) in synthesizable code. Delays are for simulation only and are ignored during synthesis. Let the synthesis tool handle timing based on your timing constraints.
Ignoring Tool Warnings
Don’t ignore warnings from your synthesis and simulation tools. Warnings often indicate potential issues in your design that could lead to unexpected behavior. Fix the issues causing warnings or document why they can be safely ignored.
Not Using Parameters for Constants
Use parameters for constants in your design. This makes your code more maintainable, reusable, and easier to modify. Parameters can be overridden during module instantiation, allowing for flexible designs.
Not Using Named Port Connections
Use named port connections when instantiating modules. Named connections are clearer, less error-prone, and allow for port reordering without breaking your design.
Not Using Generate Blocks
Use generate
blocks for repetitive structures in your design. Generate blocks allow for parameterized, scalable designs and reduce code duplication.
Not Using Assertions
Use assertions to verify the correctness of your design. Assertions can catch bugs early in the design process and serve as documentation for expected behavior.