RDMA RC 协议数据缓冲模块设计:从需求到验证全解析

RDMA RC协议数据缓冲模块设计:从需求到验证全解析

在RDMA(远程直接内存访问)RC(可靠连接)协议的硬件实现中,数据缓冲模块是保障传输稳定性的核心枢纽------它既要衔接AXI-Stream接口的高速数据流入,又要配合流量控制模块实现"按需发送",还要解决数据帧的时序错配问题。本文将从工程实践角度,完整拆解一款适配QP状态机、流量控制与PDU解析模块的RDMA RC数据缓冲模块,包含设计思路、核心逻辑、验证方法及集成技巧,代码可直接用于FPGA/IC开发。

一、模块定位:为什么需要专属缓冲模块?

在RDMA RC传输链路中,缓冲模块的核心价值是解决"三大错配"问题,这也是其区别于通用FIFO的关键:

  1. 时序错配:PDU解析模块输出数据的突发特性,与流量控制模块"Credit耗尽则暂停"的刚性要求存在冲突,缓冲可平滑数据流入流出的节奏;
  2. 接口错配 :上游PDU解析模块通常输出零散数据,下游传输模块需整帧发送,缓冲模块需保障帧完整性(通过tlast信号锁存);
  3. 速率错配 :当流量控制模块反馈send_pause=1(Credit耗尽)时,缓冲模块需暂存新数据,避免数据丢失,恢复后无缝续传。

简单来说,该模块是QP状态机、流量控制、PDU解析三大模块的"数据中转站",其设计质量直接决定RDMA传输的可靠性与吞吐量。

二、核心需求:明确设计边界与指标

结合RDMA RC协议特性及前文实现的QP/flow_ctrl/PDU模块,缓冲模块需满足以下核心需求:

  1. 接口兼容 :支持AXI-Stream标准接口(tdata/tvalid/tready/tlast),无缝对接PDU解析模块与传输链路;
  2. 流量联动 :接收flow_ctrl模块的send_pause信号,暂停/恢复数据发送,无数据丢失;
  3. 背压机制 :缓冲满时输出backpressure信号,阻止上游PDU模块继续写入,避免数据溢出;
  4. 状态反馈 :输出buf_full/buf_empty状态,供flow_ctrl模块优化Credit分配策略;
  5. 参数可扩 :数据位宽(DATA_WIDTH)、缓冲深度(BUF_DEPTH)支持参数配置,适配不同传输速率需求;
  6. 整帧保障 :精准锁存tlast信号,确保数据帧完整发送,符合RDMA协议"整帧传输"要求。

三、核心设计:从接口到逻辑的完整实现

3.1 模块参数化设计(可扩展性关键)

采用参数化定义核心配置,避免硬编码,适配不同场景需求:

  • DATA_WIDTH:数据总线位宽,与PDU解析模块保持一致(默认64bit);
  • BUF_DEPTH:缓冲深度(默认16帧,需为2的幂次,简化地址生成);
  • ADDR_WIDTH:地址位宽,由BUF_DEPTH自动计算($clog2(BUF_DEPTH))。

3.2 接口定义(衔接上下游模块)

按"输入-输出"逻辑梳理接口,明确每个信号的作用与对接模块:

接口类型 信号名 位宽 作用说明 对接模块
全局信号 clk 1bit 系统时钟(100MHz) 所有模块
rst_n 1bit 异步低复位 所有模块
流量控制输入 send_pause 1bit 发送暂停信号(1=暂停) flow_ctrl模块
AXI-Stream接收 s_axis_tdata DATA_WIDTH 上游输入数据(PDU解析后的数据帧) PDU解析模块
s_axis_tvalid 1bit 接收数据有效标志 PDU解析模块
s_axis_tlast 1bit 接收帧尾标志(标记1帧PDU结束) PDU解析模块
s_axis_tready 1bit 接收就绪(背压核心,0=拒绝上游数据) PDU解析模块
AXI-Stream发送 m_axis_tdata DATA_WIDTH 下游发送数据(至传输链路) 传输模块
m_axis_tvalid 1bit 发送数据有效标志 传输模块
m_axis_tlast 1bit 发送帧尾标志 传输模块
m_axis_tready 1bit 下游接收就绪 传输模块
状态输出 buf_full 1bit 缓冲满标志 flow_ctrl/上层
buf_empty 1bit 缓冲空标志 flow_ctrl/上层
backpressure 1bit 背压信号(=buf_full,简化上游对接) PDU解析模块

3.3 内部核心结构

模块内部由"存储单元+控制逻辑"两部分组成,关键信号包括:

  • 存储单元:buf_mem[BUF_DEPTH-1:0](寄存器数组,实现数据暂存);
  • 地址计数器:wr_addr(写地址)、rd_addr(读地址),实现循环读写;
  • 状态寄存器:buf_cnt(缓冲数据计数,0~BUF_DEPTH)、tlast_r(帧尾锁存,保障整帧发送)。

3.4 关键逻辑解析(附代码片段)

缓冲模块的核心是"精准控制读写时序,联动外部信号实现可靠传输",以下拆解四大核心逻辑:

1. 写入控制:接收上游数据,锁存帧尾

当上游PDU模块输出有效数据(s_axis_tvalid=1)且缓冲就绪(s_axis_tready=1)时,将数据写入缓冲,并锁存帧尾信号供发送阶段使用:

verilog 复制代码
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        wr_addr <= {ADDR_WIDTH{1'b0}};
        tlast_r <= 1'b0;
    end else if (s_axis_tvalid && s_axis_tready) begin
        buf_mem[wr_addr] <= s_axis_tdata;  // 写入数据
        tlast_r <= s_axis_tlast;           // 锁存帧尾
        // 写地址循环自增
        wr_addr <= (wr_addr == BUF_DEPTH - 1) ? 0 : wr_addr + 1;
    end
end
2. 读取控制:联动流量控制,暂停/恢复发送

读取逻辑的核心是响应send_pause信号,同时确保帧尾同步输出:

  • send_pause=1(Credit耗尽)或缓冲空时,停止发送;
  • send_pause=0且下游就绪时,从缓冲读取数据并输出:
verilog 复制代码
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rd_addr <= 0;
        m_axis_tdata <= 0;
        m_axis_tvalid <= 0;
        m_axis_tlast <= 0;
    end else if (!send_pause && !buf_empty) begin
        if (m_axis_tready || !m_axis_tvalid) begin
            m_axis_tdata <= buf_mem[rd_addr];  // 读取数据
            m_axis_tvalid <= 1'b1;
            m_axis_tlast <= tlast_r;           // 同步输出帧尾
            // 读地址循环自增
            rd_addr <= (rd_addr == BUF_DEPTH - 1) ? 0 : rd_addr + 1;
            tlast_r <= (tlast_r) ? 1'b0 : tlast_r;  // 帧尾输出后清零
        end
    end else begin
        m_axis_tvalid <= 1'b0;  // 暂停或空缓冲时,停止发送
        m_axis_tlast <= 1'b0;
    end
end
3. 满空检测:基于计数实现精准状态反馈

通过buf_cnt(缓冲数据个数)判断满空状态,buf_cnt由"读写操作"共同更新:

  • 只写不读:buf_cnt += 1
  • 只读不写:buf_cnt -= 1
  • 读写同时:buf_cnt不变。
verilog 复制代码
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        buf_cnt <= 0;
    end else begin
        case ({s_axis_tvalid & s_axis_tready, m_axis_tvalid & m_axis_tready & !send_pause})
            2'b01: buf_cnt <= buf_cnt - 1;  // 只读
            2'b10: buf_cnt <= buf_cnt + 1;  // 只写
            default: buf_cnt <= buf_cnt;    // 无操作或同时读写
        endcase
    end
end

// 满空状态输出
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        buf_full <= 0;
        buf_empty <= 1;
        backpressure <= 0;
    end else begin
        buf_full <= (buf_cnt == BUF_DEPTH);
        buf_empty <= (buf_cnt == 0);
        backpressure <= (buf_cnt == BUF_DEPTH);  // 背压=缓冲满
    end
end
4. 背压控制:避免数据溢出

背压信号 s_axis_tready直接与buf_full联动------缓冲未满则允许上游写入,满则拒绝,从根本上避免数据溢出:

verilog 复制代码
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        s_axis_tready <= 1'b0;
    end else begin
        s_axis_tready <= !buf_full;  // 缓冲未满则就绪
    end
end

四、完整模块代码(可直接复用)

verilog 复制代码
// RDMA RC 数据缓冲模块(适配flow_ctrl/pdu_parser/QP模块)
// 核心功能:AXI-Stream数据缓存 + 背压输出 + 流量控制暂停联动 + 满/空检测
module rdma_rc_buf #(
    parameter DATA_WIDTH    = 64,    // 数据位宽(与PDU解析模块一致)
    parameter BUF_DEPTH     = 16,    // 缓冲深度(2的幂次)
    parameter ADDR_WIDTH    = $clog2(BUF_DEPTH) // 地址位宽自动计算
)(
    input  wire                     clk,                // 100MHz系统时钟
    input  wire                     rst_n,              // 异步低复位
    input  wire                     send_pause,         // 发送暂停(来自flow_ctrl)
    
    // AXI-Stream接收接口(上游:PDU解析模块)
    input  wire [DATA_WIDTH-1:0]    s_axis_tdata,
    input  wire                     s_axis_tvalid,
    input  wire                     s_axis_tlast,
    output reg                      s_axis_tready,
    
    // AXI-Stream发送接口(下游:传输模块)
    output reg [DATA_WIDTH-1:0]     m_axis_tdata,
    output reg                      m_axis_tvalid,
    output reg                      m_axis_tlast,
    input  wire                     m_axis_tready,
    
    // 状态输出
    output reg                      buf_full,
    output reg                      buf_empty,
    output reg                      backpressure
);

// 内部存储与控制信号
reg [DATA_WIDTH-1:0]  buf_mem[0:BUF_DEPTH-1]; // 缓冲存储单元
reg [ADDR_WIDTH-1:0]  wr_addr;                 // 写地址计数器
reg [ADDR_WIDTH-1:0]  rd_addr;                 // 读地址计数器
reg [ADDR_WIDTH:0]    buf_cnt;                 // 缓冲数据计数
reg                   tlast_r;                 // 帧尾锁存寄存器

// 1. 写入控制逻辑
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        wr_addr <= {ADDR_WIDTH{1'b0}};
        tlast_r <= 1'b0;
    end else if (s_axis_tvalid && s_axis_tready) begin
        buf_mem[wr_addr] <= s_axis_tdata;
        tlast_r <= s_axis_tlast;
        wr_addr <= (wr_addr == BUF_DEPTH - 1) ? {ADDR_WIDTH{1'b0}} : wr_addr + 1'b1;
    end
end

// 2. 读取控制逻辑
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        rd_addr <= {ADDR_WIDTH{1'b0}};
        m_axis_tdata <= {DATA_WIDTH{1'b0}};
        m_axis_tvalid <= 1'b0;
        m_axis_tlast <= 1'b0;
    end else if (!send_pause && !buf_empty) begin
        if (m_axis_tready || !m_axis_tvalid) begin
            m_axis_tdata <= buf_mem[rd_addr];
            m_axis_tvalid <= 1'b1;
            m_axis_tlast <= tlast_r;
            rd_addr <= (rd_addr == BUF_DEPTH - 1) ? {ADDR_WIDTH{1'b0}} : rd_addr + 1'b1;
            tlast_r <= (tlast_r) ? 1'b0 : tlast_r;
        end
    end else begin
        m_axis_tdata <= {DATA_WIDTH{1'b0}};
        m_axis_tvalid <= 1'b0;
        m_axis_tlast <= 1'b0;
    end
end

// 3. 缓冲计数与满空检测
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        buf_cnt <= {ADDR_WIDTH+1{1'b0}};
    end else begin
        case ({s_axis_tvalid & s_axis_tready, m_axis_tvalid & m_axis_tready & !send_pause})
            2'b01:  buf_cnt <= buf_cnt - 1'b1;
            2'b10:  buf_cnt <= buf_cnt + 1'b1;
            default: buf_cnt <= buf_cnt;
        endcase
    end
end

always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        buf_full <= 1'b0;
        buf_empty <= 1'b1;
        backpressure <= 1'b0;
    end else begin
        buf_full <= (buf_cnt == BUF_DEPTH);
        buf_empty <= (buf_cnt == {ADDR_WIDTH+1{1'b0}});
        backpressure <= (buf_cnt == BUF_DEPTH);
    end
end

// 4. 背压控制(接收就绪信号)
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        s_axis_tready <= 1'b0;
    end else begin
        s_axis_tready <= !buf_full;
    end
end

endmodule

五、模块验证:确保功能可靠(附测试核心思路)

验证的核心是覆盖"正常场景+边界场景",基于SystemVerilog编写TB,关键测试用例包括:

1. 基础功能验证

  • 复位状态:复位后buf_empty=1s_axis_tready=1,符合初始状态要求;
  • 正常读写:写入8帧数据后buf_cnt=8,读取后buf_cnt=0,数据无错配。

2. 边界场景验证

  • 缓冲满背压:写入16帧数据后buf_full=1s_axis_tready=0,上游无法继续写入;
  • 暂停发送:send_pause=1m_axis_tvalid=0,暂停释放后数据续传无丢失;
  • 整帧传输:tlast信号精准同步,确保1帧数据完整输出。

3. 验证工具与脚本

使用VCS编译,Verdi查看波形,编译脚本参考:

bash 复制代码
vcs -sverilog tb_rdma_rc_buf.sv rdma_rc_buf.v -debug_access+all -fsdb -P $VERDI_HOME/share/PLI/VCS/LINUX64/novas.tab $VERDI_HOME/share/PLI/VCS/LINUX64/pli.a

六、集成注意事项

  1. 位宽对齐DATA_WIDTH必须与PDU解析模块、传输模块保持一致(默认64bit);
  2. 时序同步 :所有模块使用同一clk/rst_n,避免异步交互导致的 metastability;
  3. 参数配置 :根据实际吞吐量需求调整BUF_DEPTH------高频场景可增大至32/64,资源受限场景可减小至8。

七、总结与扩展

本文设计的RDMA RC缓冲模块,通过"参数化、接口标准化、逻辑精准化"实现了三大核心价值:

  1. 兼容性:无缝对接QP/flow_ctrl/PDU模块,无需额外适配逻辑;
  2. 可靠性:背压+满空检测+帧尾锁存,从根本上避免数据丢失与溢出;
  3. 可扩展性:支持位宽、深度配置,适配不同FPGA/IC芯片资源。

扩展方向:

  • 动态深度:通过配置寄存器实现BUF_DEPTH动态调整;
  • 多通道扩展:增加通道选择信号,实现多QP共享缓冲;
  • 错误检测:新增奇偶校验位,检测数据传输错误。

如果在模块使用或验证中遇到问题,欢迎在评论区交流,也可结合具体场景进一步优化设计细节~

相关推荐
可可苏饼干2 小时前
NoSQL 与 Redis
数据库·redis·笔记·学习·nosql
源码方舟2 小时前
【GitHub和Gitee两大平台对比分析】
gitee·github
重生之我在番茄自学网安拯救世界2 小时前
网络安全中级阶段学习笔记(一):DVWA靶场安装配置教程与网络空间搜索语法
笔记·学习·网络安全·靶场·dvwa·fofa·google hack
摇滚侠2 小时前
零基础小白自学 Git_Github 教程,Git 命令行操作2,笔记19
笔记·git·github
TL滕2 小时前
从0开始学算法——第五天(初级排序算法)
数据结构·笔记·学习·算法·排序算法
NocoBase2 小时前
社区用户分享:用 NocoBase 搭建可落地的 ERP
低代码·开源·github
走在路上的菜鸟2 小时前
Android学Dart学习笔记第十节 循环
android·笔记·学习·flutter
Xudde.3 小时前
friendly靶机渗透
笔记·学习·安全·web安全·php
轻赚时代3 小时前
PC 端 AI 图像处理工具实操指南:抠图 / 证件照优化 / 智能擦除全流程解析
图像处理·人工智能·经验分享·笔记·深度学习·创业创新·学习方法