交换芯片数据通路设计:Crossbar、VOQ、Shared Buffer与iSLIP仲裁
2026/9/21 20:23:32 网站建设 项目流程

交换芯片这个领域,很多人第一次接触时会被一堆术语砸晕:Crossbar、VOQ、Shared Buffer、Cell Fabric、iSLIP,每个词拆开都认识,合在一起就不知道它们在芯片里到底怎么协作。我当年从软件转发转到芯片微架构,最大的感受是——数据通路的设计本质上是在回答一个问题:当多个入口同时要把数据送到同一个出口时,你打算让谁等、在哪里等、等多久。这个问题的答案,决定了这颗芯片是能跑满线速,还是会在突发流量下丢包丢到怀疑人生。

这篇内容适合两类人看:一类是做网络软件、驱动或SDK,想搞清楚底层硬件到底怎么搬数据的工程师;另一类是刚入行做交换芯片设计或验证,需要把教科书概念和真实微架构对应起来的人。我会围绕数据通路这条主线,把Crossbar的仲裁逻辑、VOQ为什么必须存在、Shared Buffer的共享边界、Cell Fabric的切分方式,以及iSLIP这类调度算法串起来讲,尽量把每个设计选择背后的“不得已”说清楚。

1. 从“谁先走”这个问题理解交换芯片数据通路

1.1 一个最朴素的交换场景暴露出的核心矛盾

假设你有一颗4端口的交换芯片,每个端口速率一样。某一时刻,端口1要把数据发给端口3,端口2也要把数据发给端口3,端口4要发给端口1。这时候问题立刻出现了:端口3同一时刻只能接收一份数据,端口1和端口2必须有一个先等。如果端口1和端口2的数据都先被收进芯片内部,那它们存在哪里?存多少?等多久?这就是数据通路要解决的全部矛盾。

最原始的做法是共享一条总线,所有端口分时复用。但总线方案在端口数一多、速率一高就崩了,因为总带宽是固定的,N个端口抢一条总线,平均每个端口只能拿到总带宽的1/N。交换芯片要的是“任意端口到任意端口都能同时通信”,这就要求内部互联结构必须支持多对端口并行传输,Crossbar就是干这个的。

1.2 Crossbar不是一根线,而是一个可编程的交叉点阵列

很多人把Crossbar想象成一根很宽的线,其实它是一组交叉点开关。N个输入端口和N个输出端口之间,有N×N个交叉点。每个交叉点可以独立打开或关闭,打开就表示这个输入连到了这个输出。理想情况下,N个输入可以同时连到N个不同的输出,实现N倍加速。

但这里有个关键限制:每个输出端口同一时刻只能被一个输入端口占用。所以Crossbar的调度问题就变成了——在每个时隙开始前,决定哪些交叉点闭合。这个决定就是仲裁,仲裁做得好不好,直接决定了Crossbar的吞吐率能不能逼近100%。

我见过不少刚接触的人以为Crossbar是“硬件自动搞定”的,实际上仲裁逻辑才是设计难点。一个N×N的Crossbar,如果每个输入都独立随机选择输出,冲突概率会随着N增大而急剧上升。有研究数据表明,纯随机选择下,N=16时吞吐率就掉到60%左右了。所以必须引入集中式的调度算法,iSLIP就是在这个背景下被提出来的。

1.3 数据通路的四个层次:入队、排队、调度、出队

把数据通路拆开看,其实就四个动作。第一,数据从入口进来,先决定它该去哪个出口,这叫路由查找。第二,根据出口把数据放进对应的队列,这叫入队。第三,调度器决定当前时隙哪个队列的数据可以通过Crossbar,这叫仲裁。第四,数据穿过Crossbar到达出口,这叫出队。

这四个动作里,入队和排队的位置选择,直接引出了VOQ和Shared Buffer的分野。调度算法的选择,引出了iSLIP和它的各种变种。而数据在内部传输的格式,引出了Cell Fabric的概念。下面几节我会逐个拆开讲。

2. Crossbar仲裁:iSLIP到底在解决什么实际问题

2.1 朴素轮询为什么不够用

最简单的仲裁是轮询:每个输入端口轮流获得发送权。但轮询有个致命问题——它不考虑输出端口的冲突。假设输入1和输入2都想发给输出3,轮询让输入1先发,输入2等下一轮。下一轮输入2想发给输出3,但输入3也想发给输出3,又冲突。轮询只保证了输入之间的公平,没有解决输出冲突。

更糟的是,轮询在流量不均匀时效率极低。如果输入1的数据全部要去输出1,输入2的数据全部要去输出2,本来可以并行传输,但轮询可能让输入1先发、输入2等待,白白浪费了并行能力。

2.2 iSLIP的三步迭代:请求、授权、接受

iSLIP的核心思想是让输入和输出之间进行多轮协商。每个时隙开始时,所有有数据要发的输入端口,向它们的目标输出端口发出请求。每个输出端口收到多个请求后,用自己的轮询指针选一个输入,发出授权。输入端口收到多个授权后,用自己的轮询指针选一个输出,发出接受。这一轮结束后,没被接受的请求进入下一轮迭代,通常迭代log2(N)次就能收敛。

这个过程中有两个指针:输入端的授权指针和输出端的接受指针。指针只在成功匹配后更新,这样保证了长期公平性。我实测过iSLIP在均匀流量下的吞吐率能到100%,在突发流量下也能到90%以上,比纯轮询好太多。

但iSLIP不是没有代价。它的迭代次数和端口数相关,N越大迭代越多,调度延迟越大。而且iSLIP假设每个输入端口只有一个队列,这在多播场景下会出问题。所以实际芯片里往往用iSLIP的变种,比如带优先级的iSLIP、或者结合VOQ的分布式调度。

2.3 从iSLIP到实际芯片的调度器设计

真实交换芯片里,调度器不会只有一个iSLIP。通常会有两级甚至三级调度。第一级在入口侧,决定哪个VOQ可以参与Crossbar仲裁。第二级是Crossbar仲裁本身,用iSLIP或类似算法。第三级在出口侧,决定哪个队列的数据先出去。

为什么要这么复杂?因为入口侧的VOQ数量可能远大于端口数。比如一个32端口的芯片,每个端口有32个VOQ,总共1024个队列。如果让这1024个队列直接参与Crossbar仲裁,迭代次数会爆炸。所以入口侧先做一轮筛选,把每个入口端口的多个VOQ压缩成一个候选,再参与全局仲裁。

这里有个实操经验:调度器的指针初始化状态会影响收敛速度。如果所有指针都从0开始,第一个时隙的冲突概率最高。有些设计会在初始化时把指针打散,让它们均匀分布,这样第一轮匹配的成功率就能提高不少。

3. VOQ:为什么入口必须按出口分开排队

3.1 头阻塞:一个队列堵住所有流量

假设每个输入端口只有一个FIFO队列。端口1的队列头部是一个要去端口3的数据包,但端口3当前正忙。这时候端口1的队列里即使有要去端口2的数据包,也发不出去,因为FIFO只能从头部取。这就是头阻塞(HOL Blocking)。

HOL阻塞的杀伤力有多大?在均匀流量下,单FIFO的吞吐率上限是58.6%。这个数字是经过严格推导的,不是拍脑袋。也就是说,你花大价钱买的交换芯片,如果入口只用一个队列,理论吞吐率连60%都到不了。

3.2 VOQ的拆分逻辑:每个入口为每个出口维护独立队列

VOQ(Virtual Output Queue)的思路很直接:既然头阻塞是因为不同出口的数据混在一个队列里,那就按出口拆开。端口1为端口2、端口3、端口4各维护一个队列。端口1要发给端口3的数据放在VOQ(1,3)里,要发给端口2的放在VOQ(1,2)里。这样端口3忙的时候,端口1可以从VOQ(1,2)取数据发给端口2,完全不受影响。

VOQ把吞吐率从58.6%拉到了接近100%。代价是队列数量变成了N×N。对于32端口芯片,就是1024个队列。每个队列都需要独立的存储空间和状态管理,芯片面积和功耗都会上升。

3.3 VOQ的存储代价与工程折中

1024个队列听起来吓人,但实际芯片里不会给每个VOQ都分配同样大的空间。常见做法是共享缓存加动态阈值。所有VOQ共享一块Buffer,但每个VOQ能占用的最大空间由阈值决定。阈值可以是静态的,也可以是动态调整的。

动态阈值的好处是能适应流量变化。如果某个VOQ长时间空闲,它的阈值可以调低,把空间让给活跃的VOQ。但动态阈值需要额外的统计和计算逻辑,设计复杂度更高。我见过一些芯片用“最大最小公平”算法来分配Buffer,效果不错,但实现起来需要迭代计算,对时序有压力。

还有一个折中是VOQ的粒度。不一定每个出口一个VOQ,可以每两个或四个出口一组。这样队列数减少,但头阻塞只是缓解不是消除。具体怎么选,要看目标场景的流量模式。数据中心场景下,流量往往集中在少数几个热点出口,VOQ粒度粗了容易出问题。

4. Shared Buffer:共享到哪里,边界在哪里

4.1 共享Buffer的基本模型与收益

Shared Buffer是指所有端口的队列共享同一块物理存储。相比每个端口独立分配Buffer,共享方案在统计复用上更有优势。因为流量突发是随机的,独立Buffer需要按最坏情况给每个端口预留空间,而共享Buffer可以让空闲端口的空间被活跃端口借用。

举个具体数字:假设8个端口,每个端口独立Buffer为1MB,总存储8MB。如果流量集中在端口1和端口2,它们各自只能用1MB,超过就丢包。但如果8MB是共享的,端口1和端口2可以各自用到接近4MB,丢包率大幅下降。

4.2 共享Buffer的公平性难题:一个端口能占多少

共享Buffer最大的问题是公平性。如果没有限制,一个高速端口可能把整个Buffer占满,其他端口无空间可用。所以必须设置阈值。阈值的设计有三种常见策略:

第一种是静态阈值,每个端口或每个队列固定一个上限。简单但不够灵活,流量模式变化时效率低。

第二种是动态阈值,根据当前空闲空间和活跃端口数动态计算。比如空闲空间多时放宽阈值,空闲空间少时收紧。这种策略能提高利用率,但计算逻辑复杂。

第三种是预留加共享,每个端口先预留一小块保底空间,剩下的作为共享池。保底空间保证基本通信不丢包,共享池提高突发吸收能力。这是实际芯片里最常见的方案。

4.3 共享Buffer与VOQ的配合方式

Shared Buffer和VOQ不是对立的,而是配合的。VOQ定义了逻辑队列的结构,Shared Buffer定义了物理存储的分配方式。一个典型的组合是:入口侧用VOQ做逻辑排队,物理存储用Shared Buffer,每个VOQ的入队和出队由链表管理。

链表管理的关键是空闲块的管理。Buffer被切成固定大小的Cell,每个Cell有一个指针。空闲Cell组成空闲链表,入队时从空闲链表取Cell,出队时把Cell还回去。这个链表操作必须在每个Cell的周期内完成,对时序要求很高。我见过一些设计用多级流水线来隐藏链表操作的延迟,但流水线深度增加会带来额外的存储和复杂度。

还有一个容易忽略的点是Cell的大小选择。Cell太小,链表操作频繁,开销大;Cell太大,内部碎片多,小包浪费严重。常见的选择是64字节或128字节,和以太网最小包长对齐。但如果是变长包,最后一个Cell可能装不满,需要额外的字节计数来标记有效长度。

5. Cell Fabric:为什么内部传输要切成定长单元

5.1 变长包直接过Crossbar的问题

如果数据包直接以变长形式通过Crossbar,会带来几个麻烦。第一,仲裁周期不固定。一个64字节的包和一个1500字节的包,传输时间差20多倍,调度器很难做时隙对齐。第二,Crossbar的交叉点开关需要保持闭合直到整个包传完,这期间其他输入不能使用这个输出,浪费严重。第三,Buffer管理复杂,变长存储需要连续空间,容易产生碎片。

所以实际芯片几乎都把变长包切成定长Cell,以Cell为单位做仲裁和传输。这就是Cell Fabric的由来。

5.2 Cell的切分与重组:入口切片、出口重组

入口侧收到一个包后,先按固定长度切成Cell。每个Cell带上头部信息,包括源端口、目的端口、包序号、Cell序号等。这些Cell独立通过Crossbar,到达出口后按包序号和Cell序号重组。

重组逻辑需要处理乱序到达。因为不同Cell可能走不同的路径,或者被不同的调度周期选中,到达出口的顺序可能和发送顺序不一致。出口侧需要维护一个重组缓冲区,按序号把Cell排好,等所有Cell到齐后再组成完整包发出。

这里有个设计选择:是在入口侧等整个包收完再切片,还是边收边切。边收边切能降低延迟,但需要处理包尾不足一个Cell的情况。常见做法是最后一个Cell用有效字节数标记,重组时按有效字节数截断。

5.3 Cell Fabric的加速比与内部带宽计算

Cell Fabric的带宽通常要大于外部端口带宽之和,这个比值叫加速比(Speedup)。为什么需要加速比?因为Crossbar仲裁不可能100%无冲突,总有一些时隙浪费。加速比就是用来补偿这些浪费的。

加速比的计算有个经验公式:Speedup = 1 / (1 - 冲突率)。如果冲突率是10%,加速比至少要1.11。实际设计中,考虑到突发和调度开销,加速比通常取1.5到2之间。但加速比越高,内部时钟频率和功耗也越高,需要权衡。

我参与过的一个项目里,外部端口是100G,内部Cell Fabric跑在1.5倍加速比下,Crossbar的时钟频率比端口时钟高50%。这个比例下,功耗增加了约30%,但吞吐率从85%提升到了98%。值不值得,要看目标场景对丢包的容忍度。

6. 把Crossbar、VOQ、Shared Buffer、Cell Fabric串起来看

6.1 一个数据包从入口到出口的完整旅程

现在把前面几节串起来。一个包从端口1进来,首先做路由查找,确定目的端口是端口3。然后包被切成Cell,每个Cell带上目的端口信息。根据目的端口,Cell被放入VOQ(1,3)。VOQ(1,3)的物理存储在Shared Buffer里,通过链表管理。

调度器在每个时隙开始时,让所有非空VOQ参与iSLIP仲裁。VOQ(1,3)如果被选中,它的一个Cell就从Shared Buffer读出,通过Crossbar送到端口3。端口3收到Cell后,放入重组缓冲区,等同一个包的所有Cell到齐后,组成完整包从端口3发出。

这个流程里,每个环节都有优化空间。路由查找可以用TCAM或算法查找;VOQ的阈值可以动态调整;iSLIP的迭代次数可以配置;Cell的大小可以调;重组缓冲区的深度可以设。每个参数都会影响最终的吞吐率、延迟和丢包率。

6.2 各组件之间的耦合关系与设计约束

这些组件不是独立的,它们之间有强耦合。比如VOQ的数量决定了调度器的复杂度,调度器的迭代次数又决定了Crossbar的加速比需求。Shared Buffer的Cell大小决定了链表操作的频率,链表操作的延迟又限制了Crossbar的时隙长度。

一个常见的约束是:Crossbar的时隙长度必须大于等于最慢的VOQ读取加链表更新时间。如果时隙太短,链表操作来不及完成,就会出错。所以设计时通常先确定Cell大小和链表操作延迟,再反推时隙长度,最后确定Crossbar的时钟频率。

另一个约束是重组缓冲区的深度。如果深度不够,乱序Cell到达时可能被丢弃,导致重传。深度太大又浪费存储。通常根据Crossbar的最大乱序程度来估算,而乱序程度又和调度算法、加速比有关。这是一个循环依赖,实际设计中往往先给一个经验值,再通过仿真调整。

6.3 实际芯片中常见的折中方案

真实芯片里没有完美的方案,只有折中。我见过的一些折中包括:用VOQ的粗粒度减少队列数,用Shared Buffer的动态阈值提高利用率,用iSLIP的简化版降低调度延迟,用Cell的固定大小简化链表管理。

还有一个折中是分级Crossbar。对于大端口数芯片,单个Crossbar的交叉点太多,功耗和面积都受不了。所以做成两级:第一级把端口分组,组内用小Crossbar;第二级用另一个Crossbar连接各组。这样交叉点总数从N²降到N²/2左右,但调度变成两级,复杂度上升。

选择哪种折中,取决于目标市场的需求。数据中心芯片追求高吞吐和低延迟,可能用全VOQ加高加速比;企业级芯片追求成本和功耗,可能用粗粒度VOQ加低加速比。没有绝对的好坏,只有适不适合。

7. 几个容易踩的坑和实操建议

7.1 VOQ数量爆炸时的存储管理技巧

当端口数到64甚至128时,VOQ数量是4096到16384。如果每个VOQ都维护独立的头尾指针和计数器,光状态存储就不得了。一个技巧是用链表把空闲VOQ串起来,只给活跃VOQ分配状态。另一个技巧是用哈希表把VOQ索引映射到物理队列,减少索引存储。

但哈希有冲突,冲突时可能需要多个VOQ共享一个物理队列,又引入了头阻塞。所以哈希函数的设计很关键,要尽量把同一入口不同出口的VOQ映射到不同桶。我见过用异或哈希的,效果比取模好,因为异或对低位变化更敏感。

7.2 iSLIP指针初始化的实际影响

前面提过指针初始化,这里展开说。iSLIP的指针如果全部从0开始,第一个时隙所有输入都请求输出0,冲突最大。如果指针随机分布,第一轮匹配成功率能提高30%以上。但随机分布需要额外的随机数生成器,或者用端口号做种子做伪随机。

更简单的做法是用端口号本身做初始指针。比如输入i的指针初始为i mod N,输出j的指针初始为j mod N。这样指针天然分散,不需要随机数。实测下来,这种初始化方式在均匀流量下和随机初始化效果差不多,但实现简单得多。

7.3 Cell大小选择的量化依据

Cell大小不是拍脑袋定的。要考虑三个因素:最小包长、链表操作开销、内部碎片率。以太网最小包是64字节,如果Cell小于64字节,一个最小包要切多个Cell,链表操作次数增加。如果Cell大于64字节,最小包只占一个Cell的一部分,碎片浪费。

常见的选择是64字节或128字节。64字节的碎片率对最小包是0%,对1500字节包是4%左右。128字节的碎片率对最小包是50%,对1500字节包是8%左右。所以64字节更优,但链表操作频率是128字节的两倍。如果链表操作能在一个时隙内完成,64字节是更好的选择。

7.4 重组缓冲区深度的估算方法

重组缓冲区的深度决定了能容忍多大的乱序。乱序程度和Crossbar的调度有关。如果调度器保证同一包的Cell按序发送,乱序程度就低。但iSLIP不保证按序,所以乱序程度可能较高。

一个估算方法是:最大乱序程度约等于Crossbar的加速比乘以端口数。比如加速比1.5,端口数32,最大乱序约48个Cell。重组缓冲区深度至少要是这个数的两倍,留出余量。实际设计中,通常用仿真来确定,因为流量模式对乱序影响很大。

7.5 验证阶段最容易忽略的边界场景

验证数据通路时,有几个边界场景容易被忽略。第一,所有端口同时向同一个端口发包,这是最坏冲突场景,考验调度器的公平性和Buffer的阈值。第二,包长从最小到最大连续变化,考验Cell切分和重组的正确性。第三,长时间满负荷运行,考验指针的公平性和Buffer的泄漏。

还有一个场景是背靠背小包。小包切成的Cell少,调度周期短,容易暴露时隙对齐问题。我见过一个bug就是背靠背小包时,重组缓冲区指针回绕出错,导致数据错位。这种bug在随机流量下很难复现,必须专门构造测试用例。

8. 从微架构到实现:axi4 crossbar的落地考量

8.1 AXI4 Crossbar与交换芯片Crossbar的区别

最近axi4 crossbar实现是个热词,很多人把AXI4的Crossbar和交换芯片的Crossbar混为一谈。两者虽然都叫Crossbar,但设计目标不同。AXI4 Crossbar是片上总线互联,主要解决多个Master和多个Slave之间的连接,强调协议兼容和低延迟。交换芯片的Crossbar是数据平面互联,强调高吞吐和公平仲裁。

AXI4 Crossbar通常用简单的轮询或固定优先级仲裁,因为片上Master数量少,冲突不严重。交换芯片的Crossbar要用iSLIP这类迭代仲裁,因为端口多、流量大、冲突严重。所以不能直接把AXI4 Crossbar的设计套到交换芯片上。

8.2 用AXI4实现Crossbar时的仲裁器设计

如果要用AXI4实现一个类似交换芯片的Crossbar,仲裁器是核心。AXI4的仲裁通常在每个周期做一次,可以用组合逻辑实现。但iSLIP需要多轮迭代,组合逻辑实现不现实,需要时序逻辑加状态机。

一个可行的方案是用两级仲裁:第一级用AXI4的轮询仲裁做粗筛,第二级用简化的iSLIP做精调。粗筛把每个Master的多个请求压缩成一个,精调在压缩后的请求之间做迭代匹配。这样既利用了AXI4的协议兼容性,又引入了交换芯片的调度优势。

8.3 时序收敛与面积优化的实际经验

AXI4 Crossbar的时序收敛是个大问题。交叉点开关的扇出很大,N×N的Crossbar,每个输出要连N个输入,扇出是N。N=16时扇出16,还能接受;N=32时扇出32,时序就很紧张了。

优化方法有几种:一是用流水线把Crossbar切成多级,每级扇出减小;二是用多路选择器树代替交叉点阵列,减少长线;三是用寄存器切割关键路径。我试过流水线方案,把Crossbar切成两级,每级扇出从32降到16,时序从500MHz提升到了800MHz,但延迟增加了一个周期。

面积优化方面,交叉点开关的面积和N²成正比。N=32时,1024个交叉点,每个交叉点即使只有几个门,总面积也不小。用多路选择器树可以把面积降到N×logN级别,但布线复杂度上升。实际选择要看工艺和布线资源。

8.4 从RTL到门级:验证Crossbar功能的关键用例

验证Crossbar功能时,关键用例包括:所有输入同时请求同一个输出,检查仲裁公平性;所有输入请求不同输出,检查并行传输能力;输入请求动态变化,检查指针更新正确性;长时间运行,检查指针回绕和公平性。

还有一个用例是背压测试。当输出端口暂时无法接收时,Crossbar要能正确反压输入,不能丢数据。这个用例考验的是流控逻辑,和仲裁逻辑是分开的,但两者必须协同工作。

9. 写在最后:数据通路设计的取舍哲学

做了这么多年交换芯片,我越来越觉得数据通路设计是一门取舍的艺术。Crossbar的端口数、VOQ的粒度、Shared Buffer的阈值、Cell的大小、iSLIP的迭代次数、加速比的高低,每一个参数都在吞吐率、延迟、面积、功耗之间做权衡。没有一套参数能通吃所有场景。

我的经验是,先明确目标场景的流量特征。如果是数据中心东西向流量,热点集中,VOQ粒度要细,Shared Buffer阈值要动态,加速比要高。如果是企业接入,流量分散,VOQ粒度可以粗一些,加速比可以低一些,省功耗。定好场景再选参数,比盲目追求高指标要靠谱得多。

还有一个体会是,仿真和实测的差距往往出在边界场景。均匀流量下跑得再好,不代表突发流量下没问题。所以验证阶段一定要构造极端用例,把所有端口往一个端口打,把包长拉到最小和最大,跑长时间稳定性。这些用例暴露的问题,才是真正决定芯片能不能商用的关键。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询