树形架构破解FPGA扇出瓶颈

通过构建基于 AXI Stream 协议的树形数据流切换架构(包含 N 选 1 仲裁器和 1 分 N 分发器),利用多级流水线寄存器打拍技术,将原本集中式的大扇出(Fanout)信号分解为多层级的小扇出网络,从而显著降低布线拥塞并提升 FPGA 最高工作频率。

核心原理与优势分析

树形结构通过将大规模的多路选择或分发任务分解为多个小规模的子任务(如 2 选 1 或 N 选 1),并在每一级逻辑之间插入寄存器(Pipeline Registers),实现了物理层面的信号隔离与时序优化。

优化维度 传统扁平化设计痛点 树形结构 + 寄存器打拍方案优势
扇出 (Fanout) 单个控制信号需驱动大量负载,导致布线延迟大,成为时序瓶颈 。 将大扇出拆解为多级小扇出,每级仅驱动少量下级模块,大幅降低单节点负载 。
工作频率 长组合逻辑路径和高压摆率限制导致 F_{max} 难以提升。 寄存器打拍切断了长组合逻辑链,将关键路径限制在局部小树节点内,显著提升 F_{max} 。
地址/通道管理 业务模块需自行维护复杂的地址映射和路由逻辑。 切换模块自动根据控制位(CTL)重写通道 ID,业务模块无需感知路由细节 。
扩展性 增加输入/输出端口需重新设计整个多路选择器。 支持参数化递归实例化,可灵活构建任意规模(N路)的树形网络 。

关键技术实现:N 选 1 仲裁器 (Tree Arbiter)

该模块采用递归生成(Generate-Recursion)方式构建。当输入端口数大于基数(如 N=2)时,先并行实例化多个底层仲裁器,再将它们的输出作为输入传递给上一级仲裁器,形成树状拓扑。关键步骤包含通道 ID 的自动注入与流水线寄存。

systemverilog 复制代码
// 树形 N 选 1 AXI Stream 仲裁器
// 功能:将 NUM_IN 路输入汇聚为 1 路输出,通过树形结构降低扇出
module tree_packet_arb_n_to_1 # (
  parameter DAT_BYTS = 8,
  parameter DAT_BITS = DAT_BYTS*8,
  parameter CTL_BITS = 8,
  parameter NUM_IN = 8,          // 总输入路数
 parameter LOG2_NUM_IN = NUM_IN == 1 ? 1 : $clog2(NUM_IN),
  parameter OVR_WRT_BIT = CTL_BITS - LOG2_NUM_IN, // 控制位中用于覆盖通道 ID 的起始位
  parameter N = 2,               // 树的分支因子 (如 2 叉树)
  parameter MAX = NUM_IN         // 最大路数常量
) (
  input i_clk, i_rst,
  if_axi_stream.sink   i_n_axi [NUM_IN-1:0], // 多路输入接口
 if_axi_stream.source o_axi                // 单路输出接口
);

localparam MOD_BITS = $clog2(DAT_BYTS);
localparam NUM_IN_GRP = (NUM_IN+N-1)/N; // 计算本级需要的子模块数量

generate
  genvar g, h;
  
  // 定义内部互联接口
  if_axi_stream #(.DAT_BYTS(DAT_BYTS), .DAT_BITS(DAT_BITS), .CTL_BITS(CTL_BITS)) o_axi_int [NUM_IN_GRP-1:0] (i_clk);
    
  for (g = 0; g < NUM_IN_GRP; g++) begin: GEN_TREE_LEVEL
    // 计算当前组实际有效的输入数量 localparam NUM_IN_INT = (g+1)*N <= NUM_IN ? N : (NUM_IN % N);
    
    if_axi_stream #(.DAT_BYTS(DAT_BYTS), .DAT_BITS(DAT_BITS), .CTL_BITS(CTL_BITS)) i_axi_int [NUM_IN_INT-1:0] (i_clk);
    
    // 1. 输入连接与通道 ID 注入
    for (h = 0; h < NUM_IN_INT; h++) begin
      always_comb begin // 复制数据流信号
        i_axi_int[h].copy_if_comb(i_n_axi[g*N + h].dat, i_n_axi[g*N + h].val, i_n_axi[g*N + h].sop, 
                                  i_n_axi[g*N + h].eop, i_n_axi[g*N + h].err, i_n_axi[g*N + h].mod, 
                                  i_n_axi[g*N + h].ctl);
        // 反压传递
 i_n_axi[g*N + h].rdy = i_axi_int[h].rdy;
        
        // 关键优化:在入口处自动重写控制位,标记数据来源通道 ID
        if (MAX == NUM_IN)
          i_axi_int[h].ctl[OVR_WRT_BIT +: LOG2_NUM_IN] = g*N + h;
      end
    end
    
    // 2. 实例化底层仲裁器 (带流水线寄存器 PIPELINE=1)
    packet_arb_bp # (
      .DAT_BYTS     ( DAT_BYTS    ),
      .DAT_BITS     ( DAT_BITS    ),
      .CTL_BITS     ( CTL_BITS    ),
      .NUM_IN       ( NUM_IN_INT  ),
      .OVR_WRT_BIT  ( OVR_WRT_BIT ),
      .PIPELINE     ( 1           ), // 启用寄存器打拍,切断组合逻辑,提高频率
      .PRIORITY_IN  ( 0           ),
      .OVERRIDE_CTL ( 0           )
    )
    packet_arb_bp_inst (
      .i_clk ( i_clk ), 
      .i_rst ( i_rst ),
      .i_axi ( i_axi_int    ), 
      .o_axi ( o_axi_int[g] )
    );
  end
    
  // 3. 递归构建上层树或输出最终结果
  if (NUM_IN_GRP > 1) begin
    // 若子模块输出多于 1 路,递归调用自身构建上一级树
    tree_packet_arb_n_to_1 # (
      .DAT_BYTS     ( DAT_BYTS    ),
      .DAT_BITS     ( DAT_BITS    ),
      .CTL_BITS     ( CTL_BITS    ),
      .NUM_IN       ( NUM_IN_GRP  ),
      .OVR_WRT_BIT  ( OVR_WRT_BIT ),
      .N            ( N           ),
      .MAX          ( MAX         )
    ) 
    tree_upper_level (
      .i_clk ( i_clk ), 
      .i_rst ( i_rst ),
      .i_n_axi ( o_axi_int ), 
      .o_axi ( o_axi )
    );
  end else begin
    // 若仅剩一路,直接连通
    always_comb begin
      o_axi.copy_if_comb(o_axi_int[0].dat, o_axi_int[0].val, o_axi_int[0].sop, o_axi_int[0].eop, 
                         o_axi_int[0].err, o_axi_int[0].mod, o_axi_int[0].ctl);
      o_axi_int[0].rdy = o_axi.rdy;
    end
  end
    
endgenerate

endmodule

关键技术实现:1 分 N 分发器 (Tree Distributor)

分发器同样采用树形结构,依据数据包控制位(CTL)中的通道 ID 将数据路由至指定输出端口。通过在树的中继节点插入流水线寄存器(pipeline_bp_if),确保高速时钟下信号能稳定传输至叶子节点。

systemverilog 复制代码
// 树形 1 分 N AXI Stream 分发器
// 功能:将 1 路输入根据 CTL 位分发至 NUM_OUT 路输出,利用树形结构降低扇出
module tree_packet_arb_1_to_n # (
  parameter DAT_BYTS = 8,
  parameter DAT_BITS = DAT_BYTS*8,
  parameter CTL_BITS = 8,
  parameter NUM_OUT = 8,
  parameter LOG2_NUM_OUT = NUM_OUT == 1 ? 1 : $clog2(NUM_OUT),
  parameter OVR_WRT_BIT = CTL_BITS - LOG2_NUM_OUT,
  parameter N = 2,               // 树分支因子
  parameter MAX = NUM_OUT
) (
  input i_clk, i_rst,
  if_axi_stream.sink   i_axi,                  // 单路输入
  if_axi_stream.source o_n_axi[NUM_OUT-1:0]    // 多路输出
);

localparam LOG2_MAX = MAX == 1 ? 1 : $clog2(MAX);
localparam NUM_OUT_GRP = (NUM_OUT+N-1)/N;

generate
  genvar g, h;
  
  // 情况 A: 输出数量较少,直接进行逻辑判断分发
 if (NUM_OUT_GRP == 1) begin: FIRST_STAGE_GEN
    logic [NUM_OUT-1:0] rdy_i; 
    always_comb i_axi.rdy = |rdy_i; // 只要有一个输出准备好,输入即可发送
    
    for (h = 0; h < NUM_OUT; h++) begin: FINAL_GEN
      logic in_range_i;
      // 根据 CTL 位计算目标通道索引
      always_comb rdy_i[h] = o_n_axi[h].rdy && in_range_i;
      always_comb in_range_i = i_axi.ctl[OVR_WRT_BIT +: LOG2_MAX] / (MAX/NUM_OUT) == h;
      
      always_comb begin
        // 仅当匹配当前通道时拉高 valid 信号
        o_n_axi[h].copy_if_comb(i_axi.dat, i_axi.val && in_range_i, i_axi.sop, i_axi.eop, 
                                i_axi.err, i_axi.mod, i_axi.ctl);
      end
    end
        
  end else begin
    // 情况 B: 输出数量多,构建多级树形结构
  
    if_axi_stream #(.DAT_BYTS(DAT_BYTS), .DAT_BITS(DAT_BITS), .CTL_BITS(CTL_BITS)) i_pipe [NUM_OUT_GRP-1:0] (i_clk);
  
    // 1. 递归调用自身,先将数据分发到各个子树组
    tree_packet_arb_1_to_n # (
      .DAT_BYTS     ( DAT_BYTS    ),
      .DAT_BITS     ( DAT_BITS    ),
      .CTL_BITS     ( CTL_BITS    ),
      .NUM_OUT      ( NUM_OUT_GRP ),
      .OVR_WRT_BIT  ( OVR_WRT_BIT ),
      .N            ( N           ),
      .MAX          ( MAX         )
    ) 
    tree_upper_dist (
      .i_clk ( i_clk ), 
      .i_rst ( i_rst ),
      .i_axi ( i_axi ), 
      .o_n_axi ( i_pipe )
    );  

    // 2. 对每个子树组的输入进行流水线打拍,并分发到具体端口
    for (g = 0; g < NUM_OUT_GRP; g++) begin: GEN_TREE_LEVEL
        localparam NUM_OUT_INT = N;
        if_axi_stream #(.DAT_BYTS(DAT_BYTS), .DAT_BITS(DAT_BITS), .CTL_BITS(CTL_BITS)) o_pipe (i_clk);
        logic [NUM_OUT_INT-1:0] rdy_o;
        
        always_comb o_pipe.rdy = |rdy_o;
        
        // 关键优化:插入流水线级,降低长路径延迟,提高频率
        pipeline_bp_if  #(
          .DAT_BITS   ( DAT_BITS ),
          .CTL_BITS   ( CTL_BITS ),
          .NUM_STAGES ( 1        ) // 插入1 级寄存器
        )
        pipeline_stage (
          .i_rst ( i_rst  ),
          .i_clk ( i_clk  ),
          .i_if  ( i_pipe[g] ),
          .o_if  ( o_pipe )
        );
        
        for (h = 0; h < NUM_OUT_INT; h++) begin: FINAL_LEAF_GEN
          logic in_range_o;
          // 计算叶子节点的目标范围
          always_comb in_range_o = o_pipe.ctl[OVR_WRT_BIT +: LOG2_MAX] / (MAX/NUM_OUT) == g*N + h;
          
          always_comb begin
            rdy_o[h] = o_n_axi[g*N + h].rdy && in_range_o;
            o_n_axi[g*N + h].copy_if_comb(o_pipe.dat, o_pipe.val && in_range_o, o_pipe.sop, 
                                          o_pipe.eop, o_pipe.err, o_pipe.mod, o_pipe.ctl);
          end
        end
    end
  endendgenerate

endmodule

实施要点总结

  1. 寄存器打拍(Pipeline Register) :在 packet_arb_bp 和 pipeline_bp_if 模块中显式启用流水线模式(PIPELINE=1 或 NUM_STAGES=1)。这是降低 Fanout 和提升频率的核心,它将原本跨越整个多路选择器的组合逻辑路径切割为多个短路径 。
  2. 自动通道管理 :利用 CTL 总线的高位动态写入通道 ID(i_axi_int[h].ctl[...] = g*N + h),使得下游模块无需硬编码地址逻辑,只需解析数据包自带的控制字即可识别来源或去向,简化了顶层逻辑 。
  3. 参数化递归 :通过 NUM_IN_GRP 和递归实例化,代码可自动适应任意规模的端口数,无需手动修改层级结构,保证了设计的通用性和可维护性 。

参考来源

相关推荐
FPGA小迷弟5 小时前
FPGA开发Vivado Block Design/Vitis/SDK/xlinx/开发教程,保姆级别,一看就会!
网络协议·tcp/ip·fpga开发·verilog·fpga
FPGA小迷弟5 小时前
基于FPGA实现智能网卡的概念,什么是智能网卡
网络协议·tcp/ip·fpga开发·verilog·fpga
好焊孙辉7 小时前
螺柱焊机哪家质量好
python·fpga开发
XMAIPC_Robot12 小时前
基于 i.MX95 平台在新一代工业控制器中的应用 — 工业车载平板、半导体设备主控方案解析
arm开发·人工智能·机器学习·fpga开发·rk3588+fpga·i.mx95
szxinmai主板定制专家12 小时前
基于 AM62x + Artix-7 的低功耗 ARM+FPGA 平台在轨道交通车门控制中的应用
arm开发·人工智能·fpga开发·zynq·am62x
幻智星科技12 小时前
我砍掉了FPGA里的ARM核项目反而提前交付了
arm开发·网络协议·fpga开发·硬件架构
逐光者93313 小时前
Verilog 三段式状态机:原理、模板与实战
fpga开发
XMAIPC_Robot13 小时前
NXP i.MX95 + FPGA 异构计算平台 — 工业多域融合 AI 加速嵌入式控制解决方案
人工智能·分布式·fpga开发·能源·rk3576+fpga
FPGA小迷弟1 天前
用 Claude Code 重构遗留系统:从评估到落地的完整指南
fpga开发·dsp开发·fpga·1024程序员节·sdr·无线电