0.3 Verilog Coding Standards
Category Advanced Verilog Tutorial
Accidentally coming across an FGPA design I wrote years ago, the code style was passable, but the logical design was riddled with security risks. Many beginners write Verilog code based on C language thinking and style, leading to numerous non-standard common issues.
This section primarily summarizes some non-standard and dangerous Verilog designs. It mainly targets synthesizable digital designs; testbenches, which are simulation programs, generally have less stringent requirements.
Coding standards are different from coding styles. Coding styles are merely suggestions, and designers do not have to follow the coding style recommendations in this tutorial and can write code freely. As long as the logic is correct and the circuit is safe, even if the style is like flying willow catkins, as long as the compiler can compile and simulate normally, it's fine. Designers can proudly say, "Write your own code and let others guess!"
Coding standards are rules that must be followed to some extent, otherwise, they may affect the correctness of the digital circuit logic. Unless for special designs or when the designer is very familiar and confident, they can slightly deviate from Verilog coding standards. Otherwise, it is recommended to pay more attention to these standards during design, especially for beginners who are particularly prone to such issues.
On Initializing Values
Do not initialize variables when declaring them. If an initial value is set during variable declaration, the variable will have the expected initial value during simulation, but the initial value of the circuit after synthesis is uncertain. If the initial value of the signal affects the logic function, the simulation process may miss the opportunity to find logical errors due to insufficient verification. For example, the following description is not recommended:
reg [31:0] wdata = 32'b0 ;
Initial value assignments should be done during the reset state, and it is also recommended that register variables use a reset port to ensure that the system can return to its initial state upon power-up or disorder through a reset operation.
It is recommended to use positive edge logic for clocks and negative edge logic for resets during design. For detailed reset design, see 《5.1 Introduction to Reset》.
All signals in the statement block should be assigned initial values during reset, and do not miss any relevant signals.
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
cnt <= 'b0 ; // Missing initial value assignment for cout is 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
On Always Statements
Avoid using the rising and falling edges of the same clock in separate always blocks unless absolutely necessary, as this can introduce relatively complex clock quality and timing constraint issues.
// It is recommended to avoid using two always blocks with two clock edges
always @(posedge clk) begin
a <= b ;
end
always @(negedge clk) begin
c <= d ;
end
It is forbidden to use both edges of the clock as trigger conditions in one always block. Although compilation and simulation may proceed as per the designer's thoughts, such circuits are often not synthesizable, or the synthesized circuit functionality may not meet expectations.
// Forbidden to use both edges of the clock 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, a mistake many beginners often make.
// This design is incorrect
always @(posedge clk) begin
a <= b ;
end
always @(negedge clk) begin
a <= d ;
end
Do not include multiple parallel or unrelated conditional statements in one always block; use multiple always blocks instead.
When multiple parallel or unrelated conditional statements exist in one always statement, they are executed in parallel in the actual circuit synthesized or in the simulation results. However, the simulation process may be sequential, and delay information may lead to unpredictable error results. Moreover, this writing style is less readable and does not clearly delineate functional structures.
// 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 split
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
On Clocks and Asynchrony
Designs should preferably use synchronous design.
When asynchronous logic must be used, signals between different clock domains must be synchronized and cannot be directly used, otherwise metastable circuits will be generated. For specific synchronization implementations, refer to 《4.1 Synchronization and Asynchronization》 and subsequent related sections.
Avoid directly performing logical operations between clock signals and ordinary variable signals or detecting and judging clock signals by level. 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 clocks under different conditions, do not use direct selection logic, as this can cause glitches. See 《5.4 Clock Switching》 for details.
On Synthesis
Under normal circumstances, do not directly use operations such as multiplication *
, division /
, and remainder %
on signal variables. These operators, when synthesized, often result in structures and timing that are difficult to control. Optimized IP modules or integrated modules from the process library should be used instead. However, constant operations of the parameter type can use such operators, as the compiler calculates the results of constant operations at compile time and does not consume additional hardware resources.
Complete the conditions in the conditional statements of combinational logic, and list all sensitive signals in the always statements of combinational logic to avoid unintended latches. See 《Verilog Tutorial》 section 《6.5 Avoiding Latches in Verilog》 for details.
Consider whether the code can be synthesized into an actual circuit and what kind of circuit it will synthesize into during logical design. See 《9.2 Synthesizable Design》 for details.
On Instantiation
When instantiating, signals connected to input ports can be reg or wire type variables, while 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 or wire type variables.
When instantiating multiple modules, the module name comes first, followed by the instance name, and instance names must not be the same.
- 0.3 Verilog Coding Standards
-1.2 Verilog Switch-Level Modeling
-2.2 Verilog Combinational Logic UDP
-2.3 Verilog Sequential Logic UDP
-3.2 Verilog specify Block Statements
-3.3 Verilog Setup and Hold Time
-3.5 Verilog Delay Backannotation
-4.1 Verilog Synchronization and Asynchronization
-4.2 Verilog Clock Domain Crossing: Slow to Fast
-4.3 Verilog Clock Domain Crossing: Fast to Slow
-5.1 Verilog Reset Introduction
-5.2 Verilog Clock Introduction
-6.1 Verilog Low Power Introduction
-6.2 Verilog System-Level Low Power Design
-6.3 Verilog RTL-Level Low Power Design (Part 1)
-6.4 Verilog RTL-Level Low Power Design (Part 2)
-7.3 Verilog Random Numbers and Probability Distribution
-7.4 Verilog Real to Integer Conversion
-7.5 Verilog Other System Tasks
-8.3 Verilog TF Subroutine List
-8.5 Verilog ACC Subroutine List
-9.2 Verilog Synthesizable Design
WeChat Subscription
English: