深入理解NoC路由器输入缓冲的设计,掌握FIFO、寄存器堆和SRAM三种缓冲实现,理解缓冲区深度优化。
输入缓冲是路由器中面积和功耗最大的组件(占路由器面积的40-60%)。它暂存到达的flit,等待路由计算和资源分配完成。缓冲区深度直接影响路由器的吞吐量和面积开销。
使用D触发器构成移位寄存器。每个flit占一个寄存器,读出时所有后续flit前移一位。
使用读写端口分离的寄存器堆,通过指针寻址。避免了移位寄存器的多端口问题。
使用单端口SRAM宏单元,面积效率最高。但读写不能同时进行,需要双端口SRAM或时分复用。
缓冲区深度取决于多个因素:
经验公式:Depth ≥ Pipeline_Delay + Credit_Round_Trip + 1
// 环形FIFO缓冲区 - 高效实现
module circular_fifo #(
parameter DATA_WIDTH = 32,
parameter DEPTH = 4
)(
input logic clk, rst_n,
input logic [DATA_WIDTH-1:0] wr_data,
input logic wr_en,
output logic wr_full,
input logic rd_en,
output logic [DATA_WIDTH-1:0] rd_data,
output logic rd_empty,
output logic [$clog2(DEPTH+1)-1:0] occupancy
);
logic [DATA_WIDTH-1:0] mem [0:DEPTH-1];
logic [$clog2(DEPTH):0] wr_ptr, rd_ptr;
assign occupancy = wr_ptr - rd_ptr;
assign wr_full = (occupancy == DEPTH);
assign rd_empty = (occupancy == 0);
assign rd_data = mem[rd_ptr[$clog2(DEPTH)-1:0]];
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr_ptr <= 0;
rd_ptr <= 0;
end else begin
if (wr_en && !wr_full) begin
mem[wr_ptr[$clog2(DEPTH)-1:0]] <= wr_data;
wr_ptr <= wr_ptr + 1;
end
if (rd_en && !rd_empty)
rd_ptr <= rd_ptr + 1;
end
end
endmodule
// 带旁路的输入缓冲(零延迟读出)
module bypass_input_buffer #(
parameter DATA_WIDTH = 32,
parameter DEPTH = 4
)(
input logic clk, rst_n,
input logic [DATA_WIDTH-1:0] in_data,
input logic in_valid,
output logic in_ready,
output logic [DATA_WIDTH-1:0] out_data,
output logic out_valid,
input logic out_ready
);
logic [DATA_WIDTH-1:0] fifo_data;
logic fifo_empty;
logic [$clog2(DEPTH+1)-1:0] fifo_occ;
logic bypass_en;
// 旁路: 缓冲区空且输出就绪时直接通过
assign bypass_en = fifo_empty && in_valid && out_ready;
circular_fifo #(.DATA_WIDTH(DATA_WIDTH), .DEPTH(DEPTH)) u_fifo (
.clk(clk), .rst_n(rst_n),
.wr_data(in_data),
.wr_en(in_valid && in_ready && !bypass_en),
.wr_full(~in_ready),
.rd_en(out_valid && out_ready && !bypass_en),
.rd_data(fifo_data),
.rd_empty(fifo_empty),
.occupancy(fifo_occ)
);
assign in_ready = (fifo_occ < DEPTH) | bypass_en;
assign out_data = bypass_en ? in_data : fifo_data;
assign out_valid = bypass_en | ~fifo_empty;
endmodule
环形FIFO和旁路缓冲通过Verilator验证。
练习1:计算4阶段流水线+2 cycle信用延迟下的最小缓冲深度。
练习2:实现双端口SRAM缓冲的Verilog模型。
练习3:仿真旁路缓冲在低负载下的延迟改善。
你已掌握输入缓冲的设计和优化!
输入缓冲的设计选择远比"用FIFO"复杂。以下是缓冲区设计的完整空间:
| 组织方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 每端口独立FIFO | 简单 | 面积利用率低 | 标准NoC |
| 共享缓冲池 | 面积利用率高 | 仲裁复杂 | 面积受限 |
| 每VC独立FIFO | VC隔离 | 面积最大 | 多VC NoC |
| 混合(共享+独立) | 平衡 | 设计复杂 | 高性能NoC |
共享缓冲池允许不同端口动态使用缓冲区空间。需要缓冲区管理器分配和回收槽位:
// 共享缓冲池管理器
module shared_buffer_mgr #(
parameter NUM_PORTS = 5,
parameter TOTAL_DEPTH = 20,
parameter DATA_WIDTH = 32
)(
input logic clk, rst_n,
// 各端口写入请求
input logic [NUM_PORTS-1:0] wr_req,
output logic [NUM_PORTS-1:0] wr_gnt,
input logic [DATA_WIDTH-1:0] wr_data [0:NUM_PORTS-1],
// 各端口读出请求
input logic [NUM_PORTS-1:0] rd_req,
output logic [NUM_PORTS-1:0] rd_valid,
output logic [DATA_WIDTH-1:0] rd_data [0:NUM_PORTS-1],
// 空闲槽位数
output logic [$clog2(TOTAL_DEPTH):0] free_slots
);
logic [DATA_WIDTH-1:0] mem [0:TOTAL_DEPTH-1];
logic [TOTAL_DEPTH-1:0] slot_used;
assign free_slots = TOTAL_DEPTH - $countones(slot_used);
// 简化: 每端口轮流分配
for (genvar p = 0; p < NUM_PORTS; p++) begin
assign wr_gnt[p] = wr_req[p] && (free_slots > 0);
end
endmodule
传统的寄存器/SRAM缓冲区之外,新兴技术提供了新的选择:
自旋转移矩MRAM(STT-MRAM)具有近零漏电功耗的优势,写入速度比SRAM慢但读取速度相当。非常适合NoC缓冲区(读多写少)。
在缓冲区中嵌入简单的计算逻辑,实现"数据在传输途中被处理"。例如:在路由器缓冲区中进行数据聚合/过滤,减少需要传输的数据量。
将大容量SRAM缓冲区放在3D堆叠的独立层上,与逻辑层(路由器)通过TSV连接。可以在不增加逻辑层面积的情况下大幅增加缓冲容量。
以下是一个完整的4-deep环形FIFO设计实例,包含所有边界条件处理:
// 生产级FIFO设计 - 完整边界处理
module production_fifo #(
parameter DATA_WIDTH = 32,
parameter DEPTH = 4
)(
input logic clk, rst_n,
input logic [DATA_WIDTH-1:0] wr_data,
input logic wr_en,
output logic wr_full,
input logic rd_en,
output logic [DATA_WIDTH-1:0] rd_data,
output logic rd_empty,
output logic [$clog2(DEPTH+1)-1:0] count
);
logic [DATA_WIDTH-1:0] mem [0:DEPTH-1];
logic [$clog2(DEPTH):0] wr_ptr, rd_ptr;
// 指针使用格雷码(跨时钟域安全)
logic [$clog2(DEPTH):0] wr_ptr_gray, rd_ptr_gray;
assign wr_ptr_gray = wr_ptr ^ (wr_ptr >> 1);
assign rd_ptr_gray = rd_ptr ^ (rd_ptr >> 1);
assign count = wr_ptr - rd_ptr;
assign wr_full = (count == DEPTH);
assign rd_empty = (count == 0);
assign rd_data = mem[rd_ptr[$clog2(DEPTH)-1:0]];
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr_ptr <= 0;
rd_ptr <= 0;
end else begin
if (wr_en && !wr_full) begin
mem[wr_ptr[$clog2(DEPTH)-1:0]] <= wr_data;
wr_ptr <= wr_ptr + 1;
end
if (rd_en && !rd_empty)
rd_ptr <= rd_ptr + 1;
end
end
// 断言: 不允许满时写、空时读
// assert property(@(posedge clk) !(wr_en && wr_full));
// assert property(@(posedge clk) !(rd_en && rd_empty));
endmodule
缓冲区是NoC中面积和功耗最大的组件。精确的估算对设计决策至关重要:
每bit面积:约10μm²(16nm工艺)。功耗:动态约0.5μW/MHz/bit,静态约0.05μW/bit。5端口×4-deep×32bit = 640bit → 面积6400μm²,功耗320μW/MHz。
每bit面积:约1μm²(16nm工艺)。功耗:动态约0.1μW/MHz/bit,静态约0.01μW/bit。同容量640bit → 面积640μm²,功耗64μW/MHz。面积节省90%!
| 条件 | 推荐 |
|---|---|
| 缓冲深度≤8 | 寄存器(速度快) |
| 8<缓冲深度≤32 | SRAM(面积优) |
| 缓冲深度>32 | SRAM+寄存器旁路(混合) |
除了基本的FIFO和SRAM选择,还有多种高级优化技术:
在SA结果确定前,推测性地从缓冲区读取数据。如果SA结果正确(通常85%概率),节省1 cycle;否则下个cycle重读。
当读写同一地址时,选择write-first(新数据优先)或read-first(旧数据优先)。对于NoC缓冲区,read-first更常见(保证数据一致性)。
输入缓冲设计正在向更高效、更智能的方向发展:
本课涵盖了NoC设计的核心知识点。以下是关键概念的快速参考:
掌握这些概念是深入理解NoC设计的基础。建议结合Verilog代码实践,加深理解。