为什么 If 语句会导致 verilog 中的闩锁?
Why does an If statement cause a latch in verilog?
我正在尝试在 Verilog 中编写一个 controller/data-path 实现,但我对什么会导致不需要的闩锁感到困惑。本质上,我有一个状态机在 negedge 时钟上更新。该状态机根据机器所处的状态向数据路径发送 5 个控制信号(loadSquare、loadDelta、addDelta 等)。数据路径和控制器的代码如下所示。
数据路径
//Control lines
reg addSquare, addDelta, decDelta;
reg loadSquare, loadDelta;
//Input lines
reg [8:0] square, delta;
//Output register
reg [7:0] outReg;
always @(posedge clk) begin
if (loadSquare)
square = 9'h1; //used on initialization
if (loadDelta)
delta = 9'h3; //used on initialization
if (addSquare)
square = square + delta;
if (addDelta)
delta = delta + 2'h2;
if (decDelta)
outReg = (delta>>1) - 1; //used for output
else
outReg = Input;
end
控制器
//Output of module
assign Output = outReg;
//Finite State Machine
always @(currentState) begin
case(currentState)
2'h0: begin //initialize values, wait for start
{loadSquare, loadDelta} = 2'b11;
{addSquare, addDelta, decDelta} = 3'h0;
end
2'h1: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b110; //add square and delta
end
2'h2: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b001; //decrement delta, wait for reset
end
default: ; // unused
endcase
//Next state logic implemented on negedge clk (not shown)
此代码在 Xilinx 中生成以下警告:
WARNING:Xst:737 - Found 1-bit latch for signal <addDelta>. Latches may be generated from incomplete case or if statements. We do not recommend the use of latches in FPGA/CPLD designs, as they may lead to timing problems.
WARNING:Xst:737 - Found 1-bit latch for signal <decDelta>. Latches may be generated from incomplete case or if statements. We do not recommend the use of latches in FPGA/CPLD designs, as they may lead to timing problems.
WARNING:Xst:737 - Found 1-bit latch for signal <loadDelta>. Latches may be generated from incomplete case or if statements. We do not recommend the use of latches in FPGA/CPLD designs, as they may lead to timing problems.
WARNING:Xst:1294 - Latch <loadDelta> is equivalent to a wire in block <ModuleName>.
WARNING:Xst:1294 - Latch <decDelta> is equivalent to a wire in block <ModuleName>.
WARNING:Xst:1294 - Latch <addDelta> is equivalent to a wire in block <ModuleName>.
我知道不完整的 if 语句会导致闩锁。为了尝试解决这个问题,我尝试了 2 种不同的实现,但它们没有删除警告。我对 "decDelta" 案例特别困惑,因为我不明白我在这个条件语句中没有考虑到什么。
尝试#1
always @(posedge clk) begin
if (loadSquare)
square = 9'h1;
else
square = square;
if (loadDelta)
delta = 9'h3;
else
delta = delta;
//... and so on
试试#2
always @(posedge clk) begin
square = square;
delta = delta;
if (loadSquare)
square = 9'h1;
if (loadDelta)
delta = 9'h3;
//... and so on
当我 运行 模拟时,代码按预期工作,但我想更多地了解导致这些警告的原因。
当一个变量必须保留其先前的值时,如果它未分配一个始终块中的值,则推断出锁存器.必须创建一个锁存器来存储这个当前值。
闩锁 会导致各种竞争条件。不需要的锁存器会在 组合电路 中产生反馈,即将输出路由回输入 - 这可能是不可预测的,从而导致不稳定的电路行为。
不完整 if-else
语句将生成不需要的闩锁。如果没有为所有可能的输入条件定义条件之一,则 if-else 语句被视为 "incomplete"。同样,不完整的 case
语句,即没有 default
语句的语句也可以推断为 latch.
A complete if-else
语句引用了以下Mux:
而不完整 if-else
指的是从输出到输入的反馈路径,为了保持 以前的值。类似适用于 case
语句。
通常,必须避免组合循环:
A general intention of a combinational circuit is that the output is a function of input only and the circuit should not contain any internal
state (i.e., memory).
作为一个矛盾,verilog标准规定一个变量必须retain/hold它的前一个值如果它没有被赋值始终块中的值。这是闩创建的根本原因。
要避免闩锁,必须牢记以下几点:
- 包括所有个
if
或case
语句的分支。
- 为每个分支中的每个输出信号赋值。
在这里,为了避免创建锁存器,您也可以分支并显式
分配所有输出变量,以便其他输入 grounded.
if (loadSquare)
square <= 9'h1; //used on initialization
else
square <= 9'h0; // similar for all the variables
另一个备选方案是在每个时钟滴答时分配一个默认值。
always @ (posedge clk)
begin
square <= 9'h0; // similar for all the variables
if (loadSquare)
square <= 9'h1; //used on initialization
end
旁注:我在这里使用了非阻塞赋值语句,用于正确的触发器合成。
有关详细的综合信息,请参阅 FPGA prototyping by Verilog examples by Pong P. Chu pdf. Also, this and this 有关闩锁创建的链接可能会有用。
图片礼貌 doulous.com.
锁存器是由状态机逻辑引起的。下面的 always 块对 currentState
而不是时钟敏感。这不是 坏事 ,但需要一些额外的预防措施来阻止 latch 创建:
- 使用
default
案例或
- 使用默认分配或
- 使用不同的 FSM 模式
这是你的代码,我使用默认赋值进行了添加:
//Finite State Machine
always @(currentState) begin
// default assignments
{loadSquare, loadDelta} = 2'b0;
{addSquare, addDelta, decDelta} = 3'h0;
case(currentState)
2'h0: begin //initialize values, wait for start
{loadSquare, loadDelta} = 2'b11;
{addSquare, addDelta, decDelta} = 3'h0;
end
2'h1: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b110; //add square and delta
end
2'h2: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b001; //decrement delta, wait for reset
end
default: ; // unused
endcase
//Next state logic implemented on negedge clk (not shown)
有关闩锁创建的更多信息,请查看@sharvil111 的回答。他以更笼统的方式解决了这个话题。
锁存器是一个基本的存储元件,它是打开或关闭的,即它是电平敏感的。触发器基本上是两个锁存器,其中一个在使能信号的反相上运行,这使得它对边沿敏感。
当使用 always @(posedge clk)
时,您暗示了一个在 clk
的上升沿加载数据值的触发器。闩锁不会隐含在此过程中 (always @(posedge clk)
)。
正如 Sharvil111 所描述的那样,当您在组合部分(即 always @*
进程)中留下未定义的状态时,闩锁是隐含的。如果某些东西在条件的一部分中未定义,那么它会保留其值。值保留是状态,并且由于组合部分对边缘不敏感,您已强制工具插入闩锁。
为避免这种情况,请完全定义条件输出:
always @(currentState) begin
case(currentState)
2'h0: begin //initialize values, wait for start
{loadSquare, loadDelta} = 2'b11;
{addSquare, addDelta, decDelta} = 3'h0;
end
2'h1: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b110; //add square and delta
end
2'h2: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b001; //decrement delta, wait for reset
end
default: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b000;
end
endcase
当组合块中的变量未在块功能的所有可能排列中分配值时,推断为锁存器。
case(currentState)
2'h0: begin //initialize values, wait for start
{loadSquare, loadDelta} = 2'b11;
{addSquare, addDelta, decDelta} = 3'h0;
end
2'h1: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b110; //add square and delta
end
2'h2: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b001; //decrement delta, wait for reset
end
default: ; // unused // <-- not assigned so assumed keep; inferred latches
endcase
...
做类似addSquare = addSquare;
的事情仍然是一个推断闩锁。 addSquare
(以及所有其他变量)需要分配给常数、触发器(边沿敏感触发器)或常数和触发器值的组合函数项。
如果您确实不需要 addSquare
(以及所有其他变量),那么只需将它们分配给 default
条件中的常量即可。
如果确实需要保留该值,则需要添加一个同步分配给变量的触发器。在default
条件下,变量需要赋值给翻牌。示例:
always @(posedge clk) begin
...
addSquare_keep <= addSquare;
...
end
always @* begin
...
case(currentState)
...
default : begin
...
addSquare = addSquare_keep;
...
end
endcase
...
end
我正在尝试在 Verilog 中编写一个 controller/data-path 实现,但我对什么会导致不需要的闩锁感到困惑。本质上,我有一个状态机在 negedge 时钟上更新。该状态机根据机器所处的状态向数据路径发送 5 个控制信号(loadSquare、loadDelta、addDelta 等)。数据路径和控制器的代码如下所示。
数据路径
//Control lines
reg addSquare, addDelta, decDelta;
reg loadSquare, loadDelta;
//Input lines
reg [8:0] square, delta;
//Output register
reg [7:0] outReg;
always @(posedge clk) begin
if (loadSquare)
square = 9'h1; //used on initialization
if (loadDelta)
delta = 9'h3; //used on initialization
if (addSquare)
square = square + delta;
if (addDelta)
delta = delta + 2'h2;
if (decDelta)
outReg = (delta>>1) - 1; //used for output
else
outReg = Input;
end
控制器
//Output of module
assign Output = outReg;
//Finite State Machine
always @(currentState) begin
case(currentState)
2'h0: begin //initialize values, wait for start
{loadSquare, loadDelta} = 2'b11;
{addSquare, addDelta, decDelta} = 3'h0;
end
2'h1: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b110; //add square and delta
end
2'h2: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b001; //decrement delta, wait for reset
end
default: ; // unused
endcase
//Next state logic implemented on negedge clk (not shown)
此代码在 Xilinx 中生成以下警告:
WARNING:Xst:737 - Found 1-bit latch for signal <addDelta>. Latches may be generated from incomplete case or if statements. We do not recommend the use of latches in FPGA/CPLD designs, as they may lead to timing problems.
WARNING:Xst:737 - Found 1-bit latch for signal <decDelta>. Latches may be generated from incomplete case or if statements. We do not recommend the use of latches in FPGA/CPLD designs, as they may lead to timing problems.
WARNING:Xst:737 - Found 1-bit latch for signal <loadDelta>. Latches may be generated from incomplete case or if statements. We do not recommend the use of latches in FPGA/CPLD designs, as they may lead to timing problems.
WARNING:Xst:1294 - Latch <loadDelta> is equivalent to a wire in block <ModuleName>.
WARNING:Xst:1294 - Latch <decDelta> is equivalent to a wire in block <ModuleName>.
WARNING:Xst:1294 - Latch <addDelta> is equivalent to a wire in block <ModuleName>.
我知道不完整的 if 语句会导致闩锁。为了尝试解决这个问题,我尝试了 2 种不同的实现,但它们没有删除警告。我对 "decDelta" 案例特别困惑,因为我不明白我在这个条件语句中没有考虑到什么。
尝试#1
always @(posedge clk) begin
if (loadSquare)
square = 9'h1;
else
square = square;
if (loadDelta)
delta = 9'h3;
else
delta = delta;
//... and so on
试试#2
always @(posedge clk) begin
square = square;
delta = delta;
if (loadSquare)
square = 9'h1;
if (loadDelta)
delta = 9'h3;
//... and so on
当我 运行 模拟时,代码按预期工作,但我想更多地了解导致这些警告的原因。
当一个变量必须保留其先前的值时,如果它未分配一个始终块中的值,则推断出锁存器.必须创建一个锁存器来存储这个当前值。
闩锁 会导致各种竞争条件。不需要的锁存器会在 组合电路 中产生反馈,即将输出路由回输入 - 这可能是不可预测的,从而导致不稳定的电路行为。
不完整 if-else
语句将生成不需要的闩锁。如果没有为所有可能的输入条件定义条件之一,则 if-else 语句被视为 "incomplete"。同样,不完整的 case
语句,即没有 default
语句的语句也可以推断为 latch.
A complete if-else
语句引用了以下Mux:
而不完整 if-else
指的是从输出到输入的反馈路径,为了保持 以前的值。类似适用于 case
语句。
通常,必须避免组合循环:
A general intention of a combinational circuit is that the output is a function of input only and the circuit should not contain any internal state (i.e., memory).
作为一个矛盾,verilog标准规定一个变量必须retain/hold它的前一个值如果它没有被赋值始终块中的值。这是闩创建的根本原因。
要避免闩锁,必须牢记以下几点:
- 包括所有个
if
或case
语句的分支。 - 为每个分支中的每个输出信号赋值。
在这里,为了避免创建锁存器,您也可以分支并显式 分配所有输出变量,以便其他输入 grounded.
if (loadSquare)
square <= 9'h1; //used on initialization
else
square <= 9'h0; // similar for all the variables
另一个备选方案是在每个时钟滴答时分配一个默认值。
always @ (posedge clk)
begin
square <= 9'h0; // similar for all the variables
if (loadSquare)
square <= 9'h1; //used on initialization
end
旁注:我在这里使用了非阻塞赋值语句,用于正确的触发器合成。
有关详细的综合信息,请参阅 FPGA prototyping by Verilog examples by Pong P. Chu pdf. Also, this and this 有关闩锁创建的链接可能会有用。
图片礼貌 doulous.com.
锁存器是由状态机逻辑引起的。下面的 always 块对 currentState
而不是时钟敏感。这不是 坏事 ,但需要一些额外的预防措施来阻止 latch 创建:
- 使用
default
案例或 - 使用默认分配或
- 使用不同的 FSM 模式
这是你的代码,我使用默认赋值进行了添加:
//Finite State Machine
always @(currentState) begin
// default assignments
{loadSquare, loadDelta} = 2'b0;
{addSquare, addDelta, decDelta} = 3'h0;
case(currentState)
2'h0: begin //initialize values, wait for start
{loadSquare, loadDelta} = 2'b11;
{addSquare, addDelta, decDelta} = 3'h0;
end
2'h1: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b110; //add square and delta
end
2'h2: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b001; //decrement delta, wait for reset
end
default: ; // unused
endcase
//Next state logic implemented on negedge clk (not shown)
有关闩锁创建的更多信息,请查看@sharvil111 的回答。他以更笼统的方式解决了这个话题。
锁存器是一个基本的存储元件,它是打开或关闭的,即它是电平敏感的。触发器基本上是两个锁存器,其中一个在使能信号的反相上运行,这使得它对边沿敏感。
当使用 always @(posedge clk)
时,您暗示了一个在 clk
的上升沿加载数据值的触发器。闩锁不会隐含在此过程中 (always @(posedge clk)
)。
正如 Sharvil111 所描述的那样,当您在组合部分(即 always @*
进程)中留下未定义的状态时,闩锁是隐含的。如果某些东西在条件的一部分中未定义,那么它会保留其值。值保留是状态,并且由于组合部分对边缘不敏感,您已强制工具插入闩锁。
为避免这种情况,请完全定义条件输出:
always @(currentState) begin
case(currentState)
2'h0: begin //initialize values, wait for start
{loadSquare, loadDelta} = 2'b11;
{addSquare, addDelta, decDelta} = 3'h0;
end
2'h1: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b110; //add square and delta
end
2'h2: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b001; //decrement delta, wait for reset
end
default: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b000;
end
endcase
当组合块中的变量未在块功能的所有可能排列中分配值时,推断为锁存器。
case(currentState)
2'h0: begin //initialize values, wait for start
{loadSquare, loadDelta} = 2'b11;
{addSquare, addDelta, decDelta} = 3'h0;
end
2'h1: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b110; //add square and delta
end
2'h2: begin
{loadSquare, loadDelta} = 2'b00;
{addSquare, addDelta, decDelta} = 3'b001; //decrement delta, wait for reset
end
default: ; // unused // <-- not assigned so assumed keep; inferred latches
endcase
...
做类似addSquare = addSquare;
的事情仍然是一个推断闩锁。 addSquare
(以及所有其他变量)需要分配给常数、触发器(边沿敏感触发器)或常数和触发器值的组合函数项。
如果您确实不需要 addSquare
(以及所有其他变量),那么只需将它们分配给 default
条件中的常量即可。
如果确实需要保留该值,则需要添加一个同步分配给变量的触发器。在default
条件下,变量需要赋值给翻牌。示例:
always @(posedge clk) begin
...
addSquare_keep <= addSquare;
...
end
always @* begin
...
case(currentState)
...
default : begin
...
addSquare = addSquare_keep;
...
end
endcase
...
end