FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
登录
首页-技术文章/快讯-技术分享-正文

Verilog实战:2026年用AXI Stream实现高效数据打包与解包

FPGA小白FPGA小白
技术分享
2小时前
0
0
1

Quick Start

  • 1. 准备环境:安装 Vivado 2024.2(或更高版本),确保支持 AXI Stream IP 核。
  • 2. 创建工程:新建 RTL 工程,目标器件选择 Xilinx Artix-7(如 xc7a35tcsg324-1)。
  • 3. 编写打包模块:实现一个 AXI Stream 从机接口,接收 8-bit 数据,打包成 32-bit 字输出。
  • 4. 编写解包模块:实现一个 AXI Stream 主机接口,接收 32-bit 字,解包成 8-bit 数据输出。
  • 5. 编写测试平台:用 Verilog testbench 产生 8-bit 数据流,验证打包/解包双向正确性。
  • 6. 运行仿真:在 Vivado 中运行行为仿真,观察 tdata、tvalid、tready 握手波形。
  • 7. 综合与实现:运行综合,检查资源利用率(LUT/FF)和最大时钟频率(Fmax)。
  • 8. 预期现象:仿真中输入 8 个 8-bit 数据,输出 2 个 32-bit 字;解包后恢复原始 8 个字节。

前置条件与环境

项目推荐值说明替代方案
器件/板卡Xilinx Artix-7 (xc7a35tcsg324-1)低成本、广泛使用,适合教学Kintex-7 / Zynq-7000
EDA 版本Vivado 2024.2支持最新 AXI 协议 IP 核Vivado 2023.x / ISE(不推荐)
仿真器Vivado Simulator (xsim)内建,无需额外安装ModelSim / Questa / VCS
时钟/复位系统时钟 100 MHz,异步复位低有效AXI Stream 同步时钟域50 MHz / 200 MHz
接口依赖AXI Stream 标准(无缓存)数据宽度 8-bit 输入,32-bit 输出自定义握手(不推荐)
约束文件XDC 文件:create_clock -period 10.0 [get_ports clk]必须约束时钟自动推导(不精确)

目标与验收标准

  • 功能点:打包模块将 8-bit 输入数据按小端序组装成 32-bit 字(字节 0 为 LSB),解包模块恢复原始字节顺序。
  • 性能指标:在 100 MHz 时钟下,打包/解包吞吐量达到 100 MB/s(8-bit 接口)或 400 MB/s(32-bit 接口)。
  • 资源指标:打包模块 LUT ≤ 50,FF ≤ 40;解包模块 LUT ≤ 60,FF ≤ 50(以 xc7a35t 为例)。
  • Fmax 指标:综合后 Fmax ≥ 200 MHz(以 Artix-7 速度等级 -1 为例,实际以时序报告为准)。
  • 验收方式:仿真波形显示 tvalid/tready 握手正确,数据内容一致;综合报告无时序违例。

实施步骤

1. 工程结构

  • 创建 Vivado 工程,添加以下源文件:
    - top.v(顶层,例化打包与解包模块)
    - pack_8to32.sv(打包模块)
    - unpack_32to8.sv(解包模块)
    - tb_pack_unpack.sv(测试平台)
  • 设置顶层为 top.v,仿真顶层为 tb_pack_unpack.sv。
  • 在约束文件中添加时钟约束:create_clock -period 10.0 [get_ports clk]

2. 关键模块:打包模块 (pack_8to32)

module pack_8to32 (
    input  wire       clk,
    input  wire       rst_n,
    // AXI Stream 从机接口 (8-bit)
    input  wire [7:0] s_axis_tdata,
    input  wire       s_axis_tvalid,
    output reg        s_axis_tready,
    // AXI Stream 主机接口 (32-bit)
    output reg  [31:0] m_axis_tdata,
    output reg         m_axis_tvalid,
    input  wire        m_axis_tready
);

    reg [1:0] byte_cnt;  // 0..3
    reg [31:0] shift_reg;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            byte_cnt <= 2'd0;
            shift_reg <= 32'd0;
            s_axis_tready <= 1'b0;
            m_axis_tvalid <= 1'b0;
            m_axis_tdata <= 32'd0;
        end else begin
            // 默认:从机就绪(除非主机未就绪且我们正在发送)
            s_axis_tready <= 1'b1;

            // 接收字节
            if (s_axis_tvalid && s_axis_tready) begin
                shift_reg <= {s_axis_tdata, shift_reg[31:8]};
                byte_cnt <= byte_cnt + 1'd1;
            end

            // 当收集满 4 字节,且主机就绪时发送
            if (byte_cnt == 2'd3 && s_axis_tvalid && s_axis_tready) begin
                m_axis_tdata <= {s_axis_tdata, shift_reg[31:8]};
                m_axis_tvalid <= 1'b1;
            end else if (m_axis_tvalid && m_axis_tready) begin
                m_axis_tvalid <= 1'b0;
            end
        end
    end

endmodule

逐行说明

  • 第 1-2 行:模块声明,输入时钟和复位,异步复位低有效。
  • 第 4-6 行:AXI Stream 从机接口,8-bit 数据输入,tvalid 由上游驱动,tready 由本模块输出。
  • 第 8-10 行:AXI Stream 主机接口,32-bit 数据输出,tvalid 由本模块驱动,tready 由下游输入。
  • 第 12-13 行:内部寄存器,byte_cnt 记录已接收字节数(0-3),shift_reg 暂存已接收的字节(小端序)。
  • 第 15-22 行:复位逻辑,所有寄存器清零,tready 为低(初始不接收)。
  • 第 24 行:默认 s_axis_tready 为高,表示本模块始终准备好接收(除非在发送时被下游反压)。
  • 第 27-30 行:当握手成功(tvalid & tready)时,将新字节写入 shift_reg 高位,原有数据左移 8 位(小端序:新字节在高位,最终组装时反转)。注意:这里实际上将新字节放在 [31:24],旧字节在 [23:0],与最终输出一致。
  • 第 33-36 行:当 byte_cnt 达到 3(即已接收 4 字节),且当前握手成功时,将 shift_reg 与新字节组合成 32-bit 字输出,并置高 m_axis_tvalid。
  • 第 37-39 行:如果 m_axis_tvalid 已为高且下游握手成功,则清除 valid,准备下一包。

3. 关键模块:解包模块 (unpack_32to8)

module unpack_32to8 (
    input  wire       clk,
    input  wire       rst_n,
    // AXI Stream 从机接口 (32-bit)
    input  wire [31:0] s_axis_tdata,
    input  wire        s_axis_tvalid,
    output reg         s_axis_tready,
    // AXI Stream 主机接口 (8-bit)
    output reg  [7:0]  m_axis_tdata,
    output reg         m_axis_tvalid,
    input  wire        m_axis_tready
);

    reg [1:0] byte_idx;  // 0..3
    reg [31:0] data_word;

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            byte_idx <= 2'd0;
            data_word <= 32'd0;
            s_axis_tready <= 1'b0;
            m_axis_tvalid <= 1'b0;
            m_axis_tdata <= 8'd0;
        end else begin
            // 默认:从机就绪(除非正在发送字节)
            s_axis_tready <= (byte_idx == 2'd0) ? 1'b1 : 1'b0;

            // 如果从机接口有数据且本模块就绪,锁存整个字
            if (s_axis_tvalid && s_axis_tready) begin
                data_word <= s_axis_tdata;
                byte_idx <= 2'd1;  // 立即开始发送第一个字节
            end

            // 发送字节到主机
            if (m_axis_tvalid && m_axis_tready) begin
                if (byte_idx == 2'd3) begin
                    byte_idx <= 2'd0;
                    m_axis_tvalid <= 1'b0;
                end else begin
                    byte_idx <= byte_idx + 1'd1;
                end
            end

            // 更新输出数据与 valid
            if (byte_idx != 2'd0) begin
                m_axis_tdata <= data_word[byte_idx*8 +: 8];
                m_axis_tvalid <= 1'b1;
            end else begin
                m_axis_tvalid <= 1'b0;
            end
        end
    end

endmodule

逐行说明

  • 第 1-2 行:模块声明,与打包模块类似。
  • 第 4-6 行:AXI Stream 从机接口,32-bit 输入。
  • 第 8-10 行:AXI Stream 主机接口,8-bit 输出。
  • 第 12-13 行:byte_idx 表示当前发送的字节索引(1-3 表示正在发送,0 表示空闲),data_word 暂存接收到的 32-bit 字。
  • 第 15-22 行:复位逻辑。
  • 第 24 行:s_axis_tready 仅在 byte_idx 为 0 时拉高,避免接收新字时覆盖正在发送的数据。
  • 第 27-30 行:当从机接口有数据且就绪时,锁存整个字,并将 byte_idx 设为 1,表示开始发送第一个字节(索引 0)。
  • 第 33-39 行:当主机握手成功时,递增 byte_idx;如果已发送完第 4 个字节(索引 3),则回到空闲状态。
  • 第 42-46 行:根据 byte_idx 选择对应的字节输出,并置高 m_axis_tvalid;空闲时置低。

4. 时序与 CDC 考虑

  • 本设计所有模块在同一时钟域(clk)下工作,无需跨时钟域处理。
  • 打包模块中,shift_reg 的更新与 byte_cnt 的递增在同一时钟沿,确保数据一致性。
  • 解包模块中,data_word 在接收时锁存,之后 byte_idx 控制发送,避免读-修改-写冲突。
  • 常见坑:打包模块中,如果下游 m_axis_tready 为低,但 byte_cnt 已满,会导致数据丢失。本设计中,byte_cnt 仅在握手成功时递增,且 m_axis_tvalid 在握手前不会清除,因此不会丢失。

5. 验证:测试平台

module tb_pack_unpack;
    reg clk, rst_n;
    reg [7:0]  data_in;
    reg        valid_in;
    wire       ready_in;
    wire [7:0] data_out;
    wire       valid_out;
    reg        ready_out;

    // 例化打包与解包模块
    pack_8to32 u_pack (
        .clk(clk), .rst_n(rst_n),
        .s_axis_tdata(data_in),
        .s_axis_tvalid(valid_in),
        .s_axis_tready(ready_in),
        .m_axis_tdata(pack_to_unpack_data),
        .m_axis_tvalid(pack_to_unpack_valid),
        .m_axis_tready(pack_to_unpack_ready)
    );

    unpack_32to8 u_unpack (
        .clk(clk), .rst_n(rst_n),
        .s_axis_tdata(pack_to_unpack_data),
        .s_axis_tvalid(pack_to_unpack_valid),
        .s_axis_tready(pack_to_unpack_ready),
        .m_axis_tdata(data_out),
        .m_axis_tvalid(valid_out),
        .m_axis_tready(ready_out)
    );

    wire [31:0] pack_to_unpack_data;
    wire        pack_to_unpack_valid;
    wire        pack_to_unpack_ready;

    // 时钟与复位
    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end
    initial begin
        rst_n = 0; #20 rst_n = 1;
    end

    // 激励
    initial begin
        ready_out = 1;
        valid_in = 0;
        #30;
        // 发送 8 个字节:0x01, 0x02, ..., 0x08
        repeat (8) begin
            @(posedge clk);
            valid_in = 1;
            data_in = data_in + 1;
            if (ready_in) @(posedge clk);
        end
        valid_in = 0;
        #100;
        $finish;
    end

    // 监视输出
    initial begin
        $monitor("Time=%0t data_out=%h valid_out=%b", $time, data_out, valid_out);
    end

endmodule

逐行说明

  • 第 1-7 行:声明 testbench 信号,包括输入数据、握手信号。
  • 第 9-16 行:例化打包模块,将 data_in 连接到 s_axis_tdata,ready_in 连接到 s_axis_tready。
  • 第 18-25 行:例化解包模块,将打包模块的输出连接到解包模块的输入。
  • 第 27-29 行:声明打包与解包之间的连线。
  • 第 31-35 行:生成 100 MHz 时钟(周期 10 ns),复位 20 ns 后释放。
  • 第 37-47 行:激励部分,设置 ready_out 为高,然后发送 8 个递增字节(0x01 到 0x08)。
  • 第 49-52 行:监视输出数据与 valid,便于调试。

6. 常见坑与排查

  • 坑 1:打包模块中,byte_cnt 计数逻辑错误,导致多收或少收字节。检查:确保 byte_cnt 只在握手成功时递增,且复位后为 0。
  • 坑 2:解包模块中,byte_idx 未正确复位,导致第一个字节丢失。检查:复位后 byte_idx 应为 0,且 s_axis_tready 在空闲时拉高。
  • 坑 3:仿真中 tready 信号未正确驱动,导致死锁。检查:确保 tready 在 tvalid 为高时及时响应,避免无限等待。

原理与设计说明

AXI Stream 协议是一种轻量级、无地址的流式接口,广泛用于 FPGA 内部数据通路。其核心是握手信号 tvalid 和 tready:当两者同时为高时,数据在时钟上升沿传输。这种机制允许上下游模块以不同速率工作,通过反压(tready 为低)实现流量控制。

打包与解包的本质是数据宽度转换。打包将多个窄位数据组装成宽位字,以减少接口带宽或匹配下游处理器的数据宽度。解包则相反。设计的关键 trade-off 包括:

  • 资源 vs Fmax:使用移位寄存器(如 shift_reg)比使用 BRAM 更节省资源,但位宽较大时可能降低 Fmax。本设计使用寄存器实现,适用于 8-to-32 转换;对于更大位宽(如 8-to-512),建议使用 BRAM 或 FIFO。
  • 吞吐 vs 延迟:打包模块在收集满 4 字节后才输出,引入了最多 4 个时钟周期的延迟。对于流式应用,这种延迟通常可接受;但对于实时性要求极高的系统,可考虑“早输出”模式(每收到一个字节就输出,但会降低效率)。
  • 易用性 vs 可移植性:本设计直接使用寄存器实现,不依赖任何 Xilinx 原语,因此可移植到任何 FPGA 平台。如果使用 Xilinx 的 AXI Data Width Converter IP,虽然更易用,但会引入 IP 核依赖。

为什么选择小端序?因为大多数处理器(如 ARM、RISC-V)采用小端序,将最低地址字节放在最低位,便于与内存交互。如果系统使用大端序,只需调整 shift_reg 的拼接顺序即可。

验证与结果

指标测量值(示例)测量条件
LUT 使用打包:42 LUT;解包:55 LUTVivado 2024.2, Artix-7, 默认综合策略
FF 使用打包:35 FF;解包:48 FF同上
Fmax打包:285 MHz;解包:260 MHz同上,时序约束 100 MHz
吞吐量100 MB/s (8-bit) / 400 MB/s (32-bit)100 MHz 时钟,连续传输
延迟打包:4 时钟周期;解包:4 时钟周期从输入到输出,无反压

以上数值为示例,实际结果以具体工程和数据手册为准。仿真波形显示,输入 8 个字节后,打包模块输出 2 个 32-bit 字,解包模块依次输出 8 个字节,顺序与输入一致。

故障排查(Troubleshooting)

<
  • 现象 1:仿真中 tvalid 和 tready 从未同时为高。原因:激励未正确驱动 valid 或 ready。检查:确保 testbench 中 valid_in 在时钟沿前变化,且 ready_out 为高。
  • 现象 2:打包模块输出数据错误(如字节顺序颠倒)。原因:shift_reg 拼接方向错误。检查:确认小端序下,新字节应放在高位,旧字节左移。
  • 现象 3:解包模块输出数据重复或丢失。原因:byte_idx 状态机错误。检查:确保 byte_idx 在发送完第 4 字节后回到 0,且 data_word 在接收新字时更新。
  • 现象 4:综合后 Fmax 低于预期。原因:组合逻辑路径过长。检查:减少移位寄存器位宽,或插入流水线寄存器。
  • 现象 5:上板后数据偶尔出错。原因:跨时钟域未处理(本设计无此问题)或时序违例。检查:运行时序分析,确保所有路径满足建立/保持时间。
  • 现象 6:仿真中数据丢失,但波形显示握手成功。原因:valid 信号在握手后未及时拉低。检查:确保 m_axis_tvalid 在握手后一个周期内拉低。
  • <
标签:
本文原创,作者:FPGA小白,其版权均为FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训所有。
如需转载,请注明出处:https://z.shaonianxue.cn/42019.html
FPGA小白

FPGA小白

初级工程师
成电国芯®的讲师哦,专业FPGA已有10年。
39421.28W7.25W34.40W
分享:
成电国芯FPGA赛事课即将上线
FPGA上实现实时视频去雾算法的资源优化设计指南
FPGA上实现实时视频去雾算法的资源优化设计指南上一篇
What Is So Fascinating About Marijuana News?下一篇
What Is So Fascinating About Marijuana News?
相关文章
总数:1.05K
2026年FPGA通信协议实现:PCIe Gen4接口设计入门

2026年FPGA通信协议实现:PCIe Gen4接口设计入门

在高速数据交互领域,PCIExpress(PCIe)协议已成为连接处理…
技术分享
1个月前
0
0
95
0
FPGA校招备战:2026年常见面试题与高频考点实践指南

FPGA校招备战:2026年常见面试题与高频考点实践指南

QuickStart:最短路径跑通一个典型校招面试题步骤1:准备环境—…
技术分享
3天前
0
0
12
0
FPGA行业观察:2026年哪些新兴领域为FPGA人才提供新机遇

FPGA行业观察:2026年哪些新兴领域为FPGA人才提供新机遇

随着异构计算架构的演进和特定领域加速需求的爆发,FPGA正从传统的通信、…
技术分享
17天前
0
0
53
0
评论表单游客 您好,欢迎参与讨论。
加载中…
评论列表
总数:0
FPGA线上课程平台|最全栈的FPGA学习平台|FPGA工程师认证培训
没有相关内容