You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

解决uart_rx模块BufferNext综合时的锁存器推断错误

UART_RX模块锁存器推断问题排查与解决

问题描述

在综合UART_RX模块时,持续收到关于BufferNext寄存器的"Inferring latch"错误,已确保每个if有else分支、case语句有default分支,且所有分支中为BufferNext显式赋值(有时为BufferNext <= BufferNext),但错误仍存在,目标是消除该锁存器推断问题。

模块代码

`timescale 1ns / 1ps

module uart_rx #(
  parameter   CLK_FREQ      = 125_000_000,
  parameter   BAUD_RATE     = 115_200,
  // Example: 125 MHz Clock / 115200 baud UART -> CLKS_PER_BIT = 1085 
  parameter   CLKS_PER_BIT  = CLK_FREQ / BAUD_RATE
)
(
  input wire        iClk, iRst,
  input wire        iRxSerial,
  output wire [7:0] oRxByte, 
  output wire       oRxDone
);

  localparam sIDLE              = 3'b000;
  localparam sSTART_DETECT      = 3'b001;
  localparam sRX_DATA_RECEIVE   = 3'b010;
  localparam sRX_END_DETECT     = 3'b011;
  localparam sRX_DONE           = 3'b100;

// Double-register the input wire to prevent metastability issues
  reg rRx1, rRx2;
  
  reg [7:0] RxSerialBuffer, BufferNext;

// -> FSM state
  reg [2:0] rFSM_Current, wFSM_Next; 
  
  // -> counter to keep track of the clock cycles
  reg [$clog2(CLKS_PER_BIT):0]   rCnt_Current, wCnt_Next;
    
  // -> counter to keep track of sent bits
  // (between 0 and 7)
  reg [6:0] rBit_Current, wBit_Next;

  

always @(posedge iClk)
  begin
    rRx1 <= iRxSerial;
    rRx2 <= rRx1;
    
    if (iRst==1)
          begin
            rFSM_Current <= sIDLE;
            rCnt_Current <= 0;
            rBit_Current <= 0;
            RxSerialBuffer <= 0;
            BufferNext <= 0;
          end
    else
          begin
            rFSM_Current <= wFSM_Next;
            rCnt_Current <= wCnt_Next;
            rBit_Current <= wBit_Next;
            BufferNext <= BufferNext;
            RxSerialBuffer <= BufferNext;
          end 
  end
  
  always @(*)
  begin
      case(rFSM_Current)
      
          sIDLE: begin
                     wCnt_Next = 0;
                     wBit_Next = 0;
                     
                     if(iRxSerial == 0) 
                         begin
                            wFSM_Next = sSTART_DETECT;
                            BufferNext <= 0;
                         end
                        
                     else 
                         begin
                            wFSM_Next = sIDLE;
                            BufferNext <= BufferNext;
                         end
                 end
                 
          sSTART_DETECT: begin
                     wBit_Next = 0;
                     
                      if (rCnt_Current < (CLKS_PER_BIT - 1) )
                        begin
                          wFSM_Next = sSTART_DETECT;
                          wCnt_Next = rCnt_Current + 1;
                          BufferNext <= BufferNext;
                        end
                      else
                        begin
                          wFSM_Next = sRX_DATA_RECEIVE;
                          wCnt_Next = 0;
                          BufferNext <= BufferNext;
                          end
                       end
                        
          sRX_DATA_RECEIVE: begin
                 if (rCnt_Current < (CLKS_PER_BIT - 1) )
                begin
               
                  if(rCnt_Current >= (CLKS_PER_BIT - 1)/2)
                      begin
                       BufferNext[rBit_Current] <= iRxSerial;
                      end
                  
                  else
                      begin
                        BufferNext[rBit_Current] <= BufferNext[rBit_Current];
                      end
                  
                  
                  wFSM_Next = sRX_DATA_RECEIVE;
                  wCnt_Next = rCnt_Current + 1;
                  wBit_Next = rBit_Current;
                end
              else
                begin
                  wCnt_Next = 0;
                  
                  if (rBit_Current != 7)
                    begin
                      wFSM_Next = sRX_DATA_RECEIVE;
                      wBit_Next = rBit_Current + 1;
                    end
                  else
                    begin
                      wFSM_Next = sRX_END_DETECT;
                      wBit_Next = 0;
                    end
                    
                  BufferNext <= BufferNext;
                    
                end
             end
             
         sRX_END_DETECT: begin
              wBit_Next = 0;
               
              if (rCnt_Current < (CLKS_PER_BIT - 1) )
                begin
                  wFSM_Next = sRX_END_DETECT;
                  wCnt_Next = rCnt_Current + 1;
                end
              else
                begin
                  wFSM_Next = sRX_DONE;
                  wCnt_Next = 0;
                end
                
              BufferNext <= BufferNext;
            end 
            
        sRX_DONE: begin
              wBit_Next = 0;
              wCnt_Next = 0;
              BufferNext <= BufferNext;
              wFSM_Next = sIDLE;
            end
           
           
          default :
            begin
              wFSM_Next = sIDLE;
              wCnt_Next = 0;
              wBit_Next = 0;
              BufferNext <= BufferNext;

            end 
    
      endcase
      
   end

assign oRxByte = RxSerialBuffer;

assign oRxDone = (rFSM_Current == sRX_DONE) ? 1 : 0;
   
endmodule

问题根源

问题出在sRX_DATA_RECEIVE状态的第一个if分支中:你仅对BufferNext的单个位(BufferNext[rBit_Current])进行赋值,而没有明确处理其他7位的状态。综合器会认为这些未被赋值的位需要保持原有值,因此推断出锁存器。

即使你在分支里写了BufferNext[rBit_Current] <= BufferNext[rBit_Current],也只覆盖了当前操作的那一位,其余位仍处于未明确赋值的状态,这是组合逻辑中产生锁存器的典型场景。

解决方案

在组合逻辑always @(*)块的开头,先给BufferNext赋一个默认值(通常是保持当前值),确保所有位在任何分支下都有明确的赋值逻辑,避免综合器推断锁存器。具体修改如下:

修改后的组合逻辑块

always @(*)
begin
    // 先给所有信号赋默认值,避免锁存器
    wFSM_Next = rFSM_Current;
    wCnt_Next = rCnt_Current;
    wBit_Next = rBit_Current;
    BufferNext = BufferNext; // 默认保持当前值

    case(rFSM_Current)
    
        sIDLE: begin
                   wCnt_Next = 0;
                   wBit_Next = 0;
                   
                   if(iRxSerial == 0) 
                       begin
                          wFSM_Next = sSTART_DETECT;
                          BufferNext = 8'h00; // 整体赋值,而非部分位
                       end
                       // else分支无需重复赋值,默认值已覆盖
               end
               
        sSTART_DETECT: begin
                   wBit_Next = 0;
                   
                    if (rCnt_Current < (CLKS_PER_BIT - 1) )
                      begin
                        wFSM_Next = sSTART_DETECT;
                        wCnt_Next = rCnt_Current + 1;
                      end
                    else
                      begin
                        wFSM_Next = sRX_DATA_RECEIVE;
                        wCnt_Next = 0;
                      end
                   end
                    
        sRX_DATA_RECEIVE: begin
               if (rCnt_Current < (CLKS_PER_BIT - 1) )
              begin
         
                wFSM_Next = sRX_DATA_RECEIVE;
                wCnt_Next = rCnt_Current + 1;
                wBit_Next = rBit_Current;

                if(rCnt_Current >= (CLKS_PER_BIT - 1)/2)
                    begin
                     // 先复制原有值,再修改目标位
                     BufferNext = BufferNext;
                     BufferNext[rBit_Current] = iRxSerial;
                    end
                // else分支无需处理,默认值已保持当前状态
              end
            else
              begin
                wCnt_Next = 0;
                
                if (rBit_Current != 7)
                  begin
                    wFSM_Next = sRX_DATA_RECEIVE;
                    wBit_Next = rBit_Current + 1;
                  end
                else
                  begin
                    wFSM_Next = sRX_END_DETECT;
                    wBit_Next = 0;
                  end
              end
         end
         
     sRX_END_DETECT: begin
          wBit_Next = 0;
           
          if (rCnt_Current < (CLKS_PER_BIT - 1) )
            begin
              wFSM_Next = sRX_END_DETECT;
              wCnt_Next = rCnt_Current + 1;
            end
          else
            begin
              wFSM_Next = sRX_DONE;
              wCnt_Next = 0;
            end
        end 
        
    sRX_DONE: begin
          wBit_Next = 0;
          wCnt_Next = 0;
          wFSM_Next = sIDLE;
        end
       
       
      default :
        begin
          wFSM_Next = sIDLE;
          wCnt_Next = 0;
          wBit_Next = 0;
        end 

  endcase
  
 end

修改要点

  1. 默认值初始化:在always @(*)块开头给所有输出信号(包括BufferNext)设置默认值,确保每个信号在所有分支下都有明确的赋值逻辑。
  2. 避免部分位赋值:在sRX_DATA_RECEIVE状态中,修改单个位时,先保持BufferNext的整体值,再更新目标位,确保所有位都被覆盖。
  3. 简化冗余代码:去掉重复的BufferNext <= BufferNext赋值,默认值已覆盖这些场景。

额外优化建议

  • 你当前的BufferNext在时序逻辑中被赋值为BufferNext <= BufferNext,这其实是多余的,因为BufferNext是组合逻辑驱动的寄存器,应该直接由组合逻辑块的BufferNext输出驱动,时序逻辑中只需保留RxSerialBuffer <= BufferNext即可。
  • 输入信号iRxSerial已经做了两级同步(rRx1、rRx2),但在组合逻辑中直接使用iRxSerial,建议改为使用同步后的rRx2,避免亚稳态影响。

内容的提问来源于stack exchange,提问作者xkffkx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 11:17:24