解决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
修改要点
- 默认值初始化:在
always @(*)块开头给所有输出信号(包括BufferNext)设置默认值,确保每个信号在所有分支下都有明确的赋值逻辑。 - 避免部分位赋值:在
sRX_DATA_RECEIVE状态中,修改单个位时,先保持BufferNext的整体值,再更新目标位,确保所有位都被覆盖。 - 简化冗余代码:去掉重复的
BufferNext <= BufferNext赋值,默认值已覆盖这些场景。
额外优化建议
- 你当前的
BufferNext在时序逻辑中被赋值为BufferNext <= BufferNext,这其实是多余的,因为BufferNext是组合逻辑驱动的寄存器,应该直接由组合逻辑块的BufferNext输出驱动,时序逻辑中只需保留RxSerialBuffer <= BufferNext即可。 - 输入信号
iRxSerial已经做了两级同步(rRx1、rRx2),但在组合逻辑中直接使用iRxSerial,建议改为使用同步后的rRx2,避免亚稳态影响。
内容的提问来源于stack exchange,提问作者xkffkx
相关产品推荐
相关产品推荐

