理解NoC路由器中交叉开关(Crossbar)的设计原理,掌握全连接、多播和部分连接交叉开关的实现。
交叉开关(Crossbar Switch)是路由器中连接输入端口到输出端口的核心互连结构。N×N交叉开关可以实现任意输入到任意输出的同时连接,是路由器面积和延迟的重要来源。
每个输出端口可以从任意输入端口接收数据。N×N交叉开关需要N²个交叉点,面积O(N²)。
限制每个输出端口只能从部分输入端口接收数据。减少交叉点数量,降低面积,但限制连接灵活性。
支持一个输入同时连接到多个输出。用于广播和多播操作,但面积更大。
| 端口数N | 交叉点数 | 面积(相对) | 关键路径 |
|---|---|---|---|
| 5 | 25 | 1× | 低 |
| 7 | 49 | 2× | 中 |
| 9 | 81 | 3.5× | 高 |
// 5×5全连接交叉开关
module crossbar_5x5 #(
parameter DATA_WIDTH = 32,
parameter NUM_PORTS = 5
)(
// 输入端口
input logic [DATA_WIDTH-1:0] in_data [0:NUM_PORTS-1],
// 选择信号: 每个输出选择一个输入
input logic [$clog2(NUM_PORTS)-1:0] sel [0:NUM_PORTS-1],
// 输出端口
output logic [DATA_WIDTH-1:0] out_data [0:NUM_PORTS-1]
);
// 每个输出端口一个MUX
always_comb begin
for (int o = 0; o < NUM_PORTS; o++) begin
case (sel[o])
3'd0: out_data[o] = in_data[0];
3'd1: out_data[o] = in_data[1];
3'd2: out_data[o] = in_data[2];
3'd3: out_data[o] = in_data[3];
3'd4: out_data[o] = in_data[4];
default: out_data[o] = {DATA_WIDTH{1'b0}};
endcase
end
end
endmodule
// 参数化N×N交叉开关
module crossbar_nxn #(
parameter DATA_WIDTH = 32,
parameter NUM_PORTS = 5
)(
input logic [DATA_WIDTH-1:0] in_data [0:NUM_PORTS-1],
input logic [$clog2(NUM_PORTS)-1:0] sel [0:NUM_PORTS-1],
output logic [DATA_WIDTH-1:0] out_data [0:NUM_PORTS-1]
);
for (genvar o = 0; o < NUM_PORTS; o++) begin : gen_output
always_comb begin
out_data[o] = in_data[sel[o]];
end
end
endmodule
交叉开关模块通过Verilator验证。
练习1:实现支持多播的交叉开关。
练习2:分析7端口路由器的交叉开关面积。
练习3:设计部分连接交叉开关,只允许相邻端口互连。
你已掌握交叉开关的设计与实现!
交叉开关的设计涉及多个维度,从简单到复杂:
5×5交叉开关的每个输出端口是一个5选1 MUX。关键路径是MUX的选择延迟。
当端口数N较大时(N>8),单级交叉开关面积和延迟过大。使用多级Clos网络:
一个3级Clos网络C(n,m,r)有r个入线组(每组n条)和r个出线组,中间级m个开关。
交叉开关的功耗主要来自数据线的翻转。优化方法:
仲裁器的输出直接驱动交叉开关的选择信号。将两者联合设计可以优化关键路径:
// 联合仲裁+交叉开关
module arbiter_xbar #(
parameter DATA_WIDTH = 32,
parameter NUM_PORTS = 5
)(
input logic [DATA_WIDTH-1:0] in_data [0:NUM_PORTS-1],
input logic [NUM_PORTS-1:0] req [0:NUM_PORTS-1],
output logic [DATA_WIDTH-1:0] out_data [0:NUM_PORTS-1],
output logic [NUM_PORTS-1:0] gnt [0:NUM_PORTS-1]
);
// 仲裁+选择一体化: 减少一级MUX延迟
for (genvar o = 0; o < NUM_PORTS; o++) begin : gen_out
always_comb begin
out_data[o] = {DATA_WIDTH{1'b0}};
gnt[o] = {NUM_PORTS{1'b0}};
for (int i = 0; i < NUM_PORTS; i++) begin
if (req[o][i]) begin
out_data[o] = in_data[i];
gnt[o][i] = 1'b1;
end
end
end
end
endmodule
在大规模NoC(8×8以上)中,交叉开关成为关键瓶颈。以下高级设计技术可以缓解:
支持一个输入同时连接到多个输出。在广播/多播流量(如一致性消息)中特别有用:
// 多播交叉开关
module multicast_xbar #(
parameter DATA_WIDTH = 32,
parameter NUM_PORTS = 5
)(
input logic [DATA_WIDTH-1:0] in_data [0:NUM_PORTS-1],
input logic [NUM_PORTS-1:0] out_en [0:NUM_PORTS-1], // 每输出的使能
input logic [$clog2(NUM_PORTS)-1:0] sel [0:NUM_PORTS-1],
output logic [DATA_WIDTH-1:0] out_data [0:NUM_PORTS-1]
);
for (genvar o = 0; o < NUM_PORTS; o++) begin
assign out_data[o] = out_en[o] ? in_data[sel[o]] : {DATA_WIDTH{1'b0}};
end
endmodule
为提高可靠性,可以在交叉开关中添加冗余行/列。当某个交叉点故障时,切换到冗余路径。面积增加10-20%,但可靠性显著提高。
以下是一个完整的5×5交叉开关设计案例,包含仲裁和选择逻辑:
// 完整5×5交叉开关(含仲裁)
module full_crossbar_5x5 #(
parameter DATA_WIDTH = 32
)(
input logic clk, rst_n,
input logic [DATA_WIDTH-1:0] in_data [0:4],
input logic [4:0] in_req [0:4], // 每输入请求的输出端口
input logic in_valid [0:4],
output logic in_ready [0:4],
output logic [DATA_WIDTH-1:0] out_data [0:4],
output logic out_valid[0:4],
input logic out_ready[0:4]
);
// 每个输出端口的请求向量
logic [4:0] out_req [0:4]; // out_req[o][i] = 输入i请求输出o
logic [4:0] out_gnt [0:4]; // out_gnt[o][i] = 输入i获得输出o
// 请求转换: in_req[i] → out_req[o]
always_comb begin
for (int o = 0; o < 5; o++) begin
out_req[o] = 5'b0;
for (int i = 0; i < 5; i++)
if (in_valid[i] && in_req[i] == o)
out_req[o][i] = 1'b1;
end
end
// 每个输出端口的RR仲裁
for (genvar o = 0; o < 5; o++) begin : gen_arb
logic [2:0] last_gnt;
always_ff @(posedge clk or negedge rst_n)
if (!rst_n) last_gnt <= 0;
else if (|out_gnt[o]) last_gnt <= last_gnt; // 简化
// 简化仲裁: 固定优先级
assign out_gnt[o] = out_req[o] & ~(out_req[o] - 1);
end
// 交叉开关连接
for (genvar o = 0; o < 5; o++) begin : gen_xbar
always_comb begin
out_data[o] = {DATA_WIDTH{1'b0}};
out_valid[o] = 1'b0;
for (int i = 0; i < 5; i++) begin
if (out_gnt[o][i]) begin
out_data[o] = in_data[i];
out_valid[o] = in_valid[i];
end
end
end
end
// 输入ready(收到gnt且输出ready)
for (genvar i = 0; i < 5; i++) begin : gen_ready
always_comb begin
in_ready[i] = 1'b0;
for (int o = 0; o < 5; o++)
if (out_gnt[o][i] && out_ready[o])
in_ready[i] = 1'b1;
end
end
endmodule
交叉开关的时序是路由器频率的关键制约因素:
5×5 32-bit交叉开关的关键路径:仲裁器输出→MUX选择→输出驱动。总计约0.5-0.8ns(16nm工艺)。
交叉开关面积 = N² × W × 每bit开关面积。5端口32-bit → 25×32 = 800个开关点。使用MUX实现约1500μm²,使用三态实现约800μm²。
以下是一个5×5 32-bit交叉开关的完整综合报告:
动态功耗: 约15μW/MHz(50%翻转率),静态功耗: 约3μW。在1GHz下总功耗约18mW,占路由器功耗的12%。
| 指标 | 优化前 | 优化后(分体+三态) | 改善 |
|---|---|---|---|
| 面积 | 2675μm² | 1800μm² | -33% |
| 延迟 | 0.35ns | 0.25ns | -29% |
| 功耗 | 18mW | 12mW | -33% |
主流商用NoC IP中交叉开关的实现方式: