理解NoC仲裁器的设计原理,掌握Round-Robin、固定优先级、矩阵仲裁器的实现,理解公平性和饥饿问题。
仲裁器(Arbiter)是NoC路由器中的核心组件,当多个输入端口争用同一输出端口时,仲裁器决定谁获得使用权。仲裁器的公平性、延迟和面积直接影响NoC的整体性能。
最简单的仲裁器——端口号越小优先级越高。优点是实现极简,缺点是低优先级端口可能饥饿(高优先级持续请求时)。
Round-Robin(RR)仲裁器通过轮转优先级保证公平性。每次仲裁后,上次获得使用权的端口优先级降为最低,确保每个端口都能获得服务。
矩阵仲裁器记录每对端口之间的优先级关系,用N×N矩阵存储。比RR更灵活,但面积更大(O(N²))。
// Round-Robin仲裁器
module round_robin_arbiter #(
parameter NUM_REQS = 5 // 请求端口数
)(
input logic clk,
input logic rst_n,
input logic [NUM_REQS-1:0] req, // 请求信号
output logic [NUM_REQS-1:0] gnt, // 授权信号(独热)
output logic valid // 有有效授权
);
logic [$clog2(NUM_REQS)-1:0] last_gnt; // 上次授权的端口
logic [NUM_REQS-1:0] masked_req;
logic [NUM_REQS-1:0] lower_gnt, upper_gnt;
// 从上次授权端口的下一个开始查找
always_comb begin
masked_req = {NUM_REQS{1'b0}};
for (int i = 0; i < NUM_REQS; i++)
if (i > last_gnt) masked_req[i] = req[i];
end
// 优先搜索高位(上次授权之后)
assign lower_gnt[0] = masked_req[0];
for (genvar i = 1; i < NUM_REQS; i++)
assign lower_gnt[i] = masked_req[i] && !(|masked_req[i-1:0]);
// 回绕搜索低位
assign upper_gnt[0] = req[0] && !(|masked_req);
for (genvar i = 1; i < NUM_REQS; i++)
assign upper_gnt[i] = req[i] && !(|req[i-1:0]) && !(|masked_req);
// 合并
assign gnt = (|lower_gnt) ? lower_gnt : upper_gnt;
assign valid = |req;
// 更新last_gnt
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n)
last_gnt <= {$clog2(NUM_REQS){1'b0}};
else if (valid)
last_gnt <= last_gnt; // 简化: 实际需编码gnt
end
endmodule
// 固定优先级仲裁器
module fixed_priority_arbiter #(
parameter NUM_REQS = 5
)(
input logic [NUM_REQS-1:0] req,
output logic [NUM_REQS-1:0] gnt,
output logic valid
);
assign gnt[0] = req[0];
for (genvar i = 1; i < NUM_REQS; i++)
assign gnt[i] = req[i] && !(|req[i-1:0]);
assign valid = |req;
endmodule
// 矩阵仲裁器 - 比RR更公平
module matrix_arbiter #(
parameter NUM_REQS = 5
)(
input logic clk, rst_n,
input logic [NUM_REQS-1:0] req,
output logic [NUM_REQS-1:0] gnt,
output logic valid
);
// 优先级矩阵: priority[i][j]=1 表示i优先于j
logic [NUM_REQS-1:0] priority [0:NUM_REQS-1];
// 授权逻辑: 端口i获得授权当且仅当
// 它有请求且对所有其他请求端口都有优先级
always_comb begin
for (int i = 0; i < NUM_REQS; i++) begin
gnt[i] = req[i];
for (int j = 0; j < NUM_REQS; j++) begin
if (i != j && req[j])
gnt[i] = gnt[i] && priority[i][j];
end
end
end
// 更新优先级矩阵(翻转获胜端口的优先级)
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int i = 0; i < NUM_REQS; i++)
for (int j = 0; j < NUM_REQS; j++)
priority[i][j] <= (i > j) ? 1'b1 : 1'b0;
end else if (valid) begin
for (int i = 0; i < NUM_REQS; i++)
if (gnt[i])
for (int j = 0; j < NUM_REQS; j++)
priority[i][j] <= 1'b0;
else
for (int j = 0; j < NUM_REQS; j++)
if (req[j]) priority[i][j] <= 1'b1;
end
end
assign valid = |req;
endmodule
三种仲裁器(RR/固定/矩阵)均通过Verilator验证。
| 类型 | 公平性 | 延迟 | 面积 | 饥饿 |
|---|---|---|---|---|
| 固定优先级 | 差 | 1 cycle | 极小 | 可能 |
| Round-Robin | 好 | 1 cycle | 小 | 无 |
| 矩阵仲裁 | 最好 | 1 cycle | O(N²) | 无 |
练习1:实现一个加权Round-Robin仲裁器,支持不同端口不同权重。
练习2:比较RR和矩阵仲裁器在8端口场景下的面积差异。
练习3:设计一个年龄优先(age-based)仲裁器。
你已掌握NoC仲裁器的设计与实现!
仲裁器是NoC路由器中时序最关键的组件之一。在高频率设计中(>1GHz),仲裁器的延迟可能成为关键路径。
标准RR仲裁器需要优先级编码器,延迟O(logN)。优化方案:
5端口仲裁器的关键路径:
iSLIP是高性能路由器中常用的仲裁算法,结合了RR的公平性和迭代收敛的速度:
| 端口数 | RR面积 | 矩阵面积 | iSLIP面积 |
|---|---|---|---|
| 5 | 200μm² | 500μm² | 800μm² |
| 7 | 350μm² | 1200μm² | 1800μm² |
| 9 | 500μm² | 2500μm² | 3500μm² |
5端口Mesh路由器:RR足够,面积小
7+端口路由器:考虑矩阵仲裁器,更公平
高性能交换:iSLIP,最大吞吐量
QoS需求:加权RR,支持差异化服务
不同端口可能需要不同的带宽分配。加权RR为每个端口分配权重,高权重端口获得更多服务机会:
// 加权RR仲裁器(权重1:1:2:1:1)
module weighted_rr_arbiter #(
parameter NUM_REQS = 5,
parameter WEIGHTS = {4'd1, 4'd1, 4'd2, 4'd1, 4'd1}
)(
input logic clk, rst_n,
input logic [NUM_REQS-1:0] req,
output logic [NUM_REQS-1:0] gnt,
output logic valid
);
logic [3:0] credit [0:NUM_REQS-1];
// 初始化credit = weight
// 每次授权后credit--
// credit为0时跳过该端口
// 所有credit为0时重置
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (int i = 0; i < NUM_REQS; i++)
credit[i] <= WEIGHTS[i*4 +: 4];
end else if (valid) begin
for (int i = 0; i < NUM_REQS; i++) begin
if (gnt[i]) credit[i] <= credit[i] - 1;
if (credit[i] == 0 && !gnt[i])
credit[i] <= WEIGHTS[i*4 +: 4]; // 重置
end
end
end
// ... 仲裁逻辑
endmodule
在NoC中,数据包的等待时间(年龄)是公平性的重要指标。年龄优先仲裁器优先服务等待时间最长的包:
仲裁器的Verilog实现中有多个常见陷阱:
如果仲裁器的gnt信号反馈到req信号(通过流控ready),可能形成组合环路。解决方法:在gnt和ready之间插入寄存器。
在固定优先级仲裁器中,低优先级请求如果持续存在,可能在高优先级请求间隙"偷"到服务,导致优先级语义不正确。解决方法:只有在高优先级无请求时才服务低优先级。
如果仲裁跨越多个cycle(如虫孔交换中路径锁定期间),需要确保仲裁器状态在多周期保持一致。
本课涵盖了NoC设计的核心知识点。以下是关键概念的快速参考:
掌握这些概念是深入理解NoC设计的基础。建议结合Verilog代码实践,加深理解。