ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于电路交换的NoC路由器设计:原理、架构与FPGA实现

基于电路交换的NoC路由器设计:原理、架构与FPGA实现 简介一篇面向片上网络NoC与路由器设计人员的技术文献系统介绍基于电路交换的NoC路由器设计与实现针对传统总线结构下的片上通信瓶颈给出面向无线通信等有保障服务场景的完整方案。资源为1个PDF文件共696KB内容涵盖NoC网络模型、电路交换原理、路由器功能定义、接口与通路划分、Verilog HDL代码实现以及Altera FPGA硬件验证等目录层次清晰便于按章节查阅。目前已有134人学习适合集成电路、计算机体系结构方向的工程师、高校师生及研究者参考。读者可从中获得支持5×5端口、每端口9.6Gbit/s、总交换容量48Gbit/s、100%无阻塞交换并可配置为多播/广播模式的路由器完整设计思路与实现细节对理解NoC路由器设计、确定性路由策略、Verilog建模和FPGA原型验证均具实用价值。该设计面向需要保证吞吐量和低延迟的无线通信场景为克服纳米级SoC设计中的带宽和接口标准化难题提供了有益借鉴。 做多核SoC互连设计的朋友应该都有过这种经历核心数一超过八个全局总线的仲裁优先级就难写仿真波形拉到十万个cycle到处都在排队等带宽。我自己在项目里切到NoCNetwork-on-Chip片上网络之后一开始直接用了主流的分组交换方案跑起来确实比总线强很多。但后来遇到一批以长块连续传输为主的应用才发现分组交换让每个数据包在每个路由器里反复查表、排队、仲裁白白浪费了大量面积和功耗。于是就有了这篇文章的主角一个基于电路交换Circuit Switching的NoC路由器。它是我自己从零开始设计、写RTL、做验证、跑FPGA综合的一套完整模块。下面我把整个设计过程包括架构、握手时序、仲裁策略、实测数据和调试教训从头到尾讲一遍。1. 为什么电路交换方案值得动手做一次在展开架构之前先把动因讲清楚。搞懂为什么用方案A而不是方案B比直接看RTL更重要。1.1 全局总线的共享介质模型撑不住传统SoC的第一反应是AMBA总线。AHB/AXI在四五个主设备的时候表现尚可一旦扩展到八个以上主设备仲裁矩阵和多路选择器的逻辑深度会明显增长时钟频率往下掉。更麻烦的是总线带宽被所有主设备共享只要有两个高带宽模块同时发起长时间传输总线上就会出现严重的拥塞。这不是靠把数据位宽从64bit加到128bit就能解决的因为限制上限的是共享介质的冲突概率而不是物理线宽。总线本质上是一个“所有节点共享一份介质”的模型想要线性扩展只能换互连拓扑。1.2 分组交换的主流方案在两种场景下很吃亏NoC替代总线之后内部采用分组交换是主流。每个数据包被切成flit每经过一个路由器都要走“路由计算、写入FIFO、排队仲裁、穿crossbar”这套流程。这套流程的优点是流量适应性强能被不同应用动态均衡缺点是每个端口都要放一堆FIFO面积和功耗都不低而且只要有拥塞端到端延迟就会明显抖动。这导致它面对两类场景时很吃亏一是流式长块传输比如DMA搬运、视频编解码模块和加速器之间的连续数据流二是对延迟抖动有硬性要求的应用比如实时信号处理、同步采样控制。这些场景里数据说白了就是一连串长flit流根本不需要在每个路由器里重新决策。1.3 电路交换的做法把决策前移把传输变直通电路交换的思路完全不同。数据开始传输前先发一个连接建立请求沿途路由器只配置一次交叉开关把源端到目的端的物理路径锁定配置完成之后路径上所有路由器不再介入数据流向判断数据一拍一个flit地从交叉开关上直通过去传完再发一个释放信号把交叉开关资源收回来。这样省掉了FIFO、省掉了逐拍仲裁延迟抖动也几乎为零。代价是需要额外的建立时间而且链路一旦占用就是独占的不适合短小密集的突发流量。所以我不认为电路交换能全面替代分组交换但把它用在对的场景里收益非常可观。这次做的4x4 mesh电路交换NoC路由器就是为了把“对场景下的收益”这组数据量化出来。2. 路由器整体架构哪些模块是电路交换独有的2.1 五端口路由器的模块组成我采用的是二维mesh里最经典的五端口路由器结构东、西、南、北四个网络端口加上一个本地处理单元PE端口。内部模块划分如下模块作用电路交换方案的特殊性输入端口控制器对输入信号采样、对齐和握手不需要大容量FIFO只需寄存器锁存路由计算单元RC根据目标坐标计算下一跳方向只在链路建立阶段工作传输阶段完全闲置链路控制单元LC维护每条链路的状态处理建立与释放这是电路交换的核心分组交换里没有交叉开关Crossbar把输入数据导向输出端口需要支持锁存配置配置后不再逐拍仲裁配置寄存器组记录端口占用情况、链路统计信息必须有用于异常排查和性能计数从表里能看出电路交换路由器和分组交换路由器最大的区别就是把“大容量FIFO逐拍仲裁器”换成了“链路控制单元可锁存的Crossbar”。这个思路听起来简单但代价是LC的逻辑必须足够严谨因为全网络的链路状态都依赖它维护。2.2 控制通路和数据通路一定要分开设计过程中一个教训是最初为了省布线和逻辑我把建立信号、ACK信号和数据flit放在同一套交叉开关里传输。结果在功能仿真里就出现了矛盾——链路建立是需要配置crossbar的可已建好的数据连接又占着crossbar不放两个操作直接冲突。后来我彻底分成两条通路控制通路用一条窄位总线专门传SETUP、TEARDOWN、ACK/NAK信令数据通路只在链路进入Active状态后才被使用。两条通路仍然同源时钟但逻辑上完全解耦。这种分层在电路交换里不是可选项而是必选项。如果你也在写这类设计建议第一步就把控制通路和数据通路分开定义不然后面重构成本很高。2.3 位宽选择与传输粒度的细节数据通路的位宽我选了64bit数据加8bit控制总共72bit每拍。选这个值主要考虑两点一是PE侧的AXI接口是64bit对齐后不用额外转换二是crossbar面积会随位宽超线性增长盲目加到128bit会让交换矩阵大出一倍还不止。传输粒度上电路交换不需要在传输过程中定义包的边界源端建好链路后可以连续发送任意长度的flit序列。不过我额外加了一个小功能SETUP请求里捎带期望传输的flit总数沿途路由器记录后用于自动释放。这样即使源端软件出错忘记发释放信号硬件也能在计数器归零后自动拆链避免链路悬挂。这个功能本来是顺手加的却在后面的调试中救了很多次。3. 链路建立与释放机制状态机和握手时序设计3.1 两阶段配置的链路建立流程链路建立我用的是“SETUP逐跳配置ACK逐跳确认”的两阶段方式。源端发出一份SETUP请求里面包含目的节点坐标X/Y、期望传输长度L和源端口号。SETUP沿路由算法指定的方向逐跳前进每一跳的中间路由器要完成三件事解析目的坐标并算出下一跳方向尝试把输入端口到对应输出端口在Crossbar上做一个临时连接连接成功就把SETUP继续转发失败则向源端方向回NAK。SETUP到达目的端后目的端不直接开传数据而是沿原路径逐跳回送ACK。沿途路由器收到ACK才把临时连接升格为正式连接。源端收到ACK后整条链路才算真正打通。这个“临时-正式”两阶段设计是为了防止部分路径已经配置好而另一部分还没配置时数据提前灌入导致中间节点状态错乱。3.2 输出端口状态机Idle、Setup、Active、Teardown每个输出端口对应一个链路控制单元LC内部状态机我设计成四个状态Idle表示端口空闲可被SETUP请求竞争Setup表示已有SETUP在路径上传送等待ACK或NAKActive表示链路已建立数据流直通crossbarTeardown表示正在回收crossbar资源等待释放完成。核心状态转移逻辑用Verilog描述大致是always (posedge clk or negedge rst_n) begin if (!rst_n) state IDLE; else begin case (state) IDLE: if (setup_granted) state SETUP; SETUP: if (ack_received) state ACTIVE; else if (nak_received) state IDLE; ACTIVE: if (teardown_req) state TEARDOWN; TEARDOWN: if (teardown_done) state IDLE; endcase end end项目里还加了超时、阻塞重试等条件主干就是这四态的循环。每种跳转都对应协议里的一个明确信号排查问题的时候逻辑非常清晰。3.3 释放链路与异常兜底释放链路最常规的做法是源端在最后一个flit发出后沿原路径发TEARDOWN信号沿途路由器收到后恢复crossbar配置回到Idle。但只做这一点是不够的我在三个位置加了保险。第一是长度计数器建立时记录的flit总数在Active状态每个cycle递减减到0就自动发起内部释放解决源端忘记发TEARDOWN的问题。第二是看门狗每条链路建立时记录时间戳如果传输计数归零后一段时间仍未完成释放就强制清除crossbar配置并上报异常计数。第三是部分释放如果某个TEARDOWN在中间跳丢失由中间跳的LC在超时后主动撤销自己的配置避免链路残留。加上这套兜底之后“链路悬挂”这类故障在长时间随机测试里基本没有再见过了。4. 路由计算与仲裁竞争场景下的细节处理4.1 为什么选XY维序路由在mesh网络里可用的路由算法不少我这个项目选XY维序路由理由有两个。第一是正确性XY是维序路由的一种任何一条从源到目标的路径都是先沿X方向走到目标列、再沿Y方向走到目标行在拓扑里是一条没有回头的单调折线。既然是单调的就不存在多条链路的持有-等待环路天然不会死锁不需要引入额外虚拟通道。第二是硬件实现简单下一跳计算就是坐标比较加符号判断一小组合逻辑就能完成。相比之下自适应路由需要实时感知邻居拥塞状态硬件复杂度、验证工作量都会大不少。对于第一版验证电路交换收益的项目XY是性价比最高的起点。4.2 建立仲裁先到先服务加原子锁定多个SETUP请求同时到达同一个输出端口是必然事件。我在每个输出端口放了独立仲裁器输入来自四个方向的SETUP请求和本地PE的SETUP请求。仲裁策略是“先到先服务原子锁定”一旦某个SETUP在仲裁中胜出该输出端口立即进入锁定状态后续SETUP不能抢占它直到该连接成功进入Active或因为NAK被释放。这个原子锁定非常关键没有它就会出现两个SETUP在crossbar上各占一半、互相错位的尴尬场景。同一拍到达的多个SETUP则用一个轮询指针决定优先级每完成一次成功建立指针后移一格这样长时间运行下每个方向都有建立机会不会出现某方向被饿死的现象。4.3 建立冲突时有限阻塞加超时回滚当SETUP走到中间一跳发现输出端口已经被其他链路锁定怎么处理很影响整体表现。最直接的想法是立刻回NAK让源端整体重来。这种做法的缺点是竞争激烈时会导致大量反复建立尝试网络有效利用率会降低。我最后采用的是“有限阻塞超时回滚”被阻塞的SETUP在节点内暂时寄存LC周期性检查目标端口一旦释放就继续往下游传只有阻塞超过设定周期我设为16个cycle才回NAK。好处是竞争高峰过去后多数被阻塞的SETUP能自动续传完成而不是把压力全部压回源端。实测中这个策略显著提高了多流并发场景下的建立成功率代价只是每个中间节点多存一份SETUP上下文的寄存器组开销可以接受。5. 验证与实测延迟、吞吐和面积的真实收益5.1 验证平台与三类测试用例验证环境搭在SystemVerilog加VCS下核心是cycle级参考模型加自动比较器。参考模型知道每一对源目的节点之间的最短路径也知道每跳建立、传输、释放各消耗几个cycle所以能精确预测每个数据flit应该在哪个节点哪个cycle出现。比较器在每个cycle把参考模型输出和DUT输出做逐位比对出现flit丢失、错位、顺序颠倒会立即打印错误信息。测试用例分三类单流长传输两个节点连续传4096个flit验证完整链路正确性多流竞争模式随机选择多对源目的节点以不同注入率同时发起连接重点验证仲裁和公平性对角线长路径让流量跨越大半个网络压测长路径下的建立延迟和释放恢复。所有用例最后都跑通连续200万cycle没有死锁和链路悬挂。5.2 延迟与吞吐的实测表现性能数字方面在4x4 mesh中一条跨3跳的典型路径从源端发出SETUP到收到ACK建立延迟实测大约6到8个cycle其中包含每跳的流水级和仲裁等待。建立完成后数据每个cycle稳定输出一个flit不是平均一拍而是每一拍都在传。这是电路交换和分组交换最本质的区别路径建好后网络中不再存在缓冲排队这个动作。多流极限竞争时建立延迟会明显上升最差能到60个cycle以上但一旦建立成功传输带宽仍然保持每拍一个flit不会因为网络拥塞而抖动。对实时性要求高的场景“建立阶段的等待可预测、传输阶段零抖动”这两点非常有用。需要说明的是不同工艺、不同频率下这些数字会浮动但反映的设计特性是一致的。5.3 与分组交换对比面积和功耗的差距为了量化收益我拿之前做过的同规格分组交换路由器做参照在同一个FPGA上做了综合对比。分组交换方案每个端口要放深度8个flit的FIFO加上逐拍仲裁逻辑LUT和FF的消耗明显更大电路交换方案把FIFO彻底去掉仲裁逻辑只在建立阶段工作单路由器面积可以做到分组交换的一半以下。功耗的差距更明显空闲状态下分组交换的FIFO指针、仲裁器状态都在随时钟翻转电路交换处于Active的链路只是保持crossbar导通没有额外翻转动态功耗降了一大截。当然这个对比不是单方面碾压。如果流量是大量短包突发比如几十个3-flit的包来回打链路建立开销会被摊到很短的传输段上电路交换的优势会消失甚至变成劣势。它的适用面很明确长块连续传输占主流的场景。6. 三个把我坑到凌晨的调试问题6.1 配置寄存器跨时钟域导致端口假占用第一个坑在配置寄存器组的跨时钟域处理上。最初我把CPU配置总线的信号直接连到核心逻辑没有做同步。结果长时间随机测试里偶尔会出现“某端口明明没有链路却一直显示被占用”的假象。定位到最后是配置总线上的异步写信号和核心逻辑时钟沿发生竞争违反了触发器的建立保持时间把中间态捕获进了寄存器。修复方案就是标准的两级触发器同步器加请求-应答握手。这里想提醒一句不要因为两个时钟标称同频就跳过跨时钟域处理只要相位关系不确定亚稳态就一定会存在。6.2 释放慢半拍导致的链路残留第二个坑很隐蔽。在Teardown状态里我最开始先恢复crossbar连接再置teardown_done信号。功能仿真一切正常但时序仿真里出现偶发窜扰新的链路建立后数据偶尔会打进上一个连接的输出端口。反复对比波形才发现crossbar连接使能的撤销路径上有一级组合延迟teardown_done已经置位但crossbar连接实际还保持半拍。修复方法不复杂在Teardown里强制插入一个额外cycle的延时确认crossbar连接真正撤销后再回到Idle。代价是释放时间增加一个cycle但彻底消除了残留连接问题。这类问题功能仿真查不出来必须靠时序仿真去暴露。6.3 建立路径组合逻辑过长频率上不去第三个坑在综合阶段。SETUP请求的信号路径要穿越“地址解析-路由计算-输出仲裁-crossbar配置”这条长组合链在4x4网络里还要跨多级路由器关键路径严重超预期。最初目标250MHz实测只能跑到180MHz左右。解决办法是把建立流程拆成三个流水级第一级做地址解析和路由计算第二级做仲裁判定第三级做crossbar配置。流水化之后建立延迟多了2个cycle但关键路径大幅缩短最终频率稳定在250MHz以上。对类似的建立路径时序问题流水化几乎是唯一靠谱的出路靠堆组合逻辑优化很难拿到理想结果。这三个问题有个共同点功能仿真都查不出来全部是时序仿真或综合后才暴露的。所以做类似项目时尽早引入时序仿真和综合约束别等RTL全部写完再集中回头处理那时候定位成本会高很多。如果重来一遍我会在第一天就把控制通路的时序约束单独提出来。电路交换NoC的核心不在数据通道而在控制通道控制通道的时序质量直接决定了整个网络能稳定跑多快。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进