
做交换芯片这行的人迟早会被同一组问题问烦为什么同样标称 12.8T 的片子跑均匀小包能线速一换成混合流量就开始抖为什么有的设计缓存看着比别人大真遇上微突发照样丢包为什么有的芯片端口数翻倍之后单口时延反而涨了一大截这些问题往上追追到最后基本都落在同一块地方——数据通路也就是包从入口进来、被搬来搬去、最后从出口出去的那条物理链路。交换芯片微架构里数据通路的设计决定了这颗片子能跑多快、能扛多狠的突发、能撑多少个端口控制面和管理面反而是相对标准化的东西。这一篇我打算把数据通路拆成四块来讲Crossbar、VOQ、Shared Buffer、Cell Fabric。这四个词不是并列关系它们是一条链上的四个环节——包进来先被切成定长信元Cell Fabric信元存在共享缓存里Shared Buffer缓存之前要有虚拟输出队列做调度隔离VOQ最后靠交换矩阵把信元送到对的出口Crossbar。任何一颗现代交换芯片都至少包含这四件事的某种组合区别只是谁放在片内、谁放在片外、谁用多级级联。这篇适合谁看如果你在做网络芯片前端设计、验证、架构评估或者在做交换机系统、网卡、DPU 相关的工作需要跟芯片团队对话那这些概念你绕不过去。如果你只是好奇一块交换芯片里面到底在发生什么我也尽量用生活化的类比把它讲明白——毕竟这行最难的不是概念本身而是概念之间的取舍。1. 交换芯片里那条真正跑数据的路1.1 从一台交换机的丢包说起我遇到过最典型的一个案例是一台标称 64×100G 的盒式交换机跑 RFC2544 的均匀流量测试完全没问题64 字节小包也能打到线速。但换成一个真实的 DC 混合流量模型大概 60% 的流是 1KB 以上剩下是 64~256 字节的小控制流还有少量组播某些端口开始出现零星丢包而且丢包位置不固定今天在 3 号口明天在 17 号口。这种问题如果只看芯片手册是查不出来的。手册会告诉你支持 64MB 共享缓存支持动态门限但不会告诉你内部信元长度是多少、共享缓存的 bank 是怎么分的、VOQ 的保底门限是怎么算的。真正卡住性能的往往是某个不起眼的实现细节比如信元切得太小导致内部头开销占比过高比如共享缓存的读带宽在满配端口时刚好卡在临界点比如某个 VOQ 的保底门限设得太低导致小流被大流饿死。数据通路之所以值得单独拿出来讲就是因为它的性能不是靠单点堆料堆出来的而是靠各个环节的参数咬合出来的。1.2 数据通路与控制通路的分工边界很多人第一次接触交换芯片架构图时会看到一大堆模块Parser、Lookup、ACL、Meter、Counter、Replication、Scheduler、Fabric……很容易以为这些都是数据通路。其实要划一条线凡是需要访问外部存储或者需要跑软件的基本属于控制/管理面凡是每个包都必须逐字节走过、且要在固定流水级数内完成的才是数据通路。Parser、Lookup 严格说是转发面它们决定这个包去哪但一旦决定完了包本体就交给数据通路去搬。搬的过程有几个硬指标确定性时延每一跳的流水级数是固定的不能因为查表慢就多等几拍否则时延抖动会失控。无阻塞保证入口进来的包只要缓存还有空间就一定能被送到出口不能出现内部死锁。顺序保证同一个流的包除非设计上明确允许乱序否则不能前后颠倒这会直接打断 TCP 的拥塞控制。这三条约束叠加起来就逼出了后面所有的设计选择。Crossbar 之所以要配调度器就是为了在无阻塞和确定性之间找平衡VOQ 之所以存在就是为了在保证利用率的同时不破坏流内顺序Shared Buffer 之所以成为主流是因为它在面积和突发吸收能力上找到了一个很好的中间点Cell Fabric 之所以要把包切片是因为定长的东西才好做硬件流水线。1.3 四个关键词串起来的完整图景用一个类比把它串起来把交换芯片想象成一个超大型的快递分拣中心。Cell Fabric是那条把大件货物拆成标准托盘传送带——不管原始包裹多大上了传送带都是统一尺寸的托盘这样传送带的齿轮、挡板、扫描器都能用同一套机构。Shared Buffer是中心里的临时货架区所有托盘都可以先扔进去谁需要谁去取。货架是大家共用的不会出现某个方向分到 10 个货架却只用 2 个、另一个方向 2 个货架爆满的情况。VOQ是每个卸货口前面画出的细分排队区——去 A 城的托盘排一队、去 B 城的排一队不能混在一起排否则前面一个去 A 城的托盘卡住了后面所有去 B 城的托盘全被堵死。Crossbar是那个可以在任意卸货口和任意装货口之间建立通道的分拣机械臂同一时刻它能同时建立多条互不干扰的通道但通道数受限于机械臂的物理数量。这四个东西互相牵制切得越细调度的灵活性越高但内部开销越大货架越大突发吸收越好但读写带宽的压力越大队列分得越细隔离性越好但队列管理和调度的复杂度越高机械臂越多并行度越高但面积和功耗涨得越快。2. Crossbar无阻塞的代价是面积和仲裁2.1 交叉矩阵到底阻塞在哪里Crossbar 的基本模型很朴素N 个输入、N 个输出每个输入到每个输出都有一个交叉点crosspoint总共 N² 个交叉点。只要把一个交叉点打开那个输入就能独占连到那个输出。听起来完美但现实里有两个限制。第一个限制是交叉点数量。8×8 只有 64 个交叉点32×32 就是 1024 个64×64 是 4096 个128×128 已经是 16384 个。每个交叉点不是一根导线而是一组多路选择器加缓冲加驱动面积和功耗是线性叠加的。真到 256×256光交叉点就 65536 个这在单芯片里已经很吃力了。第二个限制是同一时刻的匹配约束。虽然物理上有 N² 个交叉点但一个输入端口在同一时刻只能接一个输出因为它只有一条数据流一个输出端口同一时刻也只能被一个输入接。所以真正需要解决的是一个二分图匹配问题给定 N 个输入和 N 个输出每个输入可以申请多个输出怎么配对才能让最多的输入-输出对同时建立连接。这就引出了 Crossbar 的灵魂——调度器。Crossbar 本身是无内部阻塞的但它不是无输出竞争的。如果两个输入同时想发往同一个输出必然有一个要等。注意很多人把Crossbar 无阻塞理解成接上 Crossbar 就没有竞争这是错的。Crossbar 消除的是内部链路冲突输出端口的竞争依然存在必须靠调度和缓存来化解。2.2 规模上去了就得换 Clos 和 Benes单级 Crossbar 的 N² 增长曲线在 N 超过某个阈值之后就没法接受了。这时候就要上多级结构。最经典的是Clos 网络三层结构第一级有 r 个 n×m 的交换单元中间级有 m 个 r×r 的单元第三级有 r 个 m×n 的单元。它有一个非常漂亮的结论——当中间级数量 m ≥ 2n-1 时网络是严格无阻塞的也就是任何空闲的输入-输出对都能找到一条路径不需要重排已有连接。如果允许重排rearrangeably non-blockingBenes 网络可以做到 m n代价是每次建立新连接可能要动到已有的连接路径需要重新计算路由。Benes 的交叉点数量大约是 N·log₂N 量级比 N² 小得多。实际芯片里的选择通常是小规模≤32 口单级 Crossbar 打天下简单、时延低、一次匹配就完事。中大规模64~256 口三级 Clos 或者升级版的 Clos 变体用空间换确定性。超大规模多芯片级联要么用 Clos 的多芯片扩展要么用 torus/mesh 之类的拓扑做分布式交换。我在做架构评估时一般会算一笔账单级 Crossbar 的面积大致按 N² 增长三级 Clos 按 N^1.5 左右增长取决于 n 和 m 的取值而 Benes 接近 N·log N。但 Clos 和 Benes 都要额外承担调度复杂度、时延增加和路径重排的风险。这个权衡没有标准答案得看你的端口数和时延预算。2.3 匹配算法从 PIM 到 iSLIP调度器要解决的问题是每轮匹配怎么挑出尽量多的输入-输出对。最朴素的是PIMParallel Iterative Matching每个未匹配的输入随机挑一个它想连的未匹配输出如果多个输入挑中同一个输出随机选一个胜出其他输入下一轮重试。PIM 的问题是随机数生成器难做而且收敛慢高负载下需要 15 次以上迭代才能接近满匹配。iSLIPiterative round-robin with SLIP是工程上最常见的方案。它给每个输入和每个输出各维护一个轮询指针输入按指针顺序找第一个未匹配且可用的输出输出也按自己的指针接受请求。匹配成功之后指针更新到匹配位置的下一位。这个算法硬件实现简单只需要移位寄存器和优先级编码器而且在均匀流量下 3~4 次迭代就能达到接近 100% 的吞吐。再往上是DRRMDual Round Robin Matching对 iSLIP 做了一点改动让指针更新更保守一些在某些非均匀流量下表现更稳定。我把这几种算法放在一起对比过算法迭代次数收敛硬件复杂度均匀流量吞吐非均匀流量表现PIM15高需 RNG接近 100%一般iSLIP3~4低接近 100%较好DRRM3~4低接近 100%好基于信用/优先级1~2中取决于权重好但需额外状态实操心得实际芯片里很少只用一种算法通常是优先级 轮询的混合体——高优先级的业务比如控制流、时间敏感流优先匹配同优先级内部用轮询保证公平。这样既照顾了业务差异又避免了严格优先级带来的饿死问题。2.4 物理实现的几个硬约束从 RTL 到硅片Crossbar 还有几道坎。面积和布线拥塞N² 个交叉点意味着每个输入信号要扇出到 N 个位置扇出大、走线长。做 floorplan 的时候Crossbar 经常是整个 die 上布线最密的地方很难收敛。常见的缓解办法是把 Crossbar 按象限切开或者用时分复用TDM的方式让一根物理线在不同时隙传不同目的地的数据把 N² 降到 N·k。时钟和功耗交叉点上的多路选择器和驱动器每周期都在翻转功耗跟翻转率成正比。满载时 Crossbar 的功耗可能占到整颗芯片的 20%~30%。降低手段包括用时钟门控关掉空闲交叉点、用小摆幅信号降低驱动功耗、以及让信元尽量大以减少单位数据的开关次数。串扰和信号完整性高密度平行走线容易耦合。这事在先进工艺下反而更难因为线宽变窄之后线间距也随之变小。一般会在关键路径上加大间距或者插入屏蔽线代价是面积。仲裁时延调度器本身要一拍或几拍才能出结果这段时间数据得先挂着。所以很多设计会把 Crossbar 前面再放一级小的输入缓存几百字节级别专门用来吸收仲裁时延和背压时的短暂堆积。这一级缓存的深度不需要大但必须快通常是寄存器堆或者小 SRAM。3. VOQ输入排队不这么做就废了3.1 HOL 阻塞的量化推导假设一个输入端口只有一条 FIFO 队列所有输出方向的包都排在一条队里。现在队头是一个要去 A 口的包但 A 口正忙这个包走不了。问题来了排在它后面的包哪怕目的地是空闲的 B 口也必须跟着等因为 FIFO 只能从头出。这就是HOLHead-of-Line阻塞。它的危害不是稍微慢一点而是吞吐量的硬上限。经典结论是在均匀随机流量每个输入对每个输出的流量等概率下纯输入排队的最大吞吐率只有2 - √2 ≈ 0.586。也就是说即使交换矩阵和输出端口都是空闲的你也只能跑到线速的 58.6%。这个数字我第一次看到的时候是有点震惊的。原因推一下也不复杂当 N 很大时每个输入端口上某一时刻队头包的目的地恰好空闲的概率乘上输入端口的平均占用率求极值之后就得到 0.586。用更直白的话说——你在入口处人为制造了一条独木桥后面的车再好也过不去。解决办法很直接一条队列拆成 N 条。每个输出方向一条独立的队列去 A 口的包排在 A 口队列里去 B 口的排在 B 口队列里互不影响。这就是 VOQVirtual Output Queue。加了 VOQ 之后纯输入排队的理论吞吐上限就变成了 100%配合好的匹配算法。注意VOQ 解决的是队头阻塞不解决输出竞争。两个输入的 A 口队列同时有包还是要靠 Crossbar 调度器去仲裁。VOQ 只是让不该等的包不必等。3.2 VOQ 的队列组织与计数结构VOQ 的数量是 N² 。32 口就是 1024 条队列64 口就是 4096 条128 口直接 16384 条。每条队列都得有独立的读写指针和长度计数这本身就是一笔不小的寄存器开销。队列本身放在哪有两种思路放在片内 SRAM延迟低但容量受限。通常单口总 VOQ 容量在几十到几百 KB 之间。适合小端口数、低时延的场景。放在共享缓存里VOQ 只维护指针这是主流做法。VOQ 本身只存链表头和链表尾的指针真正的数据存在 Shared Buffer 的共享池里。这样 VOQ 的寄存器开销就只有每条队列几个指针 一个长度计数规模可控。这里有个实现上的细节值得说如果每条 VOQ 只管指针那么共享池的分配粒度就很重要。分配粒度太粗比如按 1KB 分小包会浪费分配粒度太细比如按 32 字节分链表节点数暴涨指针域开销吃掉大部分收益。工程上常见的折中是按信元大小对齐分配——因为包本来就切成定长信元了每个信元一个链表节点天然对齐。VOQ 的计数结构也值得琢磨每条队列需要维护当前占用信元数这个计数用来做两个判断——一是是否超过保底门限保证小流量不被饿死二是是否触发背压告诉上游别发了。计数器是每收一个信元加一、每发一个减一硬件上就是一个加法器但要小心跨时钟域的问题如果收和发在同一个周期发生计数器要同时做加和减这时候需要的是加一减一的组合逻辑而不是简单的一加一减。3.3 调度器长什么样credit 怎么还VOQ 的调度分两层入口侧请求和出口侧授权。入口侧每个输入端口在每一轮匹配时把我有哪些非空 VOQ的 bitmap 发给仲裁器。仲裁器跑 iSLIP 或者类似的算法输出一个哪个输入的哪个 VOQ 获得了本轮授权的结果。授权结果通过一条窄的总线N 位宽广播回入口入口按授权结果从对应的 VOQ 取信元送进 Crossbar。出口侧每个输出端口需要知道自己还能收多少。这里有两种流控方式Credit-based信用流控出口给每个输入、每个 VOQ 分配一个信用额度比如你最多能给我发 8 个信元。入口每发一个就扣一个信用出口每发走一个就往回返一个信用。信用计数归零时这条 VOQ 就停止发送请求。信用的优点是不丢包、时延可控缺点是往返时延RTT决定了信用池的最小深度——如果 round trip 是 100ns链路速率是 100Gbps那单个 VOQ 至少要能装下 100ns × 100Gbps / 8 1250 字节约等于 20 个 64 字节信元。Threshold-based门限流控出口维护一个剩余空间计数低于某个门限就广播背压信号。这种方式实现简单但背压信号有延迟容易在大突发下产生超调overshoot需要额外的余量。我实测下来的体会是芯片内部Crossbar 前后几乎一定用 credit因为内部 RTT 短、可控芯片外部链路级大多用门限或者 pause 帧因为 RTT 长、信用池太贵。3.4 CICQ 这条岔路值不值得走除了输入排队 Crossbar 调度还有一条路线叫CICQCombined Input and Crosspoint Queued——在每个交叉点上也放一小块缓存。它的好处是一旦某个交叉点的缓存有空位输入侧就可以直接发不需要等整个 Crossbar 的全局匹配。调度退化成输入侧和输出侧两组独立的仲裁大大简化了全局调度器的复杂度。CICQ 理论上也能达到 100% 吞吐而且时延比 VOQ 集中调度更低。但代价是交叉点缓存的数量也是 N² 每个交叉点都要配独立的读写口和仲裁逻辑面积和功耗都不划算。所以现实中 CICQ 多见于学术论文和小规模设计量产芯片里用得不多。我个人的判断端口数小于 16 的场景可以考虑 CICQ 或者简化的交叉点缓存超过 32 口基本还是 VOQ 集中调度更现实。4. Shared Buffer拿内存带宽换面积4.1 统计复用为什么这么香输出排队Output Queued是最理想的架构每个输出端口有独立缓存包一进来就直接送到对应输出的缓存里输出端口按自己的速率往外发。它的优点是时延最低、调度最简单、完全没有 HOL 和输出竞争问题。但它有一个致命缺陷需要 N 倍加速。因为 N 个输入可能同时在给同一个输出发数据输出端口的写带宽必须是线速的 N 倍才能不丢包。N32 就是 32 倍加速这在物理上不可能实现。共享缓存Shared Buffer换了思路不按输出分缓存所有输出共用一个大池子。每个输出端口占用的缓存量按需浮动——这个输出端口流量大就多占点流量小就少占点。这就是统计复用。它的好处非常直观。假设总缓存是 32MB32 个端口如果按输出固定分配每个端口只有 1MB。但如果某个时刻只有 4 个端口有突发流量它们可以合计占用 32MB每个 8MB吸收突发的能力提升了 8 倍。而实际网络中流量的分布本来就是极不均匀的统计复用带来的收益是实打实的。代价是所有读写都挤在一份内存上。这就把问题从带宽加速变成了内存带宽设计。4.2 单级共享与分布式共享的选择共享缓存有两种落地方式。单级共享Centralized Shared Buffer整个芯片就一块大的 SRAM 池所有端口共享。优点是复用效率最高缓存利用率接近理想。缺点是这块 SRAM 必须是多端口或多 bank而且物理上要放在 die 的中心位置所有端口的走线都往这里汇聚floorplan 压力大。同时单级共享意味着任何一个 bank 出问题都会影响全局可靠性设计要更谨慎。分布式共享Distributed Shared Buffer把缓存池切成若干份分布在芯片各处比如每个象限一份。每个端口就近访问本地池跨池访问走一条内部总线。好处是布线压力小、可以就近取数、扩展性好。坏处是跨池访问有额外时延而且池和池之间的容量不能完全自由流动突发吸收能力比单级共享弱一些。实际量产芯片里中低端小于 32 口多是单级共享高端64 口以上大多是分布式共享或者分布式 中心汇聚的混合结构。我见过的几个设计会按流量特征把端口分组同组内共享一块组间靠一条高速总线连起来。实操心得做容量规划时不要拿总缓存容量直接除以端口数。真正决定突发吸收能力的是单个端口在极端情况下能借到的最大缓存量这个量受限于共享池的分配策略和动态门限。我见过标称 64MB 的芯片实际单端口峰值只能借到 3MB因为动态门限卡死了。4.3 缓存管理链表、指针和动态门限共享缓存的分配通常用链表方式一个大的链表把所有空闲节点串起来每收一个信元就从空闲链表头摘一个节点挂到对应 VOQ 的链表尾。这里有个细节链表节点里要存下一个节点在哪这个指针域就是开销。如果信元是 64 字节节点数就是总缓存/64指针域一般 16~20 位。这是我前面说的分配粒度按信元对齐的另一个理由。门限设计是共享缓存里最有艺术性的部分。太松了一条猛流能把整个池子占满其他端口全被饿死太紧了突发吸收能力又浪费了。常见的几种策略门限策略原理优点缺点静态固定每条队列分配固定上限实现最简单无法适应流量变化浪费缓存静态共享固定保底 全局共享池兼顾公平和复用共享池的分配比例难调动态门限DT门限随剩余空间浮动复用率最高参数敏感不易调优每流门限按流五元组限流防止单流霸占流表规模爆炸性价比低动态门限的经典公式是每条队列的门限 α × (总缓存 - 当前已用)其中 α 通常取 1/8 到 1/4 之间。它的含义是空闲空间越多允许单条队列占得越多空闲空间越少门限自动收紧逼迫各队列公平竞争。这个公式我第一次推导的时候觉得有点反直觉——为什么不直接均分想通了就明白了当总缓存空闲很多时没有竞争让某条队列多占有益无害反正别人也不用当缓存快满时才需要严格限制。α 的取值决定了公平性和复用率的平衡点α 越小越公平α 越大复用率越高。4.4 带宽这道数学题必须算清楚共享缓存的瓶颈永远是内存带宽。我把这笔账算一遍你可以照着套。假设一颗芯片N 32 口每口 100Gbps总带宽 3.2Tbps。信元长度 64 字节。单个信元在 100Gbps 线上传输的时间64 × 8 / 100e9 5.12 ns每 5.12ns全芯片需要完成的写入量最坏情况下 32 个入口同时来包就是 32 × 64B 2KB同样每 5.12ns需要完成的读取量也是 32 × 64B 2KB所以共享缓存的总带宽需求 4KB / 5.12ns ≈ 800 GB/s也就是写 400GB/s 读 400GB/s这还只是平均值。如果考虑到效率损失bank 冲突、刷新开销、控制位开销实际设计余量要留到 1.3~1.5 倍也就是 1.0~1.2 TB/s 量级。那么一个 SRAM bank 能提供多少带宽假设 bank 工作在 500MHz位宽 64 字节512 bit单个 bank 就是 500e6 × 64 32 GB/s。那要跑到 800GB/s至少需要25 个 bank。考虑到冲突和余量实际会做到 32~48 个 bank。这个量级直接决定了几个设计约束bank 数量不能少少了带宽不够地址分配必须打散否则连续地址会撞在同一个 bank 上形成热点bank 的输入输出要平衡读和写不能互相抢端口所以通常用真双口或者伪双口 SRAM信元不能太小太小的话单位数据要走的控制路径次数变多效率下降。地址打散常用的做法是低位交叉 哈希把信元地址的低几位直接用作 bank 选择位保证连续信元落在不同 bank高位再做一次简单哈希比如异或折叠以应对 stride 型访问模式。这个哈希函数不需要多强低位交叉能解决 90% 的冲突剩下 10% 靠加一点 bank 余量兜住。5. Cell Fabric定长信元带来的一切5.1 为什么必须切片可变长包在交换结构里是灾难。原因有三第一调度的同步性。Crossbar 每周期只做一次匹配如果各个输入端口送来的数据长度不一样短包很快发完了要等长包还在传整个矩阵的利用率就被最短的那个包拖住。这叫同步效率损失。定长信元之后所有输入端口每周期送同样大小的数据矩阵利用率才能打满。第二缓存管理的统一性。定长信元的分配、回收、链表操作都是规整的硬件可以用固定的状态机加计数器搞定。变长包则要处理最后一个不满的信元怎么办复杂度一下子上去了。第三时延的可预测性。定长信元穿过每一级流水线的时间是固定的总时延就是级数 × 每级时间可以精确建模。变长包的最坏情况时延要靠分析最长的包再加上排队很难给出紧的界。所以从 1990 年代的 ATM 到现在凡是高性能交换结构内部一律切片。ATM 用的就是 53 字节定长信元现在的以太网交换芯片通常用 64~256 字节的内部信元。5.2 内部信元的格式与开销一个内部信元通常包含三部分源信息头标识这个信元从哪个端口来、属于哪条 VOQ、有没有被切分过首信元/中间信元/尾信元。这部分在芯片内部是已知的不需要像外部包头那样长一般几到十几个 bit 就够。目的信息头标识这个信元要去哪。如果是单播就是输出端口号如果是组播是一张 bitmap 或者一个组播组 ID。这部分决定了 Circuit 的匹配请求。载荷真正的数据。这部分占比越高内部开销越小。我把不同信元尺寸下的开销算一下。假设内部头加尾一共 8 字节这是个比较典型的数包含源 ID、目的 ID、信元类型、序列号等信元总长载荷头开销占比每个 1500 字节包需要的信元数内部搬运总量32 字节24 字节25%631890 字节64 字节56 字节12.5%271728 字节128 字节120 字节6.25%131664 字节256 字节248 字节3.1%71792 字节从这个表能看出两个趋势信元越大头开销越小但信元大到一定程度之后内部搬运总量反而回升因为最后一个不满的信元也要占用整个信元的带宽。128 字节看起来是个不错的甜点区这也是为什么很多芯片用 128 字节或者 96 字节的内部信元。但选多大还要看别的因素信元越小调度粒度越细突发流量下的时延更均匀信元越大内存访问效率越高共享缓存的 bank 压力越小。注意内部信元长度是芯片的硬约束一旦流片就没法改。做架构定义时这个参数要和 VOQ 数量、缓存 bank 数量、调度迭代次数一起联合优化单独拍脑袋定容易返工。5.3 重组、乱序与多播复制切片之后必须重组。出口侧要维护一个重组上下文记录当前正在重组的包属于哪条流、已经收了多少字节、还差多少、下一个信元应该是第几个。这里最容易出的问题是乱序——不同信元走了 Crossbar 的不同路径到达出口的顺序可能和发送顺序不一样。处理乱序有三种常见策略严格顺序Crossbar 保证同一个流的信元按序传输。实现上通常用同一 VOQ 的信元不能同时占用多条路径来约束代价是调度灵活性下降。出口重排出口侧维护一个重排缓冲区按信元序号排好再送出。代价是多一块缓存和一套排序逻辑而且序号一旦丢失比如丢包重排窗口就会卡住需要超时机制来跳过。允许乱序某些场景比如 RDMA 的乱序容忍、或者内部多路径允许流内乱序交给上层协议处理。这在数据中心场景里越来越常见但要求上层协议栈配合。多播是另一个难点。一个多播包要送到多个出口每个出口都需要一份完整的副本。实现方式有几种入口复制在进入 Crossbar 之前就复制成多份每份带不同的目的端口。优点是出口侧简单缺点是入口链路要承担多倍流量。出口复制Crossbar 只传一份出口侧复制。优点是入口节省带宽缺点是 Crossbar 要支持一点到多点的传输调度复杂。中间复制在共享缓存里存一份组播组里每个出口各自去读一份。这是共享缓存架构的天然优势因为数据本来就在中央池子里多读几次就行——代价是共享缓存的读带宽要多留余量。实操心得多播流量的读带宽放大倍数要考虑最坏情况。如果一个包要多播到 32 个口共享缓存的读带宽需求就要翻 32 倍。实际设计里不会真的允许 32 份全并发读而是靠分时复制——把组播读操作打散到多个周期里平滑掉峰值。这会引入额外时延但对绝大多数组播业务比如视频分发、行情广播都是可接受的。5.4 切片粒度怎么定总结一下选信元长度的几个考虑维度内存效率 vs 时延抖动小信元内存效率低但时延均匀大信元反过来。调度开销 vs 带宽浪费小信元每周期都要跑一次匹配调度器压力大大信元每次匹配服务的数据多调度器轻松但最后一个不满信元的浪费比例高。重组复杂度小信元意味着每个包要被切成更多份重组状态机要处理更多边界情况验证工作量成倍增加。我一般会这样估先算出目标包长分布下内部搬运总量随信元长度的曲线就是上面那张表的做法找到曲线的最低点或者拐点附近再把这个候选值代入调度器和内存带宽的模型看能不能满足时序和带宽。两个条件都满足的区间里取偏小的那个值给时延和调度留点余量。6. 常见问题与排查技巧实录6.1 线上问题的排查顺序遇到交换芯片性能问题我的排查顺序基本固定先看是不是缓存不够再看是不是调度不公最后看是不是带宽打满。判断缓存够不够看的是丢包是否发生在微突发的窗口内、丢包是否集中在少数端口、把缓存门限放宽之后丢包是否消失。如果是那就是缓存或者门限的问题。判断调度公不公平看的是是否有某些流的时延明显高于其他流、是否存在大流一来小流就卡的现象、把优先级配置改一改现象是否变化。如果是问题在 VOQ 门限或者调度算法的权重配置。判断带宽是否打满看的是是不是所有端口都在均匀流量下出问题、内部计数器的占用率是否长期高于 80%、降速之后问题是否消失。如果是那可能是内存带宽或者 Crossbar 匹配效率到了极限属于架构层面的瓶颈软件没法救。6.2 一张速查表现象可能原因排查动作解决方向均匀流量正常混合流量丢包VOQ 保底门限过低小流被饿死抓取各 VOQ 的占用率分布提高保底门限或调整动态门限 α突发流量丢包平均流量正常共享缓存容量或门限配置不足统计瞬时缓存占用峰值加大缓存池或放宽动态门限单口时延高但吞吐正常Crossbar 仲裁等待时间过长统计每个 VOQ 的排队时延减少迭代次数或增加输入缓存深度多播流量下丢包共享缓存读带宽被组播复制打满统计读带宽占用率分时复制或降低组播扇出某些端口长期低吞吐调度器轮询指针卡在某个位置检查指针更新逻辑修正 iSLIP 指针更新条件内部搬运量大于外部流量信元过小导致头开销过高算内部信元开销占比增大信元长度需流片前决定6.3 流片前怎么验证数据通路的问题一旦流片就没法改所以在 RTL 和 RTL 之前必须验证到位。我踩过的几个坑记下来供参考。坑一只验证了均匀流量。均匀随机流量是最容易过的因为它的统计特性最平滑。真正会暴露问题的是突发性流量和有相关性的流量比如同一对端口之间的长流。验证环境里必须包含基于真实抓包回放的流量模型还要加上人为构造的微突发。坑二忽略了信用回路的边界情况。信用流控在正常情况下很难出问题但一旦出现信用泄漏比如某个信元被丢弃但信用没还给入口就会慢慢累积成死锁。验证时要专门构造丢包场景检查信用计数是否能归零、是否会溢出。坑三调度器的公平性没有量化验证。iSLIP 的指针更新写错一位在功能上可能完全看不出来但在特定流量模式下会导致某个端口长期拿不到授权。这个问题的验证方法是构造 N 个输入抢 1 个输出的饥饿场景跑足够长的时间统计每个输入的授权次数看是否均匀。坑四多播复制的带宽峰值没有覆盖到。多播的读带宽放大在平均情况下看起来没问题但最坏情况一个包扇出到最大组播组且组播组覆盖的端口刚好都在忙下的峰值往往要专门构造测试用例才能触发。坑五跨时钟域的计数器竞态。收发在不同时钟域时VOQ 的长度计数器要做跨时钟同步。如果同步逻辑用了简单的两级触发器而没有做格雷码处理多次采样的值可能不一致导致最坏情况下计数偏小进而误判缓存已满或者缓存为空。这个问题在仿真里几乎必现但只有长时间高负载跑才能触发。最后说一句我的个人体会数据通路这块架构上真正难的不是选 Crossbar 还是 Clos也不是信元切 64 字节还是 128 字节而是让所有参数咬合在同一套假设下。信元长度、VOQ 数量、缓存 bank 数、动态门限 α、调度迭代次数——这五个参数里任何一个单独调优都能找到更优解但它们是耦合的。我做过的一个项目前期为了省面积把信元定成 64 字节后期发现内存带宽不够只能反过来加 bank 数结果面积又超了最后是砍了端口数才收住。这种事在项目周期里一旦发生返工成本极高最好在架构定义阶段就把这几个参数的敏感度分析做透把三到五组候选配置都跑一遍性能模型别指望后期能补。