Search code examples
veriloguartvivado

Inferring latch message for BufferNext in uart_rx module during Synthesis


I keep getting this error for the BufferNext register, and I have no idea why; any help is appreciated. Here is the code for the module:

`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

As far as I know, to get rid of latches, one should make sure that every if has an else, as well as each case statement has a default case. As far as I can tell, that shouldn't be the problem, as I have indeed added an else to each if, and a default to each case, and assigned a value to BufferNext explicitly in each one of them (Sometimes it's just BufferNext <= BufferNext just for the sake of explicitly assigning something to BufferNext). The objective here would be to get rid of that latch.


Solution

  • The cause of the latchs is this:

      default :
        begin
          wFSM_Next = sIDLE;
          wCnt_Next = 0;
          wBit_Next = 0;
          BufferNext = BufferNext;
    
        end 
    

    Its feedback around combinational logic on BufferNext.
    You are asking it to hold its value.

    As another answer pointed out, there is a serious problem that BufferNext is driven in the combinational process and in the clocked process that infers registers.

    Typical state machine style would have BufferNext combinational only, and it would act as input to a register called Buffer.

    Another problem is the use of non-blocking assignments in a combinational process. This can cause simulation & synthesis mismatches.
    Use:

    BufferNext = <RHS>;    
    

    Rather than:

    BufferNext <= <RHS>;    
    

    In combitational processes (like a case statement)