目录

1.片上存储系统原理

1.1 多级缓存架构

1.2 并行访问与Bank机制

1.3 数据复用与阵列划分

1.4 缓存控制器

2.片上存储系统实现过程

2.1 硬件模块划分

2.2 数据缓存的实现步骤

2.3 权重缓存的实现步骤

2.4 缓存控制器的实现步骤

3.片上存储系统的Verilog实现


       片上存储系统是连接计算单元(如处理单元 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
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐