人工智能芯片设计——片上存储系统原理与实现
本文介绍了面向AI加速器的片上存储系统设计与Verilog实现。该系统采用多级缓存架构,包含144KB数据缓存(48个Bank)和17KB权重缓存(1KB共享+16KB局部),通过乒乓操作、Bank并行访问和数据复用技术解决访存墙问题。重点阐述了缓存控制器的状态机设计,以及数据与权重缓存的Verilog实现细节,包括Bank结构、地址映射和访问调度机制。该系统支持计算与数据预取的并行执行,可为处理
目录
片上存储系统是连接计算单元(如处理单元 PE 阵列)与片外存储(如DRAM)的关键桥梁,其核心目标是解决访存墙问题 —— 计算单元的算力提升速度远快于存储系统的带宽增长,导致数据搬运成为性能瓶颈。片上存储系统通过多级缓存架构、并行访问、数据复用与流水线设计,实现数据的高效管理与传输,为计算单元提供低延迟、高带宽的数据流。
1.片上存储系统原理
1.1 多级缓存架构
数据缓存(Data Cache):两个144KB Multi-Bank SRAM数据缓存属于这一层级,用于存储网络层间的中间数据。通过划分成子缓存(支持CONV和FC子阵列),实现计算过程中 “输入数据供给”与“输出数据接收”的流水线操作(乒乓机制),同时利用Multi-Bank结构支持多端口并行访问,为PE阵列提供高带宽数据。
权重缓存(Weight Cache):包含1KB片内共享权重缓存和两个16KB阵列内局部缓存。共享权重缓存负责从片外加载权重并转发,局部缓存则用于计算时的权重复用,减少片外访存次数,降低延迟。
1.2 并行访问与Bank机制
Multi-Bank设计:每个数据缓存包含48个Bank,每个Bank可独立访问。当PE阵列需要并行处理多个数据时,可同时从不同Bank中读取16bit数据,最多支持48路并行,极大提升数据带宽。
Bank以“乒乓”方式工作,即一个周期内,部分Bank为PE阵列提供输入数据,另一部分Bank接收PE阵列的输出数据;下一个周期交换功能。这种机制使数据交换(PE与片上缓存)和数据预取(片上缓存与片外DRAM)可同时进行,掩盖片外访存的高延迟。
1.3 数据复用与阵列划分
数据缓存支持层间数据重用,避免重复从片外加载相同数据;权重缓存的局部缓存支持计算过程中权重的复用,减少权重的访存次数。
阵列划分(AP):数据缓存划分为CONV(卷积)和FC(全连接)子缓存,使缓存能适配不同计算模式(卷积操作和全连接操作对数据的访问模式差异大),优化数据布局与访问效率。
1.4 缓存控制器
缓存控制器是片上存储的 “大脑”,其负责:
管理数据缓存和权重缓存的访问请求,调度Bank的读写操作;
组织数据在缓存中的存储格式,确保计算单元能高效获取数据;
协调片外DRAM的数据预取,与乒乓操作配合,隐藏片外访存延迟。
2.片上存储系统实现过程
2.1 硬件模块划分
片上存储系统可划分为以下核心模块,各模块协同工作实现数据的高效管理:
数据缓存模块(Data Cache):包含两个144KB Multi-Bank SRAM,每个缓存内部划分为 CONV子缓存和FC子缓存,每个子缓存包含24个 Bank(共48个Bank)。
权重缓存模块(Weight Cache):由1KB共享权重缓存和两个16KB局部权重缓存组成。
缓存控制器模块(Cache Controller):负责解析访存请求、调度Bank访问、管理乒乓操作、协调片外数据预取。
接口模块:处理与PE阵列的接口(数据输入 / 输出)和与片外DRAM的接口(数据预取)
2.2 数据缓存的实现步骤
Bank结构设计:每个Bank采用双端口SRAM(支持同时读写,实现乒乓操作),位宽为 16bit,深度根据缓存容量和Bank数量计算(144KB总容量,48个Bank,每个Bank深度为48 Bank×2 Byte/16bit144×1024 Byte =1536)。
子缓存划分:将每个144KB数据缓存的48 个Bank平均分为两组(各24个Bank),分别作为 CONV子缓存和FC子缓存。乒乓操作控制:通过缓存控制器的状态机,控制两组子缓存在 “输入供给”和“输出接收”功能间切换。例如,周期t时,CONV子缓存的24个Bank为PE阵列提供输入数据,FC子缓存的24个Bank接收输出数据;周期t+1时,功能交换。并行访问调度:当PE阵列需要N个并行数据时(N≤48),缓存控制器从不同Bank中选择N个,同时发起读请求,将数据传输到PE 阵列。
2.3 权重缓存的实现步骤
共享权重缓存:采用单端口SRAM,位宽与权重数据宽度匹配(假设为16bit),深度为2 Byte/16bit1×1024 Byte =512。负责从片外DRAM接收权重,暂存后转发给局部权重缓存。局部权重缓存:两个16KB的双端口SRAM,每个深度为2 Byte/16bit16×1024 Byte=8192。计算时,缓存控制器根据计算模式(CONV或FC),从共享缓存加载所需权重到局部缓存,供PE阵列复用。
权重复用管理:局部缓存采用行优先或按计算粒度的存储方式,确保PE阵列的16列可同时从局部缓存获取16个权重,支持并行计算。
2.4 缓存控制器的实现步骤
状态机设计:包含 “空闲”“数据预取”“乒乓切换”“权重转发” 等状态,根据芯片的计算阶段(如卷积层计算、全连接层计算)切换状态。
请求解析与调度:解析PE阵列的访存请求(读数据/写数据、读权重/写权重),根据缓存当前状态(如Bank是否可用、子缓存功能),调度Bank的读写操作,生成SRAM的地址、读写使能等控制信号。
乒乓操作协调:在状态机中设置 “乒乓标志位”,控制子缓存功能的切换时机,确保数据交换与预取的并行。
片外预取控制:当片上缓存数据不足时,向片外DRAM发起预取请求,同时利用乒乓操作的时间窗口,在PE与片上缓存交互时,后台完成预取,隐藏延迟。
3.片上存储系统的Verilog实现
片上存储系统核心模块(数据缓存、权重缓存、缓存控制器)的Verilog代码如下所示:
//////////////////////////////////////////////////////////////////////////////////
// 模块:Bank_16bit
// 功能:双端口SRAM,支持乒乓操作的基本存储单元(位宽16bit)
//////////////////////////////////////////////////////////////////////////////////
module Bank_16bit (
input wire clk, // 时钟
input wire rst_n, // 异步复位,低有效
// 端口A:用于PE阵列或DRAM访问
input wire wen_a, // 写使能
input wire [10:0] addr_a,// 地址(深度1536,11位地址)
input wire [15:0] din_a, // 写数据
output reg [15:0] dout_a,// 读数据
// 端口B:用于乒乓操作的另一路访问
input wire wen_b, // 写使能
input wire [10:0] addr_b,// 地址
input wire [15:0] din_b, // 写数据
output reg [15:0] dout_b // 读数据
);
// 双端口SRAM存储阵列(实际工程中需替换为FPGA/ASIC的SRAM IP)
reg [15:0] sram [0:1535];
// 端口A读写逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dout_a <= 16'd0;
end else begin
if (wen_a) begin
sram[addr_a] <= din_a;
end
dout_a <= sram[addr_a];
end
end
// 端口B读写逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dout_b <= 16'd0;
end else begin
if (wen_b) begin
sram[addr_b] <= din_b;
end
dout_b <= sram[addr_b];
end
end
endmodule
//////////////////////////////////////////////////////////////////////////////////
// 模块:Cache_Controller
// 功能:缓存控制器(调度数据缓存和权重缓存的访问)
//////////////////////////////////////////////////////////////////////////////////
module Cache_Controller (
input wire clk, // 时钟
input wire rst_n, // 异步复位,低有效
// 与数据缓存的接口
output reg data_cache_ping_pong_sel, // 乒乓选择信号
output reg pe_wen, // PE写使能
output reg pe_is_conv, // PE访问CONV子缓存标志
output reg [4:0] pe_bank_sel, // PE Bank选择
output reg [10:0] pe_addr, // PE地址
output reg [15:0] pe_din, // PE写数据
input wire [15:0] pe_dout, // PE读数据
output reg dram_wen, // DRAM写使能
output reg dram_is_conv, // DRAM访问CONV子缓存标志
output reg [4:0] dram_bank_sel, // DRAM Bank选择
output reg [10:0] dram_addr, // DRAM地址
output reg [15:0] dram_din, // DRAM写数据
input wire [15:0] dram_dout, // DRAM读数据
// 与权重缓存的接口
output reg shared_wen_dram, // 共享权重缓存DRAM写使能
output reg [8:0] shared_addr_dram, // 共享权重缓存DRAM地址
output reg [15:0] shared_din_dram, // 共享权重缓存DRAM写数据
input wire [15:0] shared_dout_dram, // 共享权重缓存DRAM读数据
output reg local1_wen_pe, local1_wen_shared, // 局部权重缓存1控制
output reg [12:0] local1_addr_pe, local1_addr_shared,
output reg [15:0] local1_din_pe, local1_din_shared,
input wire [15:0] local1_dout_pe,
output reg local2_wen_pe, local2_wen_shared, // 局部权重缓存2控制
output reg [12:0] local2_addr_pe, local2_addr_shared,
output reg [15:0] local2_din_pe, local2_din_shared,
input wire [15:0] local2_dout_pe,
// 与PE阵列的控制接口
input wire pe_req, // PE访问请求
input wire pe_rw, // PE读写控制(0-读,1-写)
input wire pe_is_weight, // PE访问的是权重?(0-数据,1-权重)
input wire [1:0] pe_array_sel, // PE阵列选择(0-无,1-阵列1,2-阵列2)
input wire pe_is_conv, // PE访问CONV子阵列
output reg pe_ack, // PE请求响应
// 与片外DRAM的控制接口
input wire dram_idle, // DRAM空闲标志
input wire dram_data_valid, // DRAM数据有效
output reg dram_req, // DRAM预取请求
output reg [31:0] dram_addr_req, // DRAM请求地址
output reg dram_rw // DRAM读写控制(0-读,1-写)
);
// 状态机定义
typedef enum {
IDLE, // 空闲状态
DATA_ACCESS, // 数据缓存访问
WEIGHT_ACCESS, // 权重缓存访问
DATA_PREFETCH, // 数据预取
WEIGHT_PREFETCH,// 权重预取
PING_PONG_SWAP // 乒乓切换
} state_t;
state_t current_state, next_state;
// 内部寄存器
reg [3:0] prefetch_cnt; // 预取计数器
reg ping_pong_ready; // 乒乓切换准备标志
reg [10:0] data_prefetch_addr; // 数据预取地址
reg [8:0] weight_prefetch_addr; // 权重预取地址
// 状态机时序逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= IDLE;
end else begin
current_state <= next_state;
end
end
// 状态机组合逻辑
always @(*) begin
next_state = current_state;
case (current_state)
IDLE: begin
if (pe_req) begin
next_state = pe_is_weight ? WEIGHT_ACCESS : DATA_ACCESS;
end else if (dram_idle && ping_pong_ready) begin
next_state = DATA_PREFETCH;
end else if (dram_idle && !ping_pong_ready) begin
next_state = WEIGHT_PREFETCH;
end
end
DATA_ACCESS: begin
next_state = IDLE;
end
WEIGHT_ACCESS: begin
next_state = IDLE;
end
DATA_PREFETCH: begin
if (prefetch_cnt >= 4'd8) begin
next_state = PING_PONG_SWAP;
end
end
WEIGHT_PREFETCH: begin
if (prefetch_cnt >= 4'd8) begin
next_state = IDLE;
end
end
PING_PONG_SWAP: begin
next_state = IDLE;
end
default: next_state = IDLE;
endcase
end
// 计数器和标志位控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
prefetch_cnt <= 4'd0;
ping_pong_ready <= 1'b0;
data_prefetch_addr <= 11'd0;
weight_prefetch_addr <= 9'd0;
end else begin
case (current_state)
IDLE: begin
prefetch_cnt <= 4'd0;
end
DATA_PREFETCH: begin
prefetch_cnt <= prefetch_cnt + 4'd1;
data_prefetch_addr <= data_prefetch_addr + 11'd1;
end
WEIGHT_PREFETCH: begin
prefetch_cnt <= prefetch_cnt + 4'd1;
weight_prefetch_addr <= weight_prefetch_addr + 9'd1;
end
PING_PONG_SWAP: begin
ping_pong_ready <= ~ping_pong_ready;
end
default: begin
prefetch_cnt <= prefetch_cnt;
end
endcase
end
end
// 数据缓存访问控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
pe_wen <= 1'b0;
pe_bank_sel <= 5'd0;
pe_addr <= 11'd0;
pe_din <= 16'd0;
pe_ack <= 1'b0;
end else if (current_state == DATA_ACCESS) begin
pe_wen <= pe_rw;
pe_is_conv <= pe_is_conv;
pe_bank_sel <= 5'd0; // 实际应用中需根据地址映射逻辑生成
pe_addr <= 11'd0; // 实际应用中需根据地址映射逻辑生成
pe_din <= 16'd0; // 实际数据由PE阵列提供
pe_ack <= 1'b1;
end else begin
pe_wen <= 1'b0;
pe_ack <= 1'b0;
end
end
// 权重缓存访问控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
local1_wen_pe <= 1'b0;
local2_wen_pe <= 1'b0;
local1_addr_pe <= 13'd0;
local2_addr_pe <= 13'd0;
local1_din_pe <= 16'd0;
local2_din_pe <= 16'd0;
end else if (current_state == WEIGHT_ACCESS) begin
case (pe_array_sel)
2'd1: begin // PE阵列1访问
local1_wen_pe <= pe_rw;
local1_addr_pe <= 13'd0; // 实际地址需根据映射逻辑生成
local1_din_pe <= 16'd0; // 实际数据由PE阵列提供
end
2'd2: begin // PE阵列2访问
local2_wen_pe <= pe_rw;
local2_addr_pe <= 13'd0; // 实际地址需根据映射逻辑生成
local2_din_pe <= 16'd0; // 实际数据由PE阵列提供
end
default: begin
local1_wen_pe <= 1'b0;
local2_wen_pe <= 1'b0;
end
endcase
end else begin
local1_wen_pe <= 1'b0;
local2_wen_pe <= 1'b0;
end
end
// 数据预取控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
dram_req <= 1'b0;
dram_addr_req <= 32'd0;
dram_rw <= 1'b0;
dram_wen <= 1'b0;
dram_bank_sel <= 5'd0;
dram_addr <= 11'd0;
dram_din <= 16'd0;
end else if (current_state == DATA_PREFETCH) begin
dram_req <= 1'b1;
dram_addr_req <= 32'd0; // 实际地址需根据内存映射生成
dram_rw <= 1'b0; // 读操作
if (dram_data_valid) begin
dram_wen <= 1'b1;
dram_bank_sel <= prefetch_cnt[4:0]; // 按计数器分配Bank
dram_addr <= data_prefetch_addr;
dram_din <= dram_dout;
end else begin
dram_wen <= 1'b0;
end
end else begin
dram_req <= 1'b0;
dram_wen <= 1'b0;
end
end
// 权重预取控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
shared_wen_dram <= 1'b0;
shared_addr_dram <= 9'd0;
shared_din_dram <= 16'd0;
local1_wen_shared <= 1'b0;
local2_wen_shared <= 1'b0;
local1_addr_shared <= 13'd0;
local2_addr_shared <= 13'd0;
local1_din_shared <= 16'd0;
local2_din_shared <= 16'd0;
end else if (current_state == WEIGHT_PREFETCH) begin
// 从DRAM预取到共享权重缓存
shared_wen_dram <= 1'b1;
shared_addr_dram <= weight_prefetch_addr;
shared_din_dram <= dram_dout;
// 从共享缓存分发到局部缓存
if (prefetch_cnt[0] == 1'b0) begin // 偶数地址到局部缓存1
local1_wen_shared <= 1'b1;
local1_addr_shared <= {4'd0, weight_prefetch_addr};
local1_din_shared <= shared_dout_dram;
local2_wen_shared <= 1'b0;
end else begin // 奇数地址到局部缓存2
local2_wen_shared <= 1'b1;
local2_addr_shared <= {4'd0, weight_prefetch_addr};
local2_din_shared <= shared_dout_dram;
local1_wen_shared <= 1'b0;
end
end else begin
shared_wen_dram <= 1'b0;
local1_wen_shared <= 1'b0;
local2_wen_shared <= 1'b0;
end
end
// 乒乓切换控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_cache_ping_pong_sel <= 1'b0;
end else if (current_state == PING_PONG_SWAP) begin
data_cache_ping_pong_sel <= ~data_cache_ping_pong_sel;
end
end
endmodule
//////////////////////////////////////////////////////////////////////////////////
// 模块:Data_Cache
// 功能:完整的数据缓存(包含两个子缓存:CONV子缓存和FC子缓存)
//////////////////////////////////////////////////////////////////////////////////
module Data_Cache (
input wire clk, // 时钟
input wire rst_n, // 异步复位,低有效
input wire ping_pong_sel,// 乒乓选择:0-CONV供输入、FC收输出;1-反之
// CONV子缓存接口
output wire conv_wen_pe, conv_wen_dram,
output wire [4:0] conv_bank_sel,
output wire [10:0] conv_addr_pe, conv_addr_dram,
output wire [15:0] conv_din_pe, conv_din_dram,
input wire [15:0] conv_dout_pe, conv_dout_dram,
// FC子缓存接口
output wire fc_wen_pe, fc_wen_dram,
output wire [4:0] fc_bank_sel,
output wire [10:0] fc_addr_pe, fc_addr_dram,
output wire [15:0] fc_din_pe, fc_din_dram,
input wire [15:0] fc_dout_pe, fc_dout_dram,
// PE阵列接口(最终对外接口,根据乒乓选择映射到子缓存)
input wire pe_wen, // PE写使能
input wire pe_is_conv, // PE访问的是CONV子阵列?(0-FC,1-CONV)
input wire [4:0] pe_bank_sel,
input wire [10:0] pe_addr,
input wire [15:0] pe_din,
output wire [15:0] pe_dout,
// DRAM预取接口(最终对外接口)
input wire dram_wen, // DRAM写使能
input wire dram_is_conv, // DRAM预取的是CONV子缓存?
input wire [4:0] dram_bank_sel,
input wire [10:0] dram_addr,
input wire [15:0] dram_din,
output wire [15:0] dram_dout
);
// CONV子缓存实例化
Sub_Data_Cache u_CONV_Sub_Cache (
.clk(clk),
.rst_n(rst_n),
.bank_sel(conv_bank_sel),
.wen_pe(conv_wen_pe),
.addr_pe(conv_addr_pe),
.din_pe(conv_din_pe),
.dout_pe(conv_dout_pe),
.wen_dram(conv_wen_dram),
.addr_dram(conv_addr_dram),
.din_dram(conv_din_dram),
.dout_dram(conv_dout_dram)
);
// FC子缓存实例化
Sub_Data_Cache u_FC_Sub_Cache (
.clk(clk),
.rst_n(rst_n),
.bank_sel(fc_bank_sel),
.wen_pe(fc_wen_pe),
.addr_pe(fc_addr_pe),
.din_pe(fc_din_pe),
.dout_pe(fc_dout_pe),
.wen_dram(fc_wen_dram),
.addr_dram(fc_addr_dram),
.din_dram(fc_din_dram),
.dout_dram(fc_dout_dram)
);
// 乒乓操作:根据ping_pong_sel切换子缓存功能
// CONV子缓存控制信号
assign conv_wen_pe = ping_pong_sel ? 1'b0 : (pe_wen & pe_is_conv);
assign conv_wen_dram = ping_pong_sel ? 1'b0 : (dram_wen & dram_is_conv);
assign conv_bank_sel = ping_pong_sel ? 5'd0 : pe_bank_sel;
assign conv_addr_pe = ping_pong_sel ? 11'd0 : pe_addr;
assign conv_addr_dram = ping_pong_sel ? 11'd0 : dram_addr;
assign conv_din_pe = ping_pong_sel ? 16'd0 : pe_din;
assign conv_din_dram = ping_pong_sel ? 16'd0 : dram_din;
// FC子缓存控制信号
assign fc_wen_pe = ping_pong_sel ? (pe_wen & pe_is_conv) : 1'b0;
assign fc_wen_dram = ping_pong_sel ? (dram_wen & dram_is_conv) : 1'b0;
assign fc_bank_sel = ping_pong_sel ? pe_bank_sel : 5'd0;
assign fc_addr_pe = ping_pong_sel ? pe_addr : 11'd0;
assign fc_addr_dram = ping_pong_sel ? dram_addr : 11'd0;
assign fc_din_pe = ping_pong_sel ? pe_din : 16'd0;
assign fc_din_dram = ping_pong_sel ? dram_din : 16'd0;
// PE阵列最终输出:根据乒乓选择和pe_is_conv选择子缓存输出
assign pe_dout = pe_is_conv ? (ping_pong_sel ? fc_dout_pe : conv_dout_pe) :
(ping_pong_sel ? conv_dout_pe : fc_dout_pe);
// DRAM预取最终输出:根据dram_is_conv选择子缓存输出
assign dram_dout = dram_is_conv ? conv_dout_dram : fc_dout_dram;
endmodule
//////////////////////////////////////////////////////////////////////////////////
// 模块:OnChip_Memory_Top
// 功能:片上存储系统顶层模块(整合所有子模块)
//////////////////////////////////////////////////////////////////////////////////
module OnChip_Memory_Top (
input wire clk, // 时钟
input wire rst_n, // 异步复位,低有效
// 与PE阵列的接口
input wire pe_req, // PE访问请求
input wire pe_rw, // PE读写控制(0-读,1-写)
input wire pe_is_weight, // PE访问的是权重?(0-数据,1-权重)
input wire [1:0] pe_array_sel, // PE阵列选择(0-无,1-阵列1,2-阵列2)
input wire pe_is_conv, // PE访问CONV子阵列
input wire [15:0] pe_din, // PE写数据
output wire [15:0] pe_dout, // PE读数据
output wire pe_ack, // PE请求响应
// 与片外DRAM的接口
input wire dram_idle, // DRAM空闲标志
input wire dram_data_valid, // DRAM数据有效
input wire [15:0] dram_dout, // DRAM读数据
output wire dram_req, // DRAM预取请求
output wire [31:0] dram_addr_req, // DRAM请求地址
output wire dram_rw // DRAM读写控制(0-读,1-写)
);
// 数据缓存接口信号
wire data_cache_ping_pong_sel;
wire conv_wen_pe, conv_wen_dram;
wire [4:0] conv_bank_sel;
wire [10:0] conv_addr_pe, conv_addr_dram;
wire [15:0] conv_din_pe, conv_din_dram;
wire [15:0] conv_dout_pe, conv_dout_dram;
wire fc_wen_pe, fc_wen_dram;
wire [4:0] fc_bank_sel;
wire [10:0] fc_addr_pe, fc_addr_dram;
wire [15:0] fc_din_pe, fc_din_dram;
wire [15:0] fc_dout_pe, fc_dout_dram;
// 权重缓存接口信号
wire shared_wen_dram;
wire [8:0] shared_addr_dram;
wire [15:0] shared_din_dram;
wire [15:0] shared_dout_dram;
wire local1_wen_pe, local1_wen_shared;
wire [12:0] local1_addr_pe, local1_addr_shared;
wire [15:0] local1_din_pe, local1_din_shared;
wire [15:0] local1_dout_pe;
wire local2_wen_pe, local2_wen_shared;
wire [12:0] local2_addr_pe, local2_addr_shared;
wire [15:0] local2_din_pe, local2_din_shared;
wire [15:0] local2_dout_pe;
// 缓存控制器接口信号(数据缓存相关)
wire pe_wen;
wire pe_is_conv_ctrl;
wire [4:0] pe_bank_sel;
wire [10:0] pe_addr;
wire [15:0] pe_din_ctrl;
wire [15:0] pe_dout_ctrl;
wire dram_wen;
wire dram_is_conv;
wire [4:0] dram_bank_sel;
wire [10:0] dram_addr;
wire [15:0] dram_din;
wire [15:0] dram_dout_ctrl;
// 实例化数据缓存
Data_Cache u_Data_Cache (
.clk(clk),
.rst_n(rst_n),
.ping_pong_sel(data_cache_ping_pong_sel),
// CONV子缓存接口
.conv_wen_pe(conv_wen_pe),
.conv_wen_dram(conv_wen_dram),
.conv_bank_sel(conv_bank_sel),
.conv_addr_pe(conv_addr_pe),
.conv_addr_dram(conv_addr_dram),
.conv_din_pe(conv_din_pe),
.conv_din_dram(conv_din_dram),
.conv_dout_pe(conv_dout_pe),
.conv_dout_dram(conv_dout_dram),
// FC子缓存接口
.fc_wen_pe(fc_wen_pe),
.fc_wen_dram(fc_wen_dram),
.fc_bank_sel(fc_bank_sel),
.fc_addr_pe(fc_addr_pe),
.fc_addr_dram(fc_addr_dram),
.fc_din_pe(fc_din_pe),
.fc_din_dram(fc_din_dram),
.fc_dout_pe(fc_dout_pe),
.fc_dout_dram(fc_dout_dram),
// PE阵列接口
.pe_wen(pe_wen),
.pe_is_conv(pe_is_conv_ctrl),
.pe_bank_sel(pe_bank_sel),
.pe_addr(pe_addr),
.pe_din(pe_din_ctrl),
.pe_dout(pe_dout_ctrl),
// DRAM预取接口
.dram_wen(dram_wen),
.dram_is_conv(dram_is_conv),
.dram_bank_sel(dram_bank_sel),
.dram_addr(dram_addr),
.dram_din(dram_din),
.dram_dout(dram_dout_ctrl)
);
// 实例化权重缓存
Weight_Cache u_Weight_Cache (
.clk(clk),
.rst_n(rst_n),
// 共享权重缓存接口
.shared_wen_dram(shared_wen_dram),
.shared_addr_dram(shared_addr_dram),
.shared_din_dram(shared_din_dram),
.shared_dout_dram(shared_dout_dram),
// 局部权重缓存1接口
.local1_wen_pe(local1_wen_pe),
.local1_wen_shared(local1_wen_shared),
.local1_addr_pe(local1_addr_pe),
.local1_addr_shared(local1_addr_shared),
.local1_din_pe(local1_din_pe),
.local1_din_shared(local1_din_shared),
.local1_dout_pe(local1_dout_pe),
// 局部权重缓存2接口
.local2_wen_pe(local2_wen_pe),
.local2_wen_shared(local2_wen_shared),
.local2_addr_pe(local2_addr_pe),
.local2_addr_shared(local2_addr_shared),
.local2_din_pe(local2_din_pe),
.local2_din_shared(local2_din_shared),
.local2_dout_pe(local2_dout_pe)
);
// 实例化缓存控制器
Cache_Controller u_Cache_Controller (
.clk(clk),
.rst_n(rst_n),
// 与数据缓存的接口
.data_cache_ping_pong_sel(data_cache_ping_pong_sel),
.pe_wen(pe_wen),
.pe_is_conv(pe_is_conv_ctrl),
.pe_bank_sel(pe_bank_sel),
.pe_addr(pe_addr),
.pe_din(pe_din_ctrl),
.pe_dout(pe_dout_ctrl),
.dram_wen(dram_wen),
.dram_is_conv(dram_is_conv),
.dram_bank_sel(dram_bank_sel),
.dram_addr(dram_addr),
.dram_din(dram_din),
.dram_dout(dram_dout_ctrl),
// 与权重缓存的接口
.shared_wen_dram(shared_wen_dram),
.shared_addr_dram(shared_addr_dram),
.shared_din_dram(shared_din_dram),
.shared_dout_dram(shared_dout_dram),
.local1_wen_pe(local1_wen_pe),
.local1_wen_shared(local1_wen_shared),
.local1_addr_pe(local1_addr_pe),
.local1_addr_shared(local1_addr_shared),
.local1_din_pe(local1_din_pe),
.local1_din_shared(local1_din_shared),
.local1_dout_pe(local1_dout_pe),
.local2_wen_pe(local2_wen_pe),
.local2_wen_shared(local2_wen_shared),
.local2_addr_pe(local2_addr_pe),
.local2_addr_shared(local2_addr_shared),
.local2_din_pe(local2_din_pe),
.local2_din_shared(local2_din_shared),
.local2_dout_pe(local2_dout_pe),
// 与PE阵列的控制接口
.pe_req(pe_req),
.pe_rw(pe_rw),
.pe_is_weight(pe_is_weight),
.pe_array_sel(pe_array_sel),
.pe_ack(pe_ack),
// 与片外DRAM的控制接口
.dram_idle(dram_idle),
.dram_data_valid(dram_data_valid),
.dram_req(dram_req),
.dram_addr_req(dram_addr_req),
.dram_rw(dram_rw)
);
// PE数据输入输出连接
assign pe_din_ctrl = pe_din;
assign pe_dout = pe_is_weight ?
(pe_array_sel == 2'd1 ? local1_dout_pe : local2_dout_pe) :
pe_dout_ctrl;
endmodule
//////////////////////////////////////////////////////////////////////////////////
// 模块:Sub_Data_Cache
// 功能:数据缓存的子缓存(包含24个Bank,支持CONV或FC子阵列访问)
//////////////////////////////////////////////////////////////////////////////////
module Sub_Data_Cache (
input wire clk, // 时钟
input wire rst_n, // 异步复位,低有效
// Bank选择与访问控制
input wire [4:0] bank_sel, // 选择24个Bank中的一个(0~23)
// PE阵列接口
input wire wen_pe, // PE写使能(向子缓存写数据)
input wire [10:0] addr_pe, // PE访问地址
input wire [15:0] din_pe, // PE写数据
output wire [15:0] dout_pe, // PE读数据
// DRAM预取接口
input wire wen_dram, // DRAM写使能(从DRAM预取数据)
input wire [10:0] addr_dram,// DRAM访问地址
input wire [15:0] din_dram, // DRAM写数据
output wire [15:0] dout_dram// DRAM读数据(预取时用)
);
// 24个Bank的输出总线
wire [15:0] bank_dout [0:23];
// 选择输出的寄存器
reg [15:0] sel_dout_pe, sel_dout_dram;
// 生成24个Bank实例
genvar i;
generate
for (i = 0; i < 24; i = i + 1) begin : BANK_GEN
Bank_16bit u_Bank_16bit (
.clk(clk),
.rst_n(rst_n),
// 端口A:连接PE阵列
.wen_a(wen_pe & (bank_sel == i)),
.addr_a(addr_pe),
.din_a(din_pe),
.dout_a(bank_dout[i]),
// 端口B:连接DRAM预取
.wen_b(wen_dram & (bank_sel == i)),
.addr_b(addr_dram),
.din_b(din_dram),
.dout_b() // 端口B读数据未使用
);
end
endgenerate
// 选择对应Bank的输出到PE和DRAM接口
always @(*) begin
sel_dout_pe = bank_dout[bank_sel];
sel_dout_dram = bank_dout[bank_sel]; // 简化:预取时读同一Bank
end
assign dout_pe = sel_dout_pe;
assign dout_dram = sel_dout_dram;
endmodule
//////////////////////////////////////////////////////////////////////////////////
// 模块:Weight_Cache
// 功能:权重缓存(1KB共享权重缓存 + 两个16KB局部权重缓存)
//////////////////////////////////////////////////////////////////////////////////
module Weight_Cache (
input wire clk, // 时钟
input wire rst_n, // 异步复位,低有效
// 共享权重缓存(与片外DRAM接口)
input wire shared_wen_dram, // DRAM写使能
input wire [8:0] shared_addr_dram, // 地址(深度512,9位)
input wire [15:0] shared_din_dram, // 写数据
output wire [15:0] shared_dout_dram, // 读数据
// 局部权重缓存1(与PE阵列接口)
input wire local1_wen_pe, // PE写使能
input wire local1_wen_shared, // 从共享缓存写使能
input wire [12:0] local1_addr_pe, // PE地址(深度8192,13位)
input wire [12:0] local1_addr_shared, // 共享缓存地址
input wire [15:0] local1_din_pe, // PE写数据
input wire [15:0] local1_din_shared, // 共享缓存写数据
output wire [15:0] local1_dout_pe, // PE读数据
// 局部权重缓存2(与PE阵列接口)
input wire local2_wen_pe,
input wire local2_wen_shared,
input wire [12:0] local2_addr_pe,
input wire [12:0] local2_addr_shared,
input wire [15:0] local2_din_pe,
input wire [15:0] local2_din_shared,
output wire [15:0] local2_dout_pe
);
// 共享权重缓存(单端口SRAM)
reg [15:0] shared_sram [0:511];
reg [15:0] shared_dout_dram_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
shared_dout_dram_reg <= 16'd0;
end else begin
if (shared_wen_dram) begin
shared_sram[shared_addr_dram] <= shared_din_dram;
end
shared_dout_dram_reg <= shared_sram[shared_addr_dram];
end
end
assign shared_dout_dram = shared_dout_dram_reg;
// 局部权重缓存1(双端口SRAM)
reg [15:0] local1_sram [0:8191];
reg [15:0] local1_dout_pe_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
local1_dout_pe_reg <= 16'd0;
end else begin
if (local1_wen_pe) begin
local1_sram[local1_addr_pe] <= local1_din_pe;
end
if (local1_wen_shared) begin
local1_sram[local1_addr_shared] <= local1_din_shared;
end
local1_dout_pe_reg <= local1_sram[local1_addr_pe];
end
end
assign local1_dout_pe = local1_dout_pe_reg;
// 局部权重缓存2(双端口SRAM)
reg [15:0] local2_sram [0:8191];
reg [15:0] local2_dout_pe_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
local2_dout_pe_reg <= 16'd0;
end else begin
if (local2_wen_pe) begin
local2_sram[local2_addr_pe] <= local2_din_pe;
end
if (local2_wen_shared) begin
local2_sram[local2_addr_shared] <= local2_din_shared;
end
local2_dout_pe_reg <= local2_sram[local2_addr_pe];
end
end
assign local2_dout_pe = local2_dout_pe_reg;
endmodule
更多推荐
所有评论(0)