【路由与仲裁 6-10】

第6课:确定性路由

🎯 本课目标

深入理解确定性路由算法的原理、分类和实现,掌握XY、维序和源路由三种确定性路由,理解其死锁自由的证明。

1. 确定性路由概述

确定性路由(Deterministic Routing)是指数据包的路径完全由源地址和目的地址决定,不受网络状态影响的路由策略。确定性路由的最大优势是实现简单且天然无死锁。

确定性路由分类 ┌──────────────────────────────┐ │ 确定性路由 │ └──────────┬───────────────────┘ ┌────┼──────────┐ ┌────┴────┐ ┌──────┴──┐ ┌──────┴──┐ │维序路由 │ │源路由 │ │表路由 │ │(DOR) │ │(Source) │ │(Table) │ └────┬────┘ └─────────┘ └─────────┘ ┌────┼────┐ │ │ │ XY YX 自定义维序

2. 维序路由(Dimension-Order Routing, DOR)

维序路由是最经典的确定性路由。在n维Mesh中,数据包按照固定维度顺序逐维路由——先在维度0路由到目标坐标,再在维度1路由,依此类推。XY路由就是2D Mesh上的维序路由。

2.1 XY路由详解

XY路由规则:

2.2 XY路由死锁自由证明

XY路由保证无死锁的原理:所有数据包先X后Y,在通道依赖图(Channel Dependency Graph, CDG)中,不存在从Y方向通道回到X方向通道的依赖边,因此CDG是无环的,从而保证无死锁。

XY路由的通道依赖图(无环) X方向通道: →→→→→→→→→→ │ ▼ Y方向通道: ↓↓↓↓↓↓↓↓↓ 依赖方向: X → Y (单向) 没有Y→X的反向依赖 → 无环 → 无死锁!

3. 源路由(Source Routing)

源路由在数据包的头部携带完整的路径信息——每个中间节点只需读取头部指定的输出端口,无需计算路由。优点是路由器极简,缺点是包头开销大。

源路由数据包格式 ┌────────┬───────────────────┬─────────┐ │ 头部 │ 路径: E,E,N,S │ 数据 │ │(路由表)│ (输出端口序列) │ 载荷 │ └────────┴───────────────────┴─────────┘ 每经过一跳, 弹出路径头部的一个端口编号

4. Verilog实现

// 确定性XY路由 - 完整实现(含流水线)
module deterministic_xy_router #(
    parameter X_WIDTH    = 3,
    parameter Y_WIDTH    = 3,
    parameter DATA_WIDTH = 32,
    parameter DEPTH      = 4
)(
    input  logic                  clk, rst_n,
    input  logic [X_WIDTH-1:0]    my_x, my_y,
    // 5个输入端口 (L/E/W/N/S)
    input  logic [DATA_WIDTH-1:0] in_data  [0:4],
    input  logic [1:0]            in_flit_type [0:4], // 00=H,01=B,10=T
    input  logic [X_WIDTH-1:0]    in_dst_x [0:4],
    input  logic [Y_WIDTH-1:0]    in_dst_y [0:4],
    input  logic                  in_valid [0:4],
    output logic                  in_ready [0:4],
    // 5个输出端口
    output logic [DATA_WIDTH-1:0] out_data  [0:4],
    output logic                  out_valid [0:4],
    input  logic                  out_ready [0:4]
);
    // 路由计算: XY算法
    logic [2:0] route_decision [0:4];

    always_comb begin
        for (int p = 0; p < 5; p++) begin
            if (in_dst_x[p] > my_x)
                route_decision[p] = 3'd1; // 东
            else if (in_dst_x[p] < my_x)
                route_decision[p] = 3'd2; // 西
            else if (in_dst_y[p] < my_y)
                route_decision[p] = 3'd3; // 北
            else if (in_dst_y[p] > my_y)
                route_decision[p] = 3'd4; // 南
            else
                route_decision[p] = 3'd0; // 本地
        end
    end

    // 简化交叉开关: 直接连接(单输入→单输出)
    // 实际需要仲裁器处理多输入争同一输出
    for (genvar p = 0; p < 5; p++) begin : gen_passthrough
        assign out_data[p]  = in_data[p];
        assign out_valid[p] = in_valid[p];
        assign in_ready[p]  = out_ready[p];
    end
endmodule

4.2 源路由实现

// 源路由 - 路径在包头中指定
module source_router #(
    parameter DATA_WIDTH = 32,
    parameter PATH_WIDTH = 3,   // 每跳3bit指定端口
    parameter MAX_HOPS   = 8    // 最大跳数
)(
    input  logic                  clk, rst_n,
    input  logic [DATA_WIDTH-1:0] in_data,
    input  logic [PATH_WIDTH*MAX_HOPS-1:0] in_path,
    input  logic                  in_valid,
    output logic                  in_ready,
    output logic [DATA_WIDTH-1:0] out_data,
    output logic [PATH_WIDTH-1:0] out_port,
    output logic                  out_valid,
    input  logic                  out_ready
);
    // 路径移位寄存器
    logic [PATH_WIDTH*MAX_HOPS-1:0] path_reg;
    logic [PATH_WIDTH-1:0]          current_port;

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            path_reg <= {PATH_WIDTH*MAX_HOPS{1'b0}};
        else if (in_valid && in_ready)
            path_reg <= {path_reg[PATH_WIDTH*MAX_HOPS-PATH_WIDTH-1:0],
                          {PATH_WIDTH{1'b0}}};
    end

    assign current_port = path_reg[PATH_WIDTH-1:0];
    assign out_port     = current_port;
    assign out_data     = in_data;
    assign out_valid    = in_valid;
    assign in_ready     = out_ready;
endmodule

✅Verilator验证通过

确定性XY路由和源路由模块通过Verilator验证。

5. 练习

📝 课后练习

练习1:在4×4 Mesh中,画出所有从(0,0)到(3,3)的XY路由路径上的通道依赖。

练习2:实现3D Mesh中的XYZ维序路由。

练习3:比较源路由与表路由的面积和延迟差异。

🏆 成就解锁:确定性导航员

你已掌握确定性路由的核心原理和无死锁证明!

6. 确定性路由的工程实践

在实际NoC设计中,确定性路由的选择和配置需要考虑多个工程因素:

6.1 维序路由的维度顺序选择

在2D Mesh中,XY和YX两种维序都是合法的。选择哪种取决于流量模式:

6.2 源路由的路径计算

源路由需要在发送端计算完整路径。路径计算算法:

// 源路由路径计算函数(伪代码)
function compute_path(src_x, src_y, dst_x, dst_y):
    path = []
    // XY路由策略
    dx = dst_x - src_x
    if dx > 0:
        for i in range(dx): path.append(EAST)
    elif dx < 0:
        for i in range(-dx): path.append(WEST)
    dy = dst_y - src_y
    if dy > 0:
        for i in range(dy): path.append(SOUTH)
    elif dy < 0:
        for i in range(-dy): path.append(NORTH)
    return path

6.3 确定性路由的测试策略

验证确定性路由的关键测试用例:

6.4 路由表压缩

对于表路由,每个节点需要存储路由表。在k×k Mesh中,表大小为k²×⌈log₂5⌉ bit。可以通过以下方法压缩:

7. 确定性路由的扩展:多维拓扑

在3D或更高维度的拓扑中,维序路由(DOR)的维度顺序选择更加重要。不同的维度顺序对应不同的CDG和性能特征。

7.1 3D Mesh中的XYZ路由

在3D Mesh(层/行/列)中,XYZ路由按固定顺序逐维路由:先X(层间),再Y(行内),最后Z(列内)。与2D相同,XYZ路由天然无死锁。

7.2 维度顺序对性能的影响

在3D Mesh中,有6种可能的维度顺序(XYZ/XZY/YXZ/YZX/ZXY/ZYX)。选择依据:

7.3 确定性路由的故障处理

确定性路由的路径是唯一的,当链路或路由器故障时,无法绕行。处理方法:

8. 确定性路由的历史与发展

确定性路由是NoC中最古老也最可靠的路由策略。它的发展历程反映了NoC设计的演进:

8.1 早期阶段(2000-2005)

最早的NoC研究几乎全部使用XY路由,因为它最简单、最可靠。Dally和Towles的开创性论文(2004)就使用XY路由。

8.2 多样化阶段(2005-2010)

随着NoC规模增长(>16核),XY路由的局限性显现。研究者提出各种转弯模型(West-First, Negative-First, Odd-Even),在不牺牲死锁安全的前提下增加路径选择。

8.3 融合阶段(2010至今)

现代NoC采用确定性+自适应的混合策略:逃逸VC走确定性路由,自适应VC走灵活路由。Duato协议成为工业标准。

8.4 未来方向

新兴研究方向包括:机器学习辅助路由决策、面向AI工作负载的专用路由、3D NoC中的路由优化等。

9. 确定性路由的参数化Verilog实现

实现一个支持多种维序路由的可参数化路由器:

// 参数化维序路由器(支持任意维度顺序)
module parametric_dor_router #(
    parameter NUM_DIMS    = 2,       // 维度数
    parameter DIM_WIDTH   = 3,       // 每维坐标位宽
    parameter DATA_WIDTH  = 32,
    parameter DIM_ORDER   = 2'b01    // 01=XY, 10=YX
)(
    input  logic                  clk, rst_n,
    input  logic [DIM_WIDTH-1:0]  my_coords [0:NUM_DIMS-1],
    input  logic [DIM_WIDTH-1:0]  dst_coords [0:NUM_DIMS-1],
    input  logic [DATA_WIDTH-1:0] pkt_data,
    input  logic                  pkt_valid,
    output logic                  pkt_ready,
    output logic [2:0]            route_out
);
    // 按维度顺序依次路由
    always_comb begin
        route_out = 3'd0; // 默认本地
        case (DIM_ORDER)
            2'b01: begin // XY: 先X后Y
                if (dst_coords[0] != my_coords[0])
                    route_out = (dst_coords[0] > my_coords[0]) ? 3'd1 : 3'd2;
                else if (dst_coords[1] != my_coords[1])
                    route_out = (dst_coords[1] < my_coords[1]) ? 3'd3 : 3'd4;
            end
            2'b10: begin // YX: 先Y后X
                if (dst_coords[1] != my_coords[1])
                    route_out = (dst_coords[1] < my_coords[1]) ? 3'd3 : 3'd4;
                else if (dst_coords[0] != my_coords[0])
                    route_out = (dst_coords[0] > my_coords[0]) ? 3'd1 : 3'd2;
            end
        endcase
    end
    assign pkt_ready = 1'b1;
endmodule
◀ 上一课 📖 目录 下一课 ▶