verilog 避坑指南(持续更新)
🍀内容梗概
- if —-else.和 case语法不能传播不定态
- Verilog 的if - else 语法会被综合成为优先级选择的电路, 面积和时序均不够优化
-
在两级寄存器之间的硬件逻辑越少,则意味能够运行到更高的主频。
-
变量赋初值
变量声明时不要对变量进行赋初值操作。如果变量声明时设置初始值,仿真时变量会有期望的初值,但综合后电路的初始值是不确定的。如果信号初值会影响逻辑功能,则仿真过程可能会因验证不充分而错过查找出逻辑错误的机会。下面描述是不建议的:
reg [31:0] wdata = 32'b0 ;
◆赋初值操作应该在复位状态下完成,也建议寄存器变量都使用复位端,以保证系统上电或紊乱时,可以通过复位操作让系统恢复初始状态。
◆建议设计时,时钟采用正边沿逻辑,复位采用负边沿逻辑。
◆复位时语句块中所有的信号都应该赋予初值,不要漏掉相关信号。
always @(posedge clk or negedge rstn) begin if (!rstn) begin cnt <= 'b0 ; //漏掉 cout 赋初值,很危险 end else if (cnt == 10) begin cnt <= 4'b0 ; cout <= 1'b1 ; end else begin cnt <= cnt + 1'b1 ; cout <= 1'b0 ; end end
-
关于 always 语句
◆不到万不得已不要在 2 个 always 块中分别使用同一时钟的上升沿和下降沿逻辑,否则会引入相对复杂的时钟质量和时序约束的问题。
//建议尽量避免 2 个 always 块 2 个时钟边沿的逻辑 always @(posedge clk) begin a <= b; end always @(negedge clk) begin c <= d ; end
◆禁止在一个 always 块中同时将时钟的双边沿作为触发条件,编译、仿真可能会按照设计人员的思想进行,但此类电路往往不可综合,或综合后电路功能不会符合预期。
//禁止一个 always 块中使用双边沿逻辑 always @(posedge clk or negedge clk) begin a <= b ; end
◆禁止在 2 个 always 块中为同一个变量赋值,
//此设计是错误的 always @(posedge clk) begin a <= b ; end always @(negedge clk) begin a <= d ; end
◆一个 always 块中不要存在多个并行或不相关的条件语句,使用多个 always 分别描述。
当一个 always 语句中存在多个并行或不相关的条件语句时,仿真的执行结果或综合的实际电路中,不相关的条件语句都是并行执行的。但是仿真过程可能是顺序执行的,如果有延迟信息可能会导致不可以预知的错误结果。且该写法可读性较差,功能结构划分不明显。
//不推荐 always @(posedge clk) begin if (a == b) data_t1 <= data1 ; if (a == b && c == d) data_t2 <= data2 ; else data_t2 <= 'b0 ; end //推荐分开写 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
-
关于时钟与异步
◆设计中尽量使用同步设计。
◆必须要使用异步逻辑时,一定要对不同时钟域之间的信号进行同步处理,不能直接使用相关信号,否则会产生亚稳态电路。
◆尽量不要直接将时钟信号与普通变量信号做逻辑操作,或对时钟信号进行电平信号的检测判断。例如下列描述都是不建议的。
assign clk_gate = clk & clken ; assign dout = (clk == 1'b1) ? din : 0 ; always @(posedge clk) begin if (clk = 1'b1) data_t1 <= data1 ; end