归约运算符无法正常工作
Reduction operator does not work properly
我有一个 FSM 设计,它使用计数器在特定状态内计数并保持在那里直到表达式 &counter
产生 TRUE
,但是当它完成时(得到 1111...111 - 通过模拟器检查)&counter
永远不会 HIGH
并像无限循环一样停留在该状态。如何克服这个问题?我将提供代码的错误部分。我是 verilog 的新手,所以希望这是一个容易发现的问题。
reg [2:0] State, NextState;
reg [20:0] counter;
reg EN;
//State register
always @(posedge Clk) begin
if(Rst) begin
counter <= 0;
State <= S0;
end
else begin
State <= NextState;
counter <= EN ? counter + 1 : 0;
end
end
//Next State Logic
always @(State, COL)
case(State)
S0: begin
NextState <= S1;
EN <= 0;
end
S1: begin
if(ROW) begin
if(COL) begin
if(&counter[18:0]) begin // <--- THIS EXP. NEVER GETS 1
PrevCOL <= COL;
NextState <= S3;
EN <= 0;
end
else begin
EN <= 1;
NextState <= S1;
end
end
else
NextState <= S2;
end
else
NextState <= S0;
end
S2: NextState <= S1;
S3: begin
if(PrevCOL == COL) begin
if(&counter) begin // <-------- THIS EXPRESSION NEVER GETS 1
NextState <= S4;
EN <= 0;
case(ROW)
8:
case(COL)
8: ID <= I0;
4: ID <= I1;
2: ID <= I2;
1: ID <= I3;
endcase
4:
case(COL)
8: ID <= I4;
4: ID <= I5;
2: ID <= I6;
1: ID <= I7;
endcase
2:
case(COL)
8: ID <= I8;
4: ID <= I9;
2: ID <= I10;
1: ID <= I11;
endcase
1:
case(COL)
8: ID <= I12;
4: ID <= I13;
2: ID <= I14;
1: ID <= I15;
endcase
endcase
end
else begin
NextState <= S3;
EN <= 1;
end
end
else
NextState <= S0;
end
S4: NextState <= S0;
endcase
有问题的区域在评论中指出。为什么它不起作用?
谢谢!
对于组合逻辑,避免自己编写敏感列表。您应该替换为:
always @(State, COL)
有
always @(*)
您也可以使用 always_comb。基本上,当计数器递增时,您的第二个 always 块不会执行。它仅在 State 或 COL 信号发生变化时执行。
其次,您不应在组合逻辑中使用非阻塞赋值 (<=)。用阻塞赋值 (=).
替换第二个 "always" 块中的所有内容
正如 Convergent 指出的那样。 always @(State, COL)
应该是always @*
(与always @(*)
同义)或者SystemVerilog的always_comb
,组合块中应该使用阻塞赋值(=
);非非阻塞 (<=
).
另一个问题是 EN
、PrevCOL
和 ID
中推断出的复杂锁存器。当我说锁存器时,我指的是电平敏感(活动 high/low)锁存器。当我说触发器时,我指的是边沿触发触发器。
推断锁存器是当 reg
没有为 always 块中的所有可能路径分配值时。就其本身而言,锁存器本身并不是一件坏事,只要它是有意的,数据尊重建立和保持时间,并且使能引脚没有毛刺。通常,这些都不是必需的,并且被许多人认为是设计错误。 FPGA 通常比锁存器有更多的触发器和逻辑门(锁存器使形式意图逻辑门的时序比专用锁存器更差)。如果使用锁存器,最好尽可能简单,将其放在与解码逻辑分开的always块中,并使用非阻塞赋值。 Verilog 只有隐含锁存器; lint 和 LEC(逻辑等效检查)将针对可能的设计错误发出警告。 SystemVerilog 允许显式锁存; link 并且 LEC 不会发出警告。下面的例子。总之,只在需要时使用锁存器并保持简单。
// Verilog clean implicit latch
always @* begin
if (lat_en) // requirement: 'lat_en' must be glitch free
lat <= data; // requirement: 'data' must respects setup/hold time
end
// SystemVerilog explicit latch
always_latch begin
if (lat_en) // requirement: 'lat_en' must be glitch free
lat <= data; // requirement: 'data' must respects setup/hold time
end
推断的 复杂 闩锁比简单的闩锁更危险。复杂锁存器的启用引脚或数据基于其他组合逻辑或锁存器。很难满足时序要求并且经常出现故障。这意味着您的代码可以 运行 正常一段时间,然后意外地得到错误的数据。它们很难调试,因为时序 window 当合成和应用 SDF 注释时,故障可能会在更改时发生,并且可以根据其他变量再次更改 FPGA 或硅片。根据事件调度,RLT 模拟甚至可能在 0 时间发生故障。再次总结,仅在需要时使用锁存器并保持简单。
当谈到 RTL 编码风格时,我通常遵循 Cliff Cummings 的 2-always 块解决方案的建议(一个用于下一状态组合逻辑,另一个用于同步分配):
我的个人资料中还有 link 有用的 Verilog/SystemVerilog 参考资料。
我建议将 EN
、PrevCOL
和 ID
变成适当的触发器,并在组合块中计算分配的 next_*
。
//Synchronous Logic Assignments
always @(posedge Clk) begin // SV: use 'always_ff' instead of 'always'
if(Rst) begin
counter <= 0;
State <= S0;
EN <= 0;
PrevCOL <= 0;
ID <= 0;
end
else begin
State <= NextState;
counter <= next_EN ? counter + 1 : 0;
EN <= next_EN;
PrevCOL <= next_PrefCOL;
ID <= next_ID;
end
end
//Next State Logic Calculations
always @* begin // SV: use 'always_comb' instead of 'always @*'
// Default value assignments
next_EN = EN;
next_PrevCOL = PrevCOL;
next_ID = ID;
// Update value logic
case(State)
// Update NextState and next_* values in here
endcase
end
仅供参考 always @*
,其中 RHS 只是常量,可能永远不会在模拟中执行。 LRM 未定义此条件,模拟器的某些创建者决定实施时间 0 执行,其他人则没有。它将正确合成。 SystemVerilog 的 always_comb
更好,因为它将在时间 0 执行。参见 IEEE Std 1800-2012 § 9.2.2.2.2 always_comb
与 always @*
相比.使用 Verilog,您可以使用 assign
语句。
我有一个 FSM 设计,它使用计数器在特定状态内计数并保持在那里直到表达式 &counter
产生 TRUE
,但是当它完成时(得到 1111...111 - 通过模拟器检查)&counter
永远不会 HIGH
并像无限循环一样停留在该状态。如何克服这个问题?我将提供代码的错误部分。我是 verilog 的新手,所以希望这是一个容易发现的问题。
reg [2:0] State, NextState;
reg [20:0] counter;
reg EN;
//State register
always @(posedge Clk) begin
if(Rst) begin
counter <= 0;
State <= S0;
end
else begin
State <= NextState;
counter <= EN ? counter + 1 : 0;
end
end
//Next State Logic
always @(State, COL)
case(State)
S0: begin
NextState <= S1;
EN <= 0;
end
S1: begin
if(ROW) begin
if(COL) begin
if(&counter[18:0]) begin // <--- THIS EXP. NEVER GETS 1
PrevCOL <= COL;
NextState <= S3;
EN <= 0;
end
else begin
EN <= 1;
NextState <= S1;
end
end
else
NextState <= S2;
end
else
NextState <= S0;
end
S2: NextState <= S1;
S3: begin
if(PrevCOL == COL) begin
if(&counter) begin // <-------- THIS EXPRESSION NEVER GETS 1
NextState <= S4;
EN <= 0;
case(ROW)
8:
case(COL)
8: ID <= I0;
4: ID <= I1;
2: ID <= I2;
1: ID <= I3;
endcase
4:
case(COL)
8: ID <= I4;
4: ID <= I5;
2: ID <= I6;
1: ID <= I7;
endcase
2:
case(COL)
8: ID <= I8;
4: ID <= I9;
2: ID <= I10;
1: ID <= I11;
endcase
1:
case(COL)
8: ID <= I12;
4: ID <= I13;
2: ID <= I14;
1: ID <= I15;
endcase
endcase
end
else begin
NextState <= S3;
EN <= 1;
end
end
else
NextState <= S0;
end
S4: NextState <= S0;
endcase
有问题的区域在评论中指出。为什么它不起作用?
谢谢!
对于组合逻辑,避免自己编写敏感列表。您应该替换为:
always @(State, COL)
有
always @(*)
您也可以使用 always_comb。基本上,当计数器递增时,您的第二个 always 块不会执行。它仅在 State 或 COL 信号发生变化时执行。
其次,您不应在组合逻辑中使用非阻塞赋值 (<=)。用阻塞赋值 (=).
替换第二个 "always" 块中的所有内容正如 Convergent 指出的那样。 always @(State, COL)
应该是always @*
(与always @(*)
同义)或者SystemVerilog的always_comb
,组合块中应该使用阻塞赋值(=
);非非阻塞 (<=
).
另一个问题是 EN
、PrevCOL
和 ID
中推断出的复杂锁存器。当我说锁存器时,我指的是电平敏感(活动 high/low)锁存器。当我说触发器时,我指的是边沿触发触发器。
推断锁存器是当 reg
没有为 always 块中的所有可能路径分配值时。就其本身而言,锁存器本身并不是一件坏事,只要它是有意的,数据尊重建立和保持时间,并且使能引脚没有毛刺。通常,这些都不是必需的,并且被许多人认为是设计错误。 FPGA 通常比锁存器有更多的触发器和逻辑门(锁存器使形式意图逻辑门的时序比专用锁存器更差)。如果使用锁存器,最好尽可能简单,将其放在与解码逻辑分开的always块中,并使用非阻塞赋值。 Verilog 只有隐含锁存器; lint 和 LEC(逻辑等效检查)将针对可能的设计错误发出警告。 SystemVerilog 允许显式锁存; link 并且 LEC 不会发出警告。下面的例子。总之,只在需要时使用锁存器并保持简单。
// Verilog clean implicit latch
always @* begin
if (lat_en) // requirement: 'lat_en' must be glitch free
lat <= data; // requirement: 'data' must respects setup/hold time
end
// SystemVerilog explicit latch
always_latch begin
if (lat_en) // requirement: 'lat_en' must be glitch free
lat <= data; // requirement: 'data' must respects setup/hold time
end
推断的 复杂 闩锁比简单的闩锁更危险。复杂锁存器的启用引脚或数据基于其他组合逻辑或锁存器。很难满足时序要求并且经常出现故障。这意味着您的代码可以 运行 正常一段时间,然后意外地得到错误的数据。它们很难调试,因为时序 window 当合成和应用 SDF 注释时,故障可能会在更改时发生,并且可以根据其他变量再次更改 FPGA 或硅片。根据事件调度,RLT 模拟甚至可能在 0 时间发生故障。再次总结,仅在需要时使用锁存器并保持简单。
当谈到 RTL 编码风格时,我通常遵循 Cliff Cummings 的 2-always 块解决方案的建议(一个用于下一状态组合逻辑,另一个用于同步分配):
我的个人资料中还有 link 有用的 Verilog/SystemVerilog 参考资料。
我建议将 EN
、PrevCOL
和 ID
变成适当的触发器,并在组合块中计算分配的 next_*
。
//Synchronous Logic Assignments
always @(posedge Clk) begin // SV: use 'always_ff' instead of 'always'
if(Rst) begin
counter <= 0;
State <= S0;
EN <= 0;
PrevCOL <= 0;
ID <= 0;
end
else begin
State <= NextState;
counter <= next_EN ? counter + 1 : 0;
EN <= next_EN;
PrevCOL <= next_PrefCOL;
ID <= next_ID;
end
end
//Next State Logic Calculations
always @* begin // SV: use 'always_comb' instead of 'always @*'
// Default value assignments
next_EN = EN;
next_PrevCOL = PrevCOL;
next_ID = ID;
// Update value logic
case(State)
// Update NextState and next_* values in here
endcase
end
仅供参考 always @*
,其中 RHS 只是常量,可能永远不会在模拟中执行。 LRM 未定义此条件,模拟器的某些创建者决定实施时间 0 执行,其他人则没有。它将正确合成。 SystemVerilog 的 always_comb
更好,因为它将在时间 0 执行。参见 IEEE Std 1800-2012 § 9.2.2.2.2 always_comb
与 always @*
相比.使用 Verilog,您可以使用 assign
语句。