| 操作 | 能耗(pJ) | 倍数 |
|---|---|---|
| INT8 MAC | 0.2 | 1× |
| SRAM读64KB | 3 | 15× |
| DRAM读 | 40 | 200× |
每个Bank配双缓冲,计算读Buf0时DMA写Buf1。DMA三态FSM:空闲→请求→接收。交换后计算与加载重叠。
AI加速器的存储层次设计决定了数据复用效率,进而决定能效:
| 层次 | 容量 | 带宽 | 能耗/访问 | 延迟 |
|---|---|---|---|---|
| PE寄存器 | 1-4 B | 1 B/cycle | ~0.01 pJ | 1 cycle |
| PE SRAM(局部) | 64-256 B | 2-8 B/cycle | ~0.1 pJ | 1-2 cycle |
| 全局Buffer | 64-512 KB | 16-64 B/cycle | ~1 pJ | 2-4 cycle |
| 片外DRAM | 1-16 GB | 8-32 B/cycle | ~40 pJ | 100+ cycle |
在4×4阵列中,每个权重被4个PE复用,每个激活也被4个PE复用,总数据复用=16×(矩阵乘法本身的复用)
Bank冲突是并行访问的主要瓶颈:
| 方案 | 映射规则 | 顺序访问冲突 | 行访问冲突 | 面积开销 |
|---|---|---|---|---|
| 行交织 | addr % NB | 无 | 高(同行同Bank) | 低 |
| 列交织 | addr/NB | 无 | 低 | 低 |
| 对角交织 | (addr+addr/stride)%NB | 无 | 中 | 中 |
| 按通道分Bank | ch%NB | 无 | 无(不同通道) | 中 |
片上存储容量需要根据目标网络的工作集(Working Set)来确定:
| 网络 | 最大IFM | 最大权重 | 最大OFM | 最小Buffer |
|---|---|---|---|---|
| ResNet-18 | 112×112×64=1.6MB | 512×512×3×3=4.7MB | 112×112×128=3.2MB | ~2MB |
| MobileNet-V2 | 112×112×32=0.8MB | 144×144×1×1=80KB | 112×112×144=3.6MB | ~1MB |
| BERT-Base | 512×768=768KB | 768×768=4.7MB | 512×768=768KB | ~1.5MB |
完成本课后,你已掌握 片上存储设计 的核心概念与硬件实现方法。
// On-Chip Memory: 4-Bank Double-Buffered SRAM + DMA
module onchip_mem #(parameter DW=16, AW=12, NB=4, DEPTH=4096, BURST=16)(
input clk, rst_n,
output reg [31:0] dma_addr, output reg dma_valid, output reg [15:0] dma_len,
input dma_ready, input [DW*NB-1:0] dma_data, input dma_dvalid, input dma_last,
input [AW-1:0] raddr[0:NB-1], input [NB-1:0] ren, output [DW*NB-1:0] rdata, output [NB-1:0] rvalid,
input [AW-1:0] waddr[0:NB-1], input [NB-1:0] wen, input [DW*NB-1:0] wdata,
input [NB-1:0] bsel, input [1:0] bufsel, output [NB*2-1:0] bstat
);
genvar i; generate for(i=0;i<NB;i++) begin:bg
sram_bank #(.DW(DW),.DEPTH(DEPTH)) ub0(.clk(clk),.rst_n(rst_n),.ra(raddr[i]),.re(ren[i]&&!bufsel[0]),.rd(rdata[i*DW+:DW]),.wa(waddr[i]),.we(wen[i]&&!bufsel[0]),.wd(wdata[i*DW+:DW]));
sram_bank #(.DW(DW),.DEPTH(DEPTH)) ub1(.clk(clk),.rst_n(rst_n),.ra(raddr[i]),.re(ren[i]&&bufsel[0]),.rd(),.wa(waddr[i]),.we(wen[i]&&bufsel[0]),.wd(wdata[i*DW+:DW]));
end endgenerate
assign rvalid=ren&bsel; assign bstat='0;
reg [1:0] ds; reg [15:0] dcnt; reg [31:0] dbase;
always_ff @(posedge clk or negedge rst_n)
if(!rst_n) begin ds<=0; dcnt<=0; dbase<=0; dma_valid<=0; dma_addr<=0; dma_len<=0; end
else case(ds) 0: begin dma_valid<=0; if(dma_ready&&|bsel) begin ds<=1; dcnt<=0; dma_valid<=1; dma_addr<=dbase; dma_len<=BURST; end end
1: if(dma_ready) begin dma_valid<=0; ds<=2; end
2: if(dma_dvalid) begin dcnt<=dcnt+1; if(dma_last) begin ds<=0; dbase<=dbase+BURST; end end
endcase
endmodule
module sram_bank #(parameter DW=16, DEPTH=4096, AW=12)(
input clk, rst_n, input [AW-1:0] ra, input re, output reg [DW-1:0] rd, input [AW-1:0] wa, input we, input [DW-1:0] wd
);
reg [DW-1:0] mem[0:DEPTH-1]; always_ff @(posedge clk) if(re) rd<=mem[ra]; always_ff @(posedge clk) if(we) mem[wa]<=wd;
endmoduleSRAM的物理特性直接影响加速器性能:
// 6T SRAM结构:
// 2个交叉耦合反相器(存储)
// + 2个访问晶体管(读/写)
// 面积:28nm下约0.06μm²/cell
// 1KB SRAM ≈ 6144个cell ≈ 0.003mm²
//
// 读操作:字线WL=1,位线BL放电
// 写操作:字线WL=1,位线BL/BLB驱动
//
// 关键指标:
// - 读延迟:1-2ns(28nm)
// - 写延迟:1-2ns
// - 读功耗:~0.5pJ/bit
// - 漏电功耗:~0.1nW/bit
| 方式 | 面积 | 延迟 | 功耗 | 开发时间 |
|---|---|---|---|---|
| SRAM编译器 | 1.0× | 1.0× | 1.0× | 1天 |
| 定制SRAM | 0.7× | 0.8× | 0.7× | 3-6月 |
| 寄存器文件 | 1.5× | 0.5× | 2.0× | 1周 |
更多Bank意味着更高并行度,但面积和功耗也增加:
| Bank数 | 面积开销 | 功耗开销 | 并行度 | 冲突率 |
|---|---|---|---|---|
| 1 | 基线 | 基线 | 1 | 100% |
| 2 | +15% | +10% | 2 | 50% |
| 4 | +25% | +18% | 4 | 25% |
| 8 | +35% | +25% | 8 | 12.5% |
| 16 | +50% | +35% | 16 | 6.25% |
DMA引擎是连接片上存储和片外存储的桥梁:
// 2D DMA:传输矩形区域
// 参数:base_addr, width, height, stride, element_size
// for row in range(height):
// for col in range(width):
// dst[row*dst_stride+col] = src[row*src_stride+col]
//
// 3D DMA:传输立体区域(用于特征图)
// 参数:base_addr, w, h, c, w_stride, h_stride
// for ch in range(c):
// for row in range(h):
// for col in range(w):
// dst[ch*h_stride+row*w_stride+col] = src[...]
| 模式 | 流程 | 延迟隐藏 | 缓冲需求 |
|---|---|---|---|
| 同步DMA | 加载→计算→写回 | 无 | 1套 |
| 双缓冲DMA | 加载Buf1→计算Buf0+加载Buf1→交换 | 部分 | 2套 |
| 三缓冲DMA | 加载+计算+写回三路流水 | 完全 | 3套 |
多端口SRAM需要仲裁器来解决同时访问冲突:
// 4个访问源:DMA写、计算读、计算写、CPU调试
// 优先级(可配置):DMA > 计算读 > 计算写 > CPU
//
// 仲裁逻辑:
// always_ff @(posedge clk)
// if(dma_wr_req) sram_we <= 1, sram_addr <= dma_addr
// else if(comp_rd_req) sram_we <= 0, sram_addr <= comp_rd_addr
// else if(comp_wr_req) sram_we <= 1, sram_addr <= comp_wr_addr
// else if(cpu_req) sram_we <= cpu_we, sram_addr <= cpu_addr
//
// 流量分配(典型):
// DMA写: 30% (权重加载)
// 计算读: 40% (激活/权重读取)
// 计算写: 25% (结果写回)
// CPU: 5% (调试/配置)
| 技术 | 原理 | 节省 | 面积开销 |
|---|---|---|---|
| 字线门控 | 未访问的行关闭字线 | 30-50% | 5% |
| 位线预充电控制 | 读操作才预充电 | 15-25% | 3% |
| 分块睡眠 | 未使用的块进入睡眠 | 40-60% | 10% |
| Retention模式 | 降低电压保持数据 | 70-90% | 5% |
// SEC-DED ECC(单错纠正双错检测)
// 数据宽度16-bit → 需要6-bit ECC码(汉明距离4)
// 编码:P1=P0^D1^D3^D5^... P2=P0^D2^D3^D6^... ...
// 22-bit总存储 = 16数据 + 6校验
//
// 存储开销:6/16 = 37.5%
// 面积开销:ECC编码器~500门,解码器~800门
// 延迟开销:编码1 cycle,解码2 cycle
//
// 实际选择:
// 小SRAM(<4KB):不加ECC,降低开销
// 大SRAM(≥4KB):加SEC-DED,提高可靠性
存储子系统的带宽规划是NPU设计的关键步骤:
其中复用因子取决于数据驻留策略和分块大小。
| 配置 | PE数 | 频率 | 权重带宽 | 激活带宽 | 总带宽 |
|---|---|---|---|---|---|
| 4×4 INT8 | 16 | 200MHz | 0.4GB/s | 0.4GB/s | 1.2GB/s |
| 8×8 INT8 | 64 | 200MHz | 1.6GB/s | 1.6GB/s | 4.8GB/s |
| 16×16 INT8 | 256 | 200MHz | 6.4GB/s | 6.4GB/s | 19.2GB/s |
| 8×8 INT16 | 64 | 200MHz | 3.2GB/s | 3.2GB/s | 9.6GB/s |
// SRAM容量规划步骤:
// 1. 确定目标网络的最大工作集
// working_set = max(各层的 IFM + 权重 + OFM)
// 2. 加上双缓冲开销
// total_sram = working_set × 2 (双缓冲)
// 3. 加上其他缓冲(地址表、量化参数等)
// total_sram += overhead (通常10-20%)
// 4. 向上取整到2的幂次
//
// 示例(ResNet-50):
// 最大IFM: 112×112×64 = 1.6MB
// 最大权重: 512×512×3×3 = 4.7MB (需分块,仅放部分)
// 权重块: 8×8×64×3×3 = 36KB
// 双缓冲: 2×(1.6MB+36KB+1.6MB) ≈ 6.5MB
// 实际选择: 8MB SRAM