I happened to look at an FGPA design I wrote a few years ago. The coding style was barely passable, but safety hazards in logic design were everywhere. Many beginners write Verilog code basically following C language thinking and style, resulting in many common non-standard problems.

This section mainly summarizes some non-standard and dangerous Verilog designs. It mainly targets synthesizable digital designs. Testbenches are simulation programs, and in general the requirements are not very strict.

The content covered by coding standards is different from coding style. Coding style is only a suggestion. Designers do not have to follow this tutorial's coding style suggestions, and can write code freely. As long as the logic is correct and the circuit is safe, even if the code is written in a chaotic style with willow catkins flying everywhere, as long as the compiler can compile and simulate normally, it is fine. Designers can proudly say: write your own code, and let others guess it!

Coding standards are rules that must be followed to a certain extent; otherwise, they may affect the correctness of digital circuit logic. Unless it is for some special design, or the individual is very familiar and fully confident, they may slightly go beyond the Verilog coding standards; otherwise, it is recommended to pay more attention to these standards in design. Beginners in particular are prone to making these mistakes.

About Assigning Initial Values

Do not assign initial values to variables at variable declaration. If an initial value is set at declaration, the variable will have the expected initial value during simulation, but the initial value of the circuit after synthesis is indeterminate. If the initial signal value affects the logic function, the simulation process may miss the opportunity to find logic errors due to insufficient verification. For example, the following description is not recommended:

    reg [31:0]      wdata = 32'b0 ;

Initial value assignment should be done in the reset state. It is also recommended that register variables use the reset port to ensure that when the system is powered on or becomes disordered, the system can be restored to its initial state through a reset operation.

It is recommended that the design use positive-edge logic for the clock and negative-edge logic for reset. See the reset design in detail in"5.1 Reset Introduction"。

During reset, all signals in the statement block should be assigned initial values; do not miss any related signals.

    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            cnt  <= 'b0 ; //Missing the initial value assignment for cout is very dangerous
        end
        else if (cnt == 10) begin
            cnt  <= 4'b0 ;
            cout <= 1'b1 ;
        end
        else begin
            cnt  <= cnt + 1'b1 ;
            cout <= 1'b0 ;
        end
    end

About always Statements

Unless absolutely necessary, do not use the same clock's rising-edge and falling-edge logic in two always blocks, as this will introduce relatively complex clock quality and timing constraint problems.

   //It is recommended to avoid logic using 2 clock edges in 2 always blocks as much as possible
   always @(posedge clk) begin
      a <= b ;
   end
   always @(negedge clk) begin
      c <= d ;
   end

It is forbidden to use both clock edges as trigger conditions in one always block. Compilation and simulation may work according to the designer's intent, but such circuits are often not synthesizable, or the synthesized circuit function will not meet expectations.

   //It is forbidden to use dual-edge logic in one always block
   always @(posedge clk or negedge clk) begin
      a <= b ;
   end  

It is forbidden to assign values to the same variable in two always blocks. This is a mistake many beginners easily make.

  //This design is wrong
   always @(posedge clk) begin
      a <= b ;
   end
   always @(negedge clk) begin
      a <= d ;
   end

Do not have multiple parallel or unrelated conditional statements in one always block; use multiple always blocks to describe them separately.

When there are multiple parallel or unrelated conditional statements in one always statement, in the simulation execution results or the actual synthesized circuit, the unrelated conditional statements are executed in parallel. However, the simulation process may be executed sequentially; if there is delay information, it may lead to unpredictable erroneous results. Moreover, this writing style has poor readability and the functional/structural division is not clear.

    //Not recommended
    always @(posedge clk) begin
        if (a == b)
            data_t1 <= data1 ;

        if (a == b && c == d)
            data_t2 <= data2 ;
        else
            data_t2 <= 'b0 ;
    end
   
    //Recommended to write them separately
    always @(posedge clk) begin
        if (a == b)
            data_t1 <= data1 ;
    end
    always @(posedge clk) begin
        if (a == b && c == d)
            data_t2 <= data2 ;
        else
            data_t2 <= 'b0
    end

About Clock and Asynchronous

Use synchronous design as much as possible in the design.

When asynchronous logic must be used, signals between different clock domains must be synchronized; related signals cannot be used directly, otherwise a metastable circuit will be produced. Please refer to the specific synchronization implementation in"4.1 Synchronous and Asynchronous"and the related chapters that follow.

Try not to perform logic operations directly on clock signals and ordinary variable signals, or detect and judge the level signals of clock signals. For example, the following descriptions are not recommended.

    assign clk_gate = clk & clken ;
    assign dout = (clk == 1'b1) ? din : 0 ;
    always @(posedge clk) begin
        if (clk = 1'b1)
            data_t1 <= data1 ;
    end    

When selecting the clock under different conditions, selection logic cannot be used directly, otherwise glitches will occur. See"5.4 Clock Switching"。

About Synthesis

In general, signal variables should not directly use multiplication*, division/, modulo%operations. After these operators are synthesized, the structure and timing are often difficult to control. Related optimized IP modules or integrated modules in the technology library should be used instead. However, constants of parameter type can use such operators, because the compiler calculates the results of constant operations at the beginning of compilation, consuming no extra hardware resources.

In the conditional statements of combinational logic, conditions should be completed. In the always statements of combinational logic, sensitive signals should be fully listed to avoid unwanted latches. SeeChapter "6.5 Verilog Avoid Latches" in the "Verilog Tutorial"。

When designing logic, consider whether the code can be synthesized into an actual circuit and what kind of circuit it will be synthesized into. See"9.2 Synthesizable Design"。

About Instantiation

When instantiating, signals connected to input ports can be reg-type or wire-type variables, and signals connected to output ports must be wire-type variables. However, when declaring port signals, input signals must be wire-type variables, and output signals can be reg-type or wire-type variables.

When instantiating multiple modules, the module name comes first, followed by the instance name, and instance names cannot be the same.