【IC】NoC设计入门 -- 路由逻辑
“砖块”(FIFO)已经造好,现在我们来造“GPS导航仪”。
这就是 “路由逻辑” (Routing Logic, RC) 模块。
这是我们整个“十字路口”(路由器)的“大脑”。
1. 🎯 “路由逻辑” (RC) 的任务
- 比喻: “问询处”或“GPS导航仪”。
- 功能: 它的工作极其专注——“只做计算,不做存储”。
- 输入: 1. “我”的坐标 (e.g., 我是
(1, 1)号路口)
2. “车队”的目的地坐标 (e.g., 它想去(3, 2)) - 输出: “计算结果” (e.g., “它必须从‘东出口’走!”)
关键的工程概念:
这个模块不需要时钟 (clk)!它是一个纯组合逻辑 (Combinational Logic) 模块。
为什么?因为“计算”应该是**“即时”的。当“输入”(目的地)一确定,“输出”(该走哪个门)就应该立刻**(在几个纳秒的门延迟后)被计算出来。我们不希望在这个决策上多花一个时钟周期。
我们将使用 always @(*)(Verilog中的“组合逻辑”标准写法)来实现它。
2. 📐 模块“蓝图” (Module Interface)
在设计中,我们必须给“坐标”定义一个位宽。假设我们的“城市” (Mesh) 最大不超过 16x16。
- 16=2416 = 2^416=24,所以我们需要 4位 (bit) 来表示 X 坐标,4位来表示 Y 坐标。
/** 模块:路由逻辑 (Routing Logic)* 功能:实现确定性的 (Deterministic) XY 路由算法* 类型:纯组合逻辑*/
module routing_logic #(// 参数:定义坐标的位宽parameter COORD_WIDTH = 4
) (// ---- 输入 ----// “车队”的目的地坐标 (来自 Head Flit)input [COORD_WIDTH-1:0] i_dest_x,input [COORD_WIDTH-1:0] i_dest_y,// “我”自己的坐标 (这个路由器被实例化时,它的“身份”)input [COORD_WIDTH-1:0] i_curr_x,input [COORD_WIDTH-1:0] i_curr_y,// ---- 输出 ----// “计算结果”:你想去哪个出口?(5个出口,1-bit 信号)// 这是“请求信号”,它们将发往“仲裁器”(Arbiter)output reg o_req_north, // 1'b1 = 我想去北output reg o_req_south, // 1'b1 = 我想去南output reg o_req_east, // 1'b1 = 我想去东output reg o_req_west, // 1'b1 = 我想去西output reg o_req_local // 1'b1 = 目的地就是我,我要“弹出”(eject)
);// TODO: 在这里实现 XY 路由的 "if-else" 逻辑...endmodule
3. 🧠 核心逻辑实现 (XY 路由算法)
现在我们来填充 TODO 部分。我们将用Verilog实现我们第二阶段学到的“XY路由”规则。
规则:
- 规则0 (最高优先级): 目的地就是“我”吗?
- 如果是,请求 “本地 (Local)” 出口。
- 规则1 (X方向): 目的地和“我”的 X 坐标不相等吗?
- 如果
dest_x > curr_x,请求 “东 (East)” 出口。 - 如果
dest_x < curr_x,请求 “西 (West)” 出口。
- 如果
- 规则2 (Y方向): X 坐标已经相等,但 Y 坐标不相等吗?
- 如果
dest_y > curr_y,请求 “北 (North)” 出口。 - 如果
dest_y < curr_y,请求 “南 (South)” 出口。
- 如果
下面是代码:
/** 模块:路由逻辑 (Routing Logic)* 功能:实现确定性的 (Deterministic) XY 路由算法* 类型:纯组合逻辑*/
module routing_logic #(parameter COORD_WIDTH = 4
) (// ---- 输入 ----input [COORD_WIDTH-1:0] i_dest_x,input [COORD_WIDTH-1:0] i_dest_y,input [COORD_WIDTH-1:0] i_curr_x,input [COORD_WIDTH-1:0] i_curr_y,// ---- 输出 ----output reg o_req_north,output reg o_req_south,output reg o_req_east, output reg o_req_west, output reg o_req_local
);// ---------------------------------------------// 核心时序逻辑:使用 "always @(*)" 来表示这是一个“组合逻辑”// ---------------------------------------------always @(*) begin// --- 关键安全措施:防止产生“锁存器”(Latch) ---// 在所有 "if" 判断开始前,先把所有出口的请求都设为 0 (不请求)o_req_north = 1'b0;o_req_south = 1'b0;o_req_east = 1'b0;o_req_west = 1'b0;o_req_local = 1'b0;// --- 开始执行 XY 路由算法 ---// 规则 0:目的地就是“我”吗?// (注意:用 `==` 比较多位宽)if ( (i_dest_x == i_curr_x) && (i_dest_y == i_curr_y) ) begino_req_local = 1'b1; // 请求“本地”出口end// 规则 1:X 坐标不匹配吗?(先走 X 方向)// (注意:用 `else if`)else if (i_dest_x > i_curr_x) begino_req_east = 1'b1; // 请求“东”出口endelse if (i_dest_x < i_curr_x) begino_req_west = 1'b1; // 请求“西”出口end// 规则 2:X 坐标已匹配,该走 Y 方向了else if (i_dest_y > i_curr_y) begino_req_north = 1'b1; // 请求“北”出口endelse if (i_dest_y < i_curr_y) begino_req_south = 1'b1; // 请求“南”出口end// 默认 'else':// 如果出现 (dest_x == curr_x) 且 (dest_y == curr_y)// 这种情况在 规则0 已经被捕获了。// 所以我们不需要一个 'else',因为默认值 (全0) 已经处理了// 所有未明确的情况(虽然在这个设计里所有情况都已被覆盖)。endendmodule
🔍 代码解析
-
always @(*)- 这是“圣杯”。它告诉Verilog编译器:“请把这里面的逻辑实现为纯组合逻辑(只用与门、或门、非门、Mux)”。
- “
*” (星号) 的意思是:always块内任何一个input(如i_dest_x) 发生变化,就立刻重新计算整个块。 - 这完美地模拟了我们的“GPS”——你一输入新地址,它就“立刻”算出新路线。
-
o_req_north = 1'b0;(默认值)- 这是
always @(*)块中最重要的“安全措施”。 - 为什么? 组合逻辑必须在所有可能的条件下,为所有输出都指定一个值。
- 如果你漏掉了(比如,你只写了
if (A) B=1,但没写else B=0),编译器会“懵”:“在 A 不为1时,B 应该保持它上一次的值吗?”——这种“保持”行为,就是**“锁存器”(Latch)**。 - Latch是时序设计中的“毒药”,它会带来无法预测的行为。
- 我们的办法: 在
always块的最开头,先把所有输出都清零。这样,我们只需要在if语句中把“想要的那个”设为1就行了,绝对安全,绝无 Latch。
- 这是
恭喜!你完成了第二块“砖”
你现在有了:
simple_fifo:“停车场”,它负责**“存储”** Flits(时序逻辑)。routing_logic:“GPS导航仪”,它负责**“计算”** 路径(组合逻辑)。
我们如何把它们“连接”起来?
- 当一个“火车头”(Head Flit) 到达
simple_fifo的“队首”(output)时… - “输入端口”(Input Port)模块会把这个Flit的“目的地坐标”(
o_data[19:12]) 喂给routing_logic的i_dest_x/i_dest_y。 - 同时,
routing_logic的5个o_req_...输出信号,将作为“请求”,发给我们下一个要造的组件——“交通警察”。
