Skip to main content
VHDL, 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 VHDL code.
Avoid using variables for sequential logic. Variables update immediately when assigned, which can lead to unexpected behavior and simulation-synthesis mismatches. Use signals for sequential logic, as they update at the end of the process, modeling hardware behavior more accurately.
Avoid multiple assignments to the same signal within a process. This can lead to race conditions and simulation-synthesis mismatches. Use a single assignment with conditional logic to ensure deterministic behavior.
Always use complete sensitivity lists for combinational processes. Incomplete sensitivity lists can lead to simulation-synthesis mismatches and incorrect behavior. In VHDL-2008, you can use process(all) for an automatic sensitivity list.
Always provide default assignments to all signals at the beginning of combinational processes. Incomplete assignments can create unintended latches in your design, leading to unexpected behavior and timing issues.
Avoid mixing signal assignments (non-blocking, using <=) and variable assignments (blocking, using :=) within the same process. This can lead to confusion and potential simulation-synthesis mismatches. Use signals consistently for sequential logic.
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.
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.
Avoid using wait statements in synthesizable code. wait statements are generally not synthesizable (except for some specific forms like wait until rising_edge(clk) in some tools). Use process sensitivity lists and additional registers to implement delays.
Avoid using unprotected shared variables, as they can lead to race conditions and undefined behavior. Use protected types (available in VHDL-2002 and later) to ensure thread-safe access to shared variables.
Avoid using inferred latches for memory. Instead, use a clocked process for writes and a separate combinational process for reads. This ensures that your memory is implemented using proper memory resources (like block RAM) rather than latches.
Use packages to define common types, constants, and functions that are used across multiple files. This promotes code reuse, consistency, and maintainability.
Use generics to parameterize your designs. This makes your code more reusable and configurable. Generics can be used to specify bit widths, constants, and other parameters that might need to be changed when reusing the design.
Use named association in port maps and generic maps. Named association is clearer, less error-prone, and allows for port reordering without breaking your design.
Use functions and procedures to encapsulate common logic and avoid code duplication. This makes your code more maintainable, readable, and less error-prone.
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.
Use configuration declarations to separate the structural description of your design from the binding to specific implementations. This allows you to easily switch between different implementations for simulation and synthesis.