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

解决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

修复说明

  1. 跨时钟域处理:使用异步FIFO实现不同时钟域间的数据缓存,避免亚稳态,保证数据完整性。
  2. Slave端优化:只要FIFO未满就置tready,与上游完成tvalid/tready握手后接收数据,不再限制tlast信号,支持连续数据传输。
  3. 输入计数功能:新增input_count寄存器,每完成一次握手就计数加1,实现输入数据的统计。
  4. Master端握手逻辑:从FIFO读取数据后保持tvalid直到下游给出tready,确保数据被正确采样,完成握手后撤销tvalid。
  5. 完整初始化:所有寄存器在复位时都做了明确初始化,避免未知状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:43:08