我正在尝试在Verilog中编写控制器/数据路径实现,我很困惑会导致不必要的锁存器。基本上,我有一个状态机更新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语句不完整会导致锁存。为了试图解释这一点,我尝试了两种不同的实现,但它们没有删除警告。我特别对“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
当我运行模拟时,代码按预期工作,但我想了解更多关于导致这些警告的原因。
答案 0 :(得分:1)
如果变量必须保留其先前的值,如果未分配始终为块中的值,则推断出锁存器。必须创建一个锁存器以存储此现值。
Latches 会导致各种竞争条件。不需要的锁存器在组合电路中创建反馈,即它将输出路由回输入 - 这可能是不可预测的,从而导致电路行为不稳定。
不完整 if-else
语句会生成不需要的锁存器。 if-else语句被视为&#34;不完整&#34;如果没有为所有可能的输入条件定义其中一个条件。同样,不具有case
语句的不完整default
语句也可以推断为锁存。
完整 if-else
语句引用以下Mux:
虽然不完整 if-else
指的是从输出到输入的反馈路径,以便保持之前的值。类似适用于case
声明。
通常,必须避免组合循环:
组合电路的一般意图是输出仅是输入的函数,电路不应包含任何内部电路 国家(即记忆)。
作为矛盾,verilog标准指定变量必须保留/保持其先前值(如果未在always块中分配值)。这是锁存器创建的根本原因。
要避免锁存器,必须牢记以下几点:
if
或case
声明的分支。在这里,为了避免锁存器创建,你可以分支和显式 分配所有输出变量,以便其他输入接地。
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。此外,关于锁存器创建的this和this链接可能很有用。
图片由doulous.com提供。
答案 1 :(得分:0)
锁存器由状态机逻辑引起。以下始终阻止对currentState
敏感,而不对时钟敏感。这不是坏,但需要一些额外的预防措施来阻止锁存器的创建:
default
案例或以下是我使用默认作业添加的代码:
//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的答案。他以更一般的方式解决了这个问题。
答案 2 :(得分:0)
锁存器是一个基本的存储器元件,它是打开或关闭的,即它是电平敏感的。触发器基本上是两个锁存器,其中一个锁存器工作于使能信号的反相,这使得它对边缘敏感。
使用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
答案 3 :(得分:0)
当组合块中的变量未在块功能的所有可能排列中分配值时,推断出锁存器。
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