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.
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.
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.
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.
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.
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 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.