解决memory_controller模块s01_axis_tdata数据丢失逻辑错误
修复AXI流缓存控制器的数据丢失问题
问题背景
需实现对s01_axis_tdata的所有输入进行计数并缓存,确保按原顺序输出至下游,但当前memory_controller模块存在逻辑错误,导致输入数据丢失,波形显示无法跟踪输入信号。
原代码核心问题分析
- 跨时钟域信号未同步:
flag1和flag2直接在两个异步时钟域间传递,未做同步处理,极易产生亚稳态,导致状态机逻辑混乱。 - Slave端接收条件过严:仅当
s01_axis_tvalid、s01_axis_tstrb、s01_axis_tlast同时为高时才接收数据,无法处理非帧结束的普通传输,直接丢弃大部分输入。 - 缓存容量不足:仅用单个
tmp寄存器存储数据,无法缓存多笔连续输入,后续数据直接覆盖或丢失。 - AXI流握手逻辑错误:Master端未等待
m01_axis_tready的握手确认就完成传输,且tvalid仅保持一个周期,极易导致下游采样不到数据。 - 状态机与信号初始化不完整:部分寄存器复位时未正确初始化,存在未知状态风险。
修复后的代码实现
使用异步FIFO处理跨时钟域数据缓存,修正AXI流握手逻辑,增加输入计数功能:
module memory_controller #( parameter DATA_WIDTH = 32, parameter FIFO_DEPTH = 16 // 根据需求调整缓存深度 ) ( // Slave input ports input s01_axis_aclk, input s01_axis_aresetn, input [DATA_WIDTH-1:0] s01_axis_tdata, input [(DATA_WIDTH/8)-1:0] s01_axis_tstrb, input s01_axis_tvalid, input s01_axis_tlast, output reg s01_axis_tready, // Master output ports input m01_axis_aclk, input m01_axis_aresetn, input m01_axis_tready, output reg [DATA_WIDTH-1:0] m01_axis_tdata, output reg [(DATA_WIDTH/8)-1:0] m01_axis_tstrb, output reg m01_axis_tvalid, output reg m01_axis_tlast, // 输入计数输出 output reg [$clog2(FIFO_DEPTH+1)-1:0] input_count ); // 异步FIFO信号 wire fifo_wr_en; wire fifo_rd_en; wire [DATA_WIDTH+ (DATA_WIDTH/8) +1 -1:0] fifo_wr_data; wire [DATA_WIDTH+ (DATA_WIDTH/8) +1 -1:0] fifo_rd_data; wire fifo_full; wire fifo_empty; // 拼接写入FIFO的数据:tdata + tstrb + tlast assign fifo_wr_data = {s01_axis_tlast, s01_axis_tstrb, s01_axis_tdata}; assign fifo_wr_en = s01_axis_tvalid && s01_axis_tready; assign fifo_rd_en = m01_axis_tready && !fifo_empty && m01_axis_tvalid; // 异步FIFO实例化(可替换为IP核或自定义异步FIFO) async_fifo #( .DATA_WIDTH(DATA_WIDTH + (DATA_WIDTH/8) + 1), .DEPTH(FIFO_DEPTH) ) u_async_fifo ( .wr_clk(s01_axis_aclk), .wr_rst_n(s01_axis_aresetn), .rd_clk(m01_axis_aclk), .rd_rst_n(m01_axis_aresetn), .wr_en(fifo_wr_en), .wr_data(fifo_wr_data), .rd_en(fifo_rd_en), .rd_data(fifo_rd_data), .full(fifo_full), .empty(fifo_empty) ); // Slave端逻辑:接收AXI流数据,更新计数 always @(posedge s01_axis_aclk) begin if (~s01_axis_aresetn) begin s01_axis_tready <= 1'b0; input_count <= 'd0; end else begin // 当FIFO未满时,置tready允许上游传输 s01_axis_tready <= !fifo_full; // 握手成功则计数加1 if (fifo_wr_en) begin input_count <= input_count + 1'b1; end end end // Master端逻辑:从FIFO读取数据并发送 always @(posedge m01_axis_aclk) begin if (~m01_axis_aresetn) begin m01_axis_tvalid <= 1'b0; m01_axis_tdata <= 'b0; m01_axis_tstrb <= 'b0; m01_axis_tlast <= 'b0; end else begin // FIFO非空且未在传输状态时,准备输出数据 if (!fifo_empty && !m01_axis_tvalid) begin m01_axis_tvalid <= 1'b1; {m01_axis_tlast, m01_axis_tstrb, m01_axis_tdata} <= fifo_rd_data; end // 握手成功后,撤销tvalid else if (fifo_rd_en) begin m01_axis_tvalid <= 1'b0; end end end // 自定义异步FIFO实现(可选,若使用IP核可删除) module async_fifo #( parameter DATA_WIDTH = 40, parameter DEPTH = 16 ) ( input wr_clk, input wr_rst_n, input rd_clk, input rd_rst_n, input wr_en, input [DATA_WIDTH-1:0] wr_data, input rd_en, output [DATA_WIDTH-1:0] rd_data, output full, output empty ); localparam ADDR_WIDTH = $clog2(DEPTH); reg [DATA_WIDTH-1:0] mem [DEPTH-1:0]; reg [ADDR_WIDTH:0] wr_ptr; reg [ADDR_WIDTH:0] rd_ptr; reg [ADDR_WIDTH:0] rd_ptr_sync1; reg [ADDR_WIDTH:0] rd_ptr_sync2; reg [ADDR_WIDTH:0] wr_ptr_sync1; reg [ADDR_WIDTH:0] wr_ptr_sync2; // 写指针更新 always @(posedge wr_clk or negedge wr_rst_n) begin if (~wr_rst_n) begin wr_ptr <= 'd0; end else if (wr_en && !full) begin mem[wr_ptr[ADDR_WIDTH-1:0]] <= wr_data; wr_ptr <= wr_ptr + 1'b1; end end // 读指针更新 always @(posedge rd_clk or negedge rd_rst_n) begin if (~rd_rst_n) begin rd_ptr <= 'd0; end else if (rd_en && !empty) begin rd_ptr <= rd_ptr + 1'b1; end end // 读指针同步到写时钟域 always @(posedge wr_clk or negedge wr_rst_n) begin if (~wr_rst_n) begin rd_ptr_sync1 <= 'd0; rd_ptr_sync2 <= 'd0; end else begin rd_ptr_sync1 <= rd_ptr; rd_ptr_sync2 <= rd_ptr_sync1; end end // 写指针同步到读时钟域 always @(posedge rd_clk or negedge rd_rst_n) begin if (~rd_rst_n) begin wr_ptr_sync1 <= 'd0; wr_ptr_sync2 <= 'd0; end else begin wr_ptr_sync1 <= wr_ptr; wr_ptr_sync2 <= wr_ptr_sync1; end end // 空满判断 assign empty = (rd_ptr == wr_ptr_sync2); assign full = ({~wr_ptr[ADDR_WIDTH], wr_ptr[ADDR_WIDTH-1:0]} == rd_ptr_sync2); assign rd_data = mem[rd_ptr[ADDR_WIDTH-1:0]]; endmodule endmodule
修复说明
- 跨时钟域处理:使用异步FIFO实现不同时钟域间的数据缓存,避免亚稳态,保证数据完整性。
- Slave端优化:只要FIFO未满就置
tready,与上游完成tvalid/tready握手后接收数据,不再限制tlast信号,支持连续数据传输。 - 输入计数功能:新增
input_count寄存器,每完成一次握手就计数加1,实现输入数据的统计。 - Master端握手逻辑:从FIFO读取数据后保持
tvalid直到下游给出tready,确保数据被正确采样,完成握手后撤销tvalid。 - 完整初始化:所有寄存器在复位时都做了明确初始化,避免未知状态。
内容的提问来源于stack exchange,提问作者ArthurNamz
相关产品推荐
相关产品推荐

