为什么If语句会导致verilog中的锁存?

时间:2015-11-13 02:56:44

标签: verilog fpga xilinx

我正在尝试在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

当我运行模拟时,代码按预期工作,但我想了解更多关于导致这些警告的原因。

4 个答案:

答案 0 :(得分:1)

如果变量必须保留其先前的值,如果未分配始终为块中的值,则推断出锁存器。必须创建一个锁存器以存储此现值。

Latches 会导致各种竞争条件。不需要的锁存器在组合电路中创建反馈,即它将输出路由回输入 - 这可能是不可预测的,从而导致电路行为不稳定。

不完整 if-else语句会生成不需要的锁存器。 if-else语句被视为&#34;不完整&#34;如果没有为所有可能的输入条件定义其中一个条件。同样,不具有case语句的不完整default语句也可以推断为锁存。

完整 if-else语句引用以下Mux:

Mux

虽然不完整 if-else指的是从输出到输入的反馈路径,以便保持之前的值。类似适用于case声明。

MuxLatch

通常,必须避免组合循环:

  

组合电路的一般意图是输出仅是输入的函数,电路不应包含任何内部电路   国家(即记忆)。

作为矛盾,verilog标准指定变量必须保留/保持其先前值(如果未在always块中分配值)。这是锁存器创建的根本原因。

避免锁存器,必须牢记以下几点:

  • 包含所有 ifcase声明的分支。
  • 每个分支中的每个输出信号分配一个值。

在这里,为了避免锁存器创建,你可以分支和显式 分配所有输出变量,以便其他输入接地

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。此外,关于锁存器创建的thisthis链接可能很有用。

图片由doulous.com提供。

答案 1 :(得分:0)

锁存器由状态机逻辑引起。以下始终阻止对currentState敏感,而不对时钟敏感。这不是,但需要一些额外的预防措施来阻止锁存器的创建:

  1. 使用default案例或
  2. 使用默认分配或
  3. 使用不同的FSM模式
  4. 以下是我使用默认作业添加的代码:

    //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