【干货分享】编写可综合的FPGA代码经验总结(二)

作者:张浩 ,来源:FPGA技术联盟

10. case,casez,casex语句

Verilog定义了case,casez和casex语句,用于做多种情况下的选择语句。

reg [1:0] sel;

reg [2:0] result;

always @(*)

case(sel)

2’b00: result = 3’d0;

2’b01: result = 3’d1;

2’b10: result = 3’d2;

endcase

使用case语句代替嵌套的if-else将会产生更易读的代码,更好的逻辑利用率和更高的性能。

casez和casex语句在比较中允许“don't care”条件。 casez将“z“”值视为"don't care",casex将“z”和“x”值都视为“don't care”。如果casez或casex表达式中的任何位都是"don't care value",那么该位将被忽略。以下是casez和casex的例子。

reg [1:0] sel;

reg [2:0] result;

// using casez

always @(*)

casez(sel)

2’b0?: result = 3’d0;

2’b10: result = 3’d1;

2’b11: result = 3’d2;

endcase

// using casex

always @(*)

casex(sel)

2’b0x: result = 3’d0;

2’b10: result = 3’d1;

2’b11: result = 3’d2;

Endcase

case的表达式可以是一个常量,如下例所示。

reg [1:0] sel;

reg [2:0] result;

always @(*)

case(1

~sel[1]: result = 3’d0;

sel[1] & ~sel[0]: result = 3’d1;

sel[1] & sel[0]: result = 3’d2;

Endcase

在案例中使用don't care条件很容易导致case 条件重叠或重复。而且,使用这些语句会导致综合和仿真不匹配。以下是案例case条件重叠的一个例子。

// casez statement contains overlapped case items

reg [1:0] sel;

reg [2:0] result;

always @(*)

casez(sel)

2’b0z: result = 3’d0;

2’b10: result = 3’d2;

2’b11: result = 3’d3;

2’b01: result = 3’d1; // overlap with 2’b0z

Endcase

允许重叠或重复的case条件,在大多数情况下不会触发任何仿真或综合警告。这是一种危险且难以调试的情况.。建议开发人员完全避免使用casex和casez语句。

在case语句中添加一个默认的条件是避免一系列问题的简单方法。 有两种方法可以达到相同的效果,如以下示例所示。

reg [1:0] sel;

reg [2:0] result;

// using default clause

always @(*)

case(sel)

2’b00: result = 3’d0;

2’b01: result = 3’d1;

2’b10: result = 3’d2;

default: result = 3’d3;

endcase

// 在case语句之前进行默认赋值

always @(*)

result = 3’d3;

case(sel)

2’b00: result = 3’d0;

2’b01: result = 3’d1;

2’b10: result = 3’d2;

Endcase

11. 在always块中混合阻塞和非阻塞赋值

Verilog指定了总是可以出现在块中的两种赋值类型:阻塞和非阻塞。阻塞和非阻塞赋值分别用于描述组合逻辑和时序逻辑。永远不要在同一个块中混合使用阻塞和非阻塞赋值。这样做可能会导致不可预知的综合和仿真结果。在许多情况下,综合工具不会产生任何警告,但综合结果将是不正确的。以下两个代码示例说明了阻塞和非阻塞赋值的混合使用。

reg blocking, non_blocking;

always @(posedge clk) begin

if(reset) begin

blocking = 0;

non_blocking <= 0;

end

else begin

blocking = ^data;

non_blocking <= |data;

end

end

always @(*) begin

blocking = ^data;

non_blocking <= |data;

end

正确的方法是:

reg blocking, non_blocking;

always @(posedge clk) begin

if(reset) begin

non_blocking <= 0;

end

else begin

non_blocking <= |data;

end

end

always @(*) begin

blocking = ^data;

End

12. 多个阻塞赋值

always块中的阻塞赋值按其顺序执行。 尽管这样做通常很方便,但是建议开发者限制使用多个阻塞赋值来赋值always块中的相同变量。下面的两个代码示例显示了使用多个阻塞分配的潜在问题。

reg signal_a, signal_b, signal_c, signal_d;

always (*) begin

signal_a = signal_b & signal_c;

// …

// additional code

signal_d = signal_a & signal_e;

end

无意中改变signal_a和signal_d分配的顺序将会破坏signal_d的功能。

reg [15:0] signal_a, signal_b;

always (*) begin

signal_a[15:12] = 4’b0;

// …

// additional code

signal_a = signal_b;

End

signal_a的最后一个赋值优先,signal_a的位[15:12]永远不会被复位。

13. 使用命名的always块

以下是always块的例子。

reg reg_unnamed;

always @(posedge clk) begin : myname

// only visible in the “myname” block

reg reg_named;

// post-synthesis name : myname.reg_named

reg_named <= data_in;

// post-synthesis name : reg_unnamed

reg_unnamed <= ~reg_named;

end // always

命名块可以在几种情况下有用。 因为always块是唯一标识的,所以在仿真中更容易找到它。 而且,限制变量的范围允许重复使用相同的变量名称。


文章转载自:FPGA技术联盟
*本文由FPGA技术联盟授权转发,如需转载请联系作者本人

最新文章