Vortex GPGPU的硬件代码分析(Cache篇1)


前言

本文开始分析Vortex GPGPU的RTL代码了。按照架构来看,我们首先分析cache设计。在GPGPU Core的设计中,cache出现在ICache中,图示并没有表明是否存在DCache,但作者给出的另一张slide里面现时由DCache
在这里插入图片描述
在这里插入图片描述

此外,在Core之上的ProcessorClusterSocket中也存在L3L2L1,如下:
在这里插入图片描述
一些必要的Cache架构图:
在这里插入图片描述
在这里插入图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、Cache代码结构

IP设计代码如下:

VX_cache_bank.sv
VX_cache_bypass.sv
VX_cache_cluster.sv
VX_cache_data.sv
VX_cache_define.vh
VX_cache_init.sv
VX_cache_mshr.sv
VX_cache_tags.sv
VX_cache_top.sv
VX_cache_wrap.sv
VX_cache.sv

关于以上文件的关系如下:
在这里插入图片描述

二、VX_cache.sv代码解读

删除了perf_enable之后的代码如下,这样便于分析!

`include "VX_cache_define.vh"

module VX_cache import VX_gpu_pkg::*; #(
    parameter `STRING INSTANCE_ID    = "",

    // Number of Word requests per cycle
    parameter NUM_REQS              = 4,

    // Size of cache in bytes
    parameter CACHE_SIZE            = 4096, 
    // Size of line inside a bank in bytes
    parameter LINE_SIZE             = 64, 
    // Number of banks
    parameter NUM_BANKS             = 1,
    // Number of associative ways
    parameter NUM_WAYS              = 1,
    // Size of a word in bytes
    parameter WORD_SIZE             = `XLEN/8,

    // Core Response Queue Size
    parameter CRSQ_SIZE             = 2,
    // Miss Reserv Queue Knob
    parameter MSHR_SIZE             = 8, 
    // Memory Response Queue Size
    parameter MRSQ_SIZE             = 0,
    // Memory Request Queue Size
    parameter MREQ_SIZE             = 4,

    // Enable cache writeable
    parameter WRITE_ENABLE          = 1,

    // Request debug identifier
    parameter UUID_WIDTH            = 0,

    // core request tag size
    parameter TAG_WIDTH             = UUID_WIDTH + 1,

    // Core response output register
    parameter CORE_OUT_BUF          = 0,

    // Memory request output register
    parameter MEM_OUT_BUF           = 0
 ) (    
    
    input wire clk,
    input wire reset,

    VX_mem_bus_if.slave     core_bus_if [NUM_REQS],
    VX_mem_bus_if.master    mem_bus_if
);

    `STATIC_ASSERT(NUM_BANKS == (1 << `CLOG2(NUM_BANKS)), ("invalid parameter"))

    localparam REQ_SEL_WIDTH   = `UP(`CS_REQ_SEL_BITS);
    localparam WORD_SEL_WIDTH  = `UP(`CS_WORD_SEL_BITS);
    localparam MSHR_ADDR_WIDTH = `LOG2UP(MSHR_SIZE);
    localparam MEM_TAG_WIDTH   = MSHR_ADDR_WIDTH + `CS_BANK_SEL_BITS;
    localparam WORDS_PER_LINE  = LINE_SIZE / WORD_SIZE;
    localparam WORD_WIDTH      = WORD_SIZE * 8;
    localparam WORD_SEL_BITS   = `CLOG2(WORDS_PER_LINE);
    localparam BANK_SEL_BITS   = `CLOG2(NUM_BANKS);
    localparam BANK_SEL_WIDTH  = `UP(BANK_SEL_BITS);
    localparam LINE_ADDR_WIDTH = (`CS_WORD_ADDR_WIDTH - BANK_SEL_BITS - WORD_SEL_BITS);
    localparam CORE_REQ_DATAW  = LINE_ADDR_WIDTH + 1 + WORD_SEL_WIDTH + WORD_SIZE + WORD_WIDTH + TAG_WIDTH;
    localparam CORE_RSP_DATAW  = WORD_WIDTH + TAG_WIDTH;

    localparam CORE_REQ_BUF_ENABLE = (NUM_BANKS != 1) || (NUM_REQS != 1);
    localparam MEM_REQ_BUF_ENABLE  = (NUM_BANKS != 1);


    wire [NUM_REQS-1:0]                     core_req_valid;
    wire [NUM_REQS-1:0][`CS_WORD_ADDR_WIDTH-1:0] core_req_addr;
    wire [NUM_REQS-1:0]                     core_req_rw;    
    wire [NUM_REQS-1:0][WORD_SIZE-1:0]      core_req_byteen;
    wire [NUM_REQS-1:0][`CS_WORD_WIDTH-1:0] core_req_data;
    wire [NUM_REQS-1:0][TAG_WIDTH-1:0]      core_req_tag;
    wire [NUM_REQS-1:0]                     core_req_ready;

    for (genvar i = 0; i < NUM_REQS; ++i) begin
        assign core_req_valid[i]  = core_bus_if[i].req_valid;
        assign core_req_rw[i]     = core_bus_if[i].req_data.rw;
        assign core_req_byteen[i] = core_bus_if[i].req_data.byteen;
        assign core_req_addr[i]   = core_bus_if[i].req_data.addr;
        assign core_req_data[i]   = core_bus_if[i].req_data.data;
        assign core_req_tag[i]    = core_bus_if[i].req_data.tag;
        assign core_bus_if[i].req_ready = core_req_ready[i];
        `UNUSED_VAR (core_bus_if[i].req_data.atype)
    end    

    ///

    // Core response buffering
    wire [NUM_REQS-1:0]                  core_rsp_valid_s;
    wire [NUM_REQS-1:0][`CS_WORD_WIDTH-1:0] core_rsp_data_s;
    wire [NUM_REQS-1:0][TAG_WIDTH-1:0]   core_rsp_tag_s;
    wire [NUM_REQS-1:0]                  core_rsp_ready_s;

    `RESET_RELAY (core_rsp_reset, reset);

    for (genvar i = 0; i < NUM_REQS; ++i) begin

        VX_elastic_buffer #(
            .DATAW   (`CS_WORD_WIDTH + TAG_WIDTH),
            .SIZE    (CORE_REQ_BUF_ENABLE ? `TO_OUT_BUF_SIZE(CORE_OUT_BUF) : 0),
            .OUT_REG (`TO_OUT_BUF_REG(CORE_OUT_BUF))
        ) core_rsp_buf (
            .clk       (clk),
            .reset     (core_rsp_reset),
            .valid_in  (core_rsp_valid_s[i]),
            .ready_in  (core_rsp_ready_s[i]),
            .data_in   ({core_rsp_data_s[i], core_rsp_tag_s[i]}),
            .data_out  ({core_bus_if[i].rsp_data.data, core_bus_if[i].rsp_data.tag}), 
            .valid_out (core_bus_if[i].rsp_valid),
            .ready_out (core_bus_if[i].rsp_ready)
        );
    end

    ///

    // Memory request buffering
    wire                             mem_req_valid_s;
    wire [`CS_MEM_ADDR_WIDTH-1:0]    mem_req_addr_s;
    wire                             mem_req_rw_s;
    wire [LINE_SIZE-1:0]             mem_req_byteen_s;
    wire [`CS_LINE_WIDTH-1:0]        mem_req_data_s;
    wire [MEM_TAG_WIDTH-1:0]         mem_req_tag_s;
    wire                             mem_req_ready_s;

    VX_elastic_buffer #(
        .DATAW   (1 + LINE_SIZE + `CS_MEM_ADDR_WIDTH + `CS_LINE_WIDTH + MEM_TAG_WIDTH),
        .SIZE    (MEM_REQ_BUF_ENABLE ? `TO_OUT_BUF_SIZE(MEM_OUT_BUF) : 0),
        .OUT_REG (`TO_OUT_BUF_REG(MEM_OUT_BUF))
    ) mem_req_buf (
        .clk       (clk),
        .reset     (reset),
        .valid_in  (mem_req_valid_s), 
        .ready_in  (mem_req_ready_s), 
        .data_in   ({mem_req_rw_s, mem_req_byteen_s, mem_req_addr_s, mem_req_data_s, mem_req_tag_s}),
        .data_out  ({mem_bus_if.req_data.rw, mem_bus_if.req_data.byteen, mem_bus_if.req_data.addr, mem_bus_if.req_data.data, mem_bus_if.req_data.tag}), 
        .valid_out (mem_bus_if.req_valid), 
        .ready_out (mem_bus_if.req_ready)
    );
    
    assign mem_bus_if.req_data.atype = '0;

    ///

    // Memory response buffering
    wire                         mem_rsp_valid_s;
    wire [`CS_LINE_WIDTH-1:0]    mem_rsp_data_s;
    wire [MEM_TAG_WIDTH-1:0]     mem_rsp_tag_s;
    wire                         mem_rsp_ready_s;
        
    VX_elastic_buffer #(
        .DATAW   (MEM_TAG_WIDTH + `CS_LINE_WIDTH), 
        .SIZE    (MRSQ_SIZE),
        .OUT_REG (MRSQ_SIZE > 2)
    ) mem_rsp_queue (
        .clk        (clk),
        .reset      (reset),
        .valid_in   (mem_bus_if.rsp_valid),
        .ready_in   (mem_bus_if.rsp_ready),
        .data_in    ({mem_bus_if.rsp_data.tag, mem_bus_if.rsp_data.data}), 
        .data_out   ({mem_rsp_tag_s, mem_rsp_data_s}), 
        .valid_out  (mem_rsp_valid_s),
        .ready_out  (mem_rsp_ready_s)
    );

    ///

    wire [`CS_LINE_SEL_BITS-1:0] init_line_sel;
    wire init_enable;

    // this reset relay is required to sync with bank initialization
    `RESET_RELAY (init_reset, reset);

    VX_cache_init #( 
        .CACHE_SIZE (CACHE_SIZE),
        .LINE_SIZE  (LINE_SIZE), 
        .NUM_BANKS  (NUM_BANKS),
        .NUM_WAYS   (NUM_WAYS)
    ) cache_init (
        .clk       (clk),
        .reset     (init_reset),
        .addr_out  (init_line_sel),
        .valid_out (init_enable)
    );

    ///
    
    wire [NUM_BANKS-1:0]                        per_bank_core_req_valid;
    wire [NUM_BANKS-1:0][`CS_LINE_ADDR_WIDTH-1:0] per_bank_core_req_addr;
    wire [NUM_BANKS-1:0]                        per_bank_core_req_rw;
    wire [NUM_BANKS-1:0][WORD_SEL_WIDTH-1:0]    per_bank_core_req_wsel;
    wire [NUM_BANKS-1:0][WORD_SIZE-1:0]         per_bank_core_req_byteen;
    wire [NUM_BANKS-1:0][`CS_WORD_WIDTH-1:0]    per_bank_core_req_data;
    wire [NUM_BANKS-1:0][TAG_WIDTH-1:0]         per_bank_core_req_tag;
    wire [NUM_BANKS-1:0][REQ_SEL_WIDTH-1:0]     per_bank_core_req_idx;
    wire [NUM_BANKS-1:0]                        per_bank_core_req_ready;
    
    wire [NUM_BANKS-1:0]                        per_bank_core_rsp_valid;
    wire [NUM_BANKS-1:0][`CS_WORD_WIDTH-1:0]    per_bank_core_rsp_data;
    wire [NUM_BANKS-1:0][TAG_WIDTH-1:0]         per_bank_core_rsp_tag;
    wire [NUM_BANKS-1:0][REQ_SEL_WIDTH-1:0]     per_bank_core_rsp_idx;
    wire [NUM_BANKS-1:0]                        per_bank_core_rsp_ready;

    wire [NUM_BANKS-1:0]                        per_bank_mem_req_valid;    
    wire [NUM_BANKS-1:0][`CS_MEM_ADDR_WIDTH-1:0] per_bank_mem_req_addr;
    wire [NUM_BANKS-1:0]                        per_bank_mem_req_rw;
    wire [NUM_BANKS-1:0][WORD_SEL_WIDTH-1:0]    per_bank_mem_req_wsel;
    wire [NUM_BANKS-1:0][WORD_SIZE-1:0]         per_bank_mem_req_byteen;
    wire [NUM_BANKS-1:0][`CS_WORD_WIDTH-1:0]    per_bank_mem_req_data;
    wire [NUM_BANKS-1:0][MSHR_ADDR_WIDTH-1:0]   per_bank_mem_req_id;
    wire [NUM_BANKS-1:0]                        per_bank_mem_req_ready;

    wire [NUM_BANKS-1:0]                        per_bank_mem_rsp_ready;
    
    if (NUM_BANKS == 1) begin
        assign mem_rsp_ready_s = per_bank_mem_rsp_ready;
    end else begin
        assign mem_rsp_ready_s = per_bank_mem_rsp_ready[`CS_MEM_TAG_TO_BANK_ID(mem_rsp_tag_s)];
    end

    // Bank requests dispatch

    wire [NUM_REQS-1:0][CORE_REQ_DATAW-1:0]  core_req_data_in;    
    wire [NUM_BANKS-1:0][CORE_REQ_DATAW-1:0] core_req_data_out;
    wire [NUM_REQS-1:0][LINE_ADDR_WIDTH-1:0] core_req_line_addr;
    wire [NUM_REQS-1:0][BANK_SEL_WIDTH-1:0]  core_req_bid;
    wire [NUM_REQS-1:0][WORD_SEL_WIDTH-1:0]  core_req_wsel;

    for (genvar i = 0; i < NUM_REQS; ++i) begin
        if (WORDS_PER_LINE > 1) begin
            assign core_req_wsel[i] = core_req_addr[i][0 +: WORD_SEL_BITS];
        end else begin
            assign core_req_wsel[i] = '0;
        end
        assign core_req_line_addr[i] = core_req_addr[i][(BANK_SEL_BITS + WORD_SEL_BITS) +: LINE_ADDR_WIDTH];
    end

    if (NUM_BANKS > 1) begin
        for (genvar i = 0; i < NUM_REQS; ++i) begin
            assign core_req_bid[i] = core_req_addr[i][WORD_SEL_BITS +: BANK_SEL_BITS];
        end
    end else begin
        assign core_req_bid = '0;
    end

    for (genvar i = 0; i < NUM_REQS; ++i) begin
        assign core_req_data_in[i] = {
            core_req_line_addr[i],
            core_req_rw[i],
            core_req_wsel[i],
            core_req_byteen[i],            
            core_req_data[i],
            core_req_tag[i]};
    end


    `RESET_RELAY (req_xbar_reset, reset);

     VX_stream_xbar #(
        .NUM_INPUTS  (NUM_REQS),
        .NUM_OUTPUTS (NUM_BANKS),
        .DATAW       (CORE_REQ_DATAW),
        .PERF_CTR_BITS (`PERF_CTR_BITS),
        .OUT_BUF     ((NUM_REQS > 4) ? 2 : 0)
    ) req_xbar (
        .clk       (clk),
        .reset     (req_xbar_reset),

        `UNUSED_PIN(collisions),
        .valid_in  (core_req_valid),
        .data_in   (core_req_data_in),
        .sel_in    (core_req_bid),
        .ready_in  (core_req_ready),
        .valid_out (per_bank_core_req_valid),
        .data_out  (core_req_data_out),
        .sel_out   (per_bank_core_req_idx),
        .ready_out (per_bank_core_req_ready)
    );

    for (genvar i = 0; i < NUM_BANKS; ++i) begin
        assign {
            per_bank_core_req_addr[i],
            per_bank_core_req_rw[i],
            per_bank_core_req_wsel[i],
            per_bank_core_req_byteen[i],            
            per_bank_core_req_data[i],
            per_bank_core_req_tag[i]} = core_req_data_out[i];
    end
    
    // Banks access
    for (genvar i = 0; i < NUM_BANKS; ++i) begin
        wire [`CS_LINE_ADDR_WIDTH-1:0] curr_bank_mem_req_addr;
        wire curr_bank_mem_rsp_valid;

        if (NUM_BANKS == 1) begin
            assign curr_bank_mem_rsp_valid = mem_rsp_valid_s;
        end else begin
            assign curr_bank_mem_rsp_valid = mem_rsp_valid_s && (`CS_MEM_TAG_TO_BANK_ID(mem_rsp_tag_s) == i);
        end

        `RESET_RELAY (bank_reset, reset);
        
        VX_cache_bank #(                
            .BANK_ID      (i),
            .INSTANCE_ID  (INSTANCE_ID),
            .CACHE_SIZE   (CACHE_SIZE),
            .LINE_SIZE    (LINE_SIZE),
            .NUM_BANKS    (NUM_BANKS),
            .NUM_WAYS     (NUM_WAYS),
            .WORD_SIZE    (WORD_SIZE),
            .NUM_REQS     (NUM_REQS),
            .CRSQ_SIZE    (CRSQ_SIZE),
            .MSHR_SIZE    (MSHR_SIZE),
            .MREQ_SIZE    (MREQ_SIZE),
            .WRITE_ENABLE (WRITE_ENABLE),
            .UUID_WIDTH   (UUID_WIDTH),
            .TAG_WIDTH    (TAG_WIDTH),
            .CORE_OUT_BUF (CORE_REQ_BUF_ENABLE ? 0 : CORE_OUT_BUF),
            .MEM_OUT_BUF  (MEM_REQ_BUF_ENABLE ? 0 : MEM_OUT_BUF)
        ) bank (          
            .clk                (clk),
            .reset              (bank_reset),

                    
            // Core request
            .core_req_valid     (per_bank_core_req_valid[i]),
            .core_req_addr      (per_bank_core_req_addr[i]),
            .core_req_rw        (per_bank_core_req_rw[i]),
            .core_req_wsel      (per_bank_core_req_wsel[i]),
            .core_req_byteen    (per_bank_core_req_byteen[i]),
            .core_req_data      (per_bank_core_req_data[i]),
            .core_req_tag       (per_bank_core_req_tag[i]),
            .core_req_idx       (per_bank_core_req_idx[i]),
            .core_req_ready     (per_bank_core_req_ready[i]),

            // Core response                
            .core_rsp_valid     (per_bank_core_rsp_valid[i]),
            .core_rsp_data      (per_bank_core_rsp_data[i]),
            .core_rsp_tag       (per_bank_core_rsp_tag[i]),
            .core_rsp_idx       (per_bank_core_rsp_idx[i]),
            .core_rsp_ready     (per_bank_core_rsp_ready[i]),

            // Memory request
            .mem_req_valid      (per_bank_mem_req_valid[i]),
            .mem_req_addr       (curr_bank_mem_req_addr),
            .mem_req_rw         (per_bank_mem_req_rw[i]),
            .mem_req_wsel       (per_bank_mem_req_wsel[i]),
            .mem_req_byteen     (per_bank_mem_req_byteen[i]),
            .mem_req_data       (per_bank_mem_req_data[i]),
            .mem_req_id         (per_bank_mem_req_id[i]),
            .mem_req_ready      (per_bank_mem_req_ready[i]),

            // Memory response
            .mem_rsp_valid      (curr_bank_mem_rsp_valid),
            .mem_rsp_data       (mem_rsp_data_s),
            .mem_rsp_id         (`CS_MEM_TAG_TO_REQ_ID(mem_rsp_tag_s)),
            .mem_rsp_ready      (per_bank_mem_rsp_ready[i]),

            // initialization    
            .init_enable        (init_enable),
            .init_line_sel      (init_line_sel)
        );

        if (NUM_BANKS == 1) begin
            assign per_bank_mem_req_addr[i] = curr_bank_mem_req_addr;
        end else begin
            assign per_bank_mem_req_addr[i] = `CS_LINE_TO_MEM_ADDR(curr_bank_mem_req_addr, i);
        end
    end   

    // Bank responses gather

    wire [NUM_BANKS-1:0][CORE_RSP_DATAW-1:0] core_rsp_data_in;
    wire [NUM_REQS-1:0][CORE_RSP_DATAW-1:0]  core_rsp_data_out;

    for (genvar i = 0; i < NUM_BANKS; ++i) begin
        assign core_rsp_data_in[i] = {per_bank_core_rsp_data[i], per_bank_core_rsp_tag[i]};
    end

    `RESET_RELAY (rsp_xbar_reset, reset);

    VX_stream_xbar #(
        .NUM_INPUTS  (NUM_BANKS),
        .NUM_OUTPUTS (NUM_REQS),
        .DATAW       (CORE_RSP_DATAW)
    ) rsp_xbar (
        .clk       (clk),
        .reset     (rsp_xbar_reset),
        `UNUSED_PIN (collisions),
        .valid_in  (per_bank_core_rsp_valid),
        .data_in   (core_rsp_data_in),
        .sel_in    (per_bank_core_rsp_idx),
        .ready_in  (per_bank_core_rsp_ready),
        .valid_out (core_rsp_valid_s),
        .data_out  (core_rsp_data_out),
        .ready_out (core_rsp_ready_s),
        `UNUSED_PIN (sel_out)
    );

    for (genvar i = 0; i < NUM_REQS; ++i) begin
        assign {core_rsp_data_s[i], core_rsp_tag_s[i]} = core_rsp_data_out[i];
    end

    ///

    wire                        mem_req_valid_p;
    wire [`CS_MEM_ADDR_WIDTH-1:0] mem_req_addr_p;
    wire                        mem_req_rw_p;
    wire [WORD_SEL_WIDTH-1:0]   mem_req_wsel_p;
    wire [WORD_SIZE-1:0]        mem_req_byteen_p;
    wire [`CS_WORD_WIDTH-1:0]   mem_req_data_p;
    wire [MEM_TAG_WIDTH-1:0]    mem_req_tag_p;
    wire [MSHR_ADDR_WIDTH-1:0]  mem_req_id_p;
    wire                        mem_req_ready_p;

    // Memory request arbitration

    wire [NUM_BANKS-1:0][(`CS_MEM_ADDR_WIDTH + MSHR_ADDR_WIDTH + 1 + WORD_SIZE + WORD_SEL_WIDTH + `CS_WORD_WIDTH)-1:0] data_in;

    for (genvar i = 0; i < NUM_BANKS; ++i) begin
        assign data_in[i] = {per_bank_mem_req_addr[i],
                             per_bank_mem_req_rw[i],
                             per_bank_mem_req_wsel[i], 
                             per_bank_mem_req_byteen[i],                              
                             per_bank_mem_req_data[i],
                             per_bank_mem_req_id[i]};
    end

    VX_stream_arb #(
        .NUM_INPUTS (NUM_BANKS),
        .DATAW      (`CS_MEM_ADDR_WIDTH + 1  + WORD_SEL_WIDTH + WORD_SIZE + `CS_WORD_WIDTH + MSHR_ADDR_WIDTH),
        .ARBITER    ("R")
    ) mem_req_arb (
        .clk       (clk),
        .reset     (reset),
        .valid_in  (per_bank_mem_req_valid),
        .ready_in  (per_bank_mem_req_ready),
        .data_in   (data_in),
        .data_out  ({mem_req_addr_p, mem_req_rw_p, mem_req_wsel_p, mem_req_byteen_p, mem_req_data_p, mem_req_id_p}),
        .valid_out (mem_req_valid_p),
        .ready_out (mem_req_ready_p),
        `UNUSED_PIN (sel_out)
    );

    if (NUM_BANKS > 1) begin
        wire [`CS_BANK_SEL_BITS-1:0] mem_req_bank_id = `CS_MEM_ADDR_TO_BANK_ID(mem_req_addr_p);
        assign mem_req_tag_p = MEM_TAG_WIDTH'({mem_req_bank_id, mem_req_id_p});            
    end else begin
        assign mem_req_tag_p = MEM_TAG_WIDTH'(mem_req_id_p);
    end   

    // Memory request multi-port handling

    assign mem_req_valid_s = mem_req_valid_p;
    assign mem_req_addr_s  = mem_req_addr_p;
    assign mem_req_tag_s   = mem_req_tag_p;
    assign mem_req_ready_p = mem_req_ready_s;

    if (WRITE_ENABLE != 0) begin
        if (`CS_WORDS_PER_LINE > 1) begin
            reg [LINE_SIZE-1:0]      mem_req_byteen_r;
            reg [`CS_LINE_WIDTH-1:0] mem_req_data_r;

            always @(*) begin
                mem_req_byteen_r = '0;
                mem_req_data_r   = 'x;
                mem_req_byteen_r[mem_req_wsel_p * WORD_SIZE +: WORD_SIZE] = mem_req_byteen_p;
                mem_req_data_r[mem_req_wsel_p * `CS_WORD_WIDTH +: `CS_WORD_WIDTH] = mem_req_data_p;
            end            
            assign mem_req_rw_s     = mem_req_rw_p;
            assign mem_req_byteen_s = mem_req_byteen_r;
            assign mem_req_data_s   = mem_req_data_r;
        end else begin
            `UNUSED_VAR (mem_req_wsel_p)
            assign mem_req_rw_s     = mem_req_rw_p;
            assign mem_req_byteen_s = mem_req_byteen_p;            
            assign mem_req_data_s   = mem_req_data_p;            
        end
    end else begin
        `UNUSED_VAR (mem_req_byteen_p)
        `UNUSED_VAR (mem_req_wsel_p)
        `UNUSED_VAR (mem_req_data_p)
        `UNUSED_VAR (mem_req_rw_p)
        
        assign mem_req_rw_s     = 0;
        assign mem_req_byteen_s = {LINE_SIZE{1'b1}};
        assign mem_req_data_s   = '0;
    end

endmodule

2.1 VX_cache_define.vh的代码总结

首先代码通过include "VX_cache_define.vh"来载入已经定义好的变量,内容并不多:

`ifndef VX_CACHE_DEFINE_VH
`define VX_CACHE_DEFINE_VH

`include "VX_define.vh"   

`define CS_REQ_SEL_BITS         `CLOG2(NUM_REQS)

`define CS_WORD_WIDTH           (8 * WORD_SIZE)
`define CS_LINE_WIDTH           (8 * LINE_SIZE)
`define CS_BANK_SIZE            (CACHE_SIZE / NUM_BANKS)
`define CS_WAY_SEL_BITS         `CLOG2(NUM_WAYS)

`define CS_LINES_PER_BANK       (`CS_BANK_SIZE / (LINE_SIZE * NUM_WAYS))
`define CS_WORDS_PER_LINE       (LINE_SIZE / WORD_SIZE)

`define CS_WORD_ADDR_WIDTH      (`MEM_ADDR_WIDTH-`CLOG2(WORD_SIZE))
`define CS_MEM_ADDR_WIDTH       (`MEM_ADDR_WIDTH-`CLOG2(LINE_SIZE))
`define CS_LINE_ADDR_WIDTH      (`CS_MEM_ADDR_WIDTH-`CLOG2(NUM_BANKS))

// Word select
`define CS_WORD_SEL_BITS        `CLOG2(`CS_WORDS_PER_LINE)
`define CS_WORD_SEL_ADDR_START  0
`define CS_WORD_SEL_ADDR_END    (`CS_WORD_SEL_ADDR_START+`CS_WORD_SEL_BITS-1)

// Bank select
`define CS_BANK_SEL_BITS        `CLOG2(NUM_BANKS)
`define CS_BANK_SEL_ADDR_START  (1+`CS_WORD_SEL_ADDR_END)
`define CS_BANK_SEL_ADDR_END    (`CS_BANK_SEL_ADDR_START+`CS_BANK_SEL_BITS-1)

// Line select
`define CS_LINE_SEL_BITS        `CLOG2(`CS_LINES_PER_BANK)
`define CS_LINE_SEL_ADDR_START  (1+`CS_BANK_SEL_ADDR_END)
`define CS_LINE_SEL_ADDR_END    (`CS_LINE_SEL_ADDR_START+`CS_LINE_SEL_BITS-1)

// Tag select
`define CS_TAG_SEL_BITS         (`CS_WORD_ADDR_WIDTH-1-`CS_LINE_SEL_ADDR_END)
`define CS_TAG_SEL_ADDR_START   (1+`CS_LINE_SEL_ADDR_END)
`define CS_TAG_SEL_ADDR_END     (`CS_WORD_ADDR_WIDTH-1)

`define CS_LINE_TAG_ADDR(x)     x[`CS_LINE_ADDR_WIDTH-1 : `CS_LINE_SEL_BITS]

///

`define CS_LINE_TO_MEM_ADDR(x, i)  {x, `CS_BANK_SEL_BITS'(i)}
`define CS_MEM_ADDR_TO_BANK_ID(x)  x[0 +: `CS_BANK_SEL_BITS]
`define CS_MEM_TAG_TO_REQ_ID(x)    x[MSHR_ADDR_WIDTH-1:0]
`define CS_MEM_TAG_TO_BANK_ID(x)   x[MSHR_ADDR_WIDTH +: `CS_BANK_SEL_BITS]

`define CS_LINE_TO_FULL_ADDR(x, i) {x, (`XLEN-$bits(x))'(i << (`XLEN-$bits(x)-`CS_BANK_SEL_BITS))}
`define CS_MEM_TO_FULL_ADDR(x)     {x, (`XLEN-$bits(x))'(0)}

///

`define PERF_CACHE_ADD(dst, src, dcount, scount) \
    `PERF_COUNTER_ADD (dst, src, reads, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, writes, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, read_misses, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, write_misses, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, bank_stalls, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, mshr_stalls, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, mem_stalls, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, crsp_stalls, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1))

`endif // VX_CACHE_DEFINE_VH

具体来说包括四个部分:宏定义及参数计算部分地址选择部分其他地址转换宏性能计数器宏

2.1.1 宏定义及其参数计算部分

首先是宏定义及参数计算部分(以下的line其实就是cache line,数字8是为了计算bit数):

宏变量 解释
CS_REQ_SEL_BITS 计算请求的选择位数,使用 CLOG2(NUM_REQS)
CS_WORD_WIDTH 定义cache中的字宽,8 * WORD_SIZE
CS_LINE_WIDTH 定义cache的line宽度,8 * LINE_SIZE
CS_BANK_SIZE 定义cache内bank的容量,CACHE_SIZE / NUM_BANKS
CS_WAY_SEL_BITS 定义多路组相联中的路数所需要的bit数,用于选择某路CLOG2(NUM_WAYS)
CS_LINES_PER_BANK 计算每个bank中的line数,CS_BANK_SIZE / (LINE_SIZE * NUM_WAYS)
CS_WORDS_PER_LINE 计算每个line中的words数,LINE_SIZE / WORD_SIZE
CS_WORD_ADDR_WIDTH 计算字word地址宽度,MEM_ADDR_WIDTH-CLOG2(WORD_SIZE)
CS_MEM_ADDR_WIDTH 计算mem地址宽度,MEM_ADDR_WIDTH-CLOG2(LINE_SIZE)
CS_LINE_ADDR_WIDTH 计算line地址宽度,CS_MEM_ADDR_WIDTH-CLOG2(NUM_BANKS)
CS_WORD_SEL_BITS 计算用于word选择,CLOG2(CS_WORDS_PER_LINE)
CS_BANK_SEL_BITS 计算用于bank选择,CLOG2(NUM_BANKS)
CS_LINE_SEL_BITS 计算用于line选择,CLOG2(CS_LINES_PER_BANK)
CS_TAG_SEL_BITS 计算用于tag选择,CS_WORD_ADDR_WIDTH-1-CS_LINE_SEL_ADDR_END

其中CS_WORD_ADDR_WIDTH CS_MEM_ADDR_WIDTHCS_LINE_ADDR_WIDTH是虚拟地址中的重要组成部分。

CS_WORD_ADDR_WIDTH 就是减去了cache line内的block offset后剩余的地址长度。
CS_MEM_ADDR_WIDTH就是减去了单路cache line内的行数表示bit数后剩余的地址长度,其中CLOG2(LINE_SIZE)就是cache内的index
CS_LINE_ADDR_WIDTH就是在减去了单路cache line内的行数表示bit数后剩余的地址长度的基础上,继续减去bank 数量表示bit数后剩余的地址长度。注意这里的cache大概率还是采用多bank设计,所以CLOG2(NUM_BANKS)可以用来索引bank

2.1.2 基于cache的存储地址划分

其次是地址选择部分

宏变量 解释
CS_WORD_SEL_ADDR_START 定义了字选择位在地址中的起始位置,0
CS_WORD_SEL_ADDR_END 定义了字选择位在地址中的结束位置,CS_WORD_SEL_ADDR_START+CS_WORD_SEL_BITS-1
CS_BANK_SEL_ADDR_START 定义了bank选择位在地址中的起始位置,1+CS_WORD_SEL_ADDR_END
CS_BANK_SEL_ADDR_END 定义了bank选择位在地址中的结束位置,CS_BANK_SEL_ADDR_START+CS_BANK_SEL_BITS-1
CS_LINE_SEL_ADDR_START 定义了line选择位在地址中的起始位置,1+CS_BANK_SEL_ADDR_END
CS_LINE_SEL_ADDR_END 定义了line选择位在地址中的结束位置,CS_LINE_SEL_ADDR_START+CS_LINE_SEL_BITS-1
CS_TAG_SEL_ADDR_START 定义了tag选择位在地址中的起始位置,1+CS_LINE_SEL_ADDR_END
CS_TAG_SEL_ADDR_END 定义了tag选择位在地址中的结束位置,CS_WORD_ADDR_WIDTH-1

所以地址各要素,从高位到低位:
| ------------1---------- | ------------2---------- | -----------3----------- | ------------4---------- |
1表示tag;2表示line索引,其实就是cache里提到的index;3表示bank,用于选择特定的cache bank;4表示wordblock offset
对比之前提到的存储地址格式,无非多了个bank索引。
在这里插入图片描述

2.1.3 其他地址转换宏

之后是其他地址转换宏,如下:

宏变量 解释
CS_LINE_TAG_ADDR(x) 宏函数定义是:x[CS_LINE_ADDR_WIDTH-1 : CS_LINE_SEL_BITS]
CS_LINE_TO_MEM_ADDR(x, i) 宏函数定义是:{x, CS_BANK_SEL_BITS(i)},将cache line地址x和索引i组合成完整的地址,用于从存储器中读取或写入数据。
CS_MEM_ADDR_TO_BANK_ID(x) 宏函数定义是:x[0 +: CS_BANK_SEL_BITS],从地址x中提取出用于选择cache中不同bank的标识。
CS_MEM_TAG_TO_REQ_ID(x) 宏函数定义是:x[MSHR_ADDR_WIDTH-1:0],将地址tag x 转换为请求标识符,用于管理和跟踪请求的状态。
CS_MEM_TAG_TO_BANK_ID(x) 宏函数定义是:x[MSHR_ADDR_WIDTH +: CS_BANK_SEL_BITS]
CS_LINE_TO_FULL_ADDR(x, i) 宏函数定义是:{x, (XLEN-$bits(x))(i << (XLEN-$bits(x)-CS_BANK_SEL_BITS))}
CS_MEM_TO_FULL_ADDR(x) 宏函数定义是:{x, (XLEN-$bits(x))'(0)}

先挑几个来讲讲,比如这里的MSHR,这个其实是为了非阻塞cache准备的,也就是允许不满足一个地址请求的时候接着响应之后的请求。说点正经话,MSHR就是"Miss-status Handling Register",用来记录每一项未完成的事务,包括失效地址关键字信息以及重命名寄存器信息 。在cache中,当发生cache miss时,MSHR用于存储之前要访问但未在cache中的请求

具体操作如下:当cache未命中时,首先搜索MSHR看是否有相同的Block也是处于缺失状态,如果找到了,就将当前请求合并到之前的请求中,一起解决历史和此次缺失;如果没有找到,且MSHR还有余位,则分配一个位置;如果没有余位,则发生资源冲突 。

所以MSHR的作用是提高缓存系统的效率,允许在处理一个缓存未命中请求的同时,继续响应其他的存储访问请求 。当缓存未命中被满足后,MSHR会被释放,从而可以处理新的缓存未命中请求 。在现代缓存设计中,MSHR的存在使得缓存系统可以是非阻塞或者无锁的,提高了处理器的性能 。

其他还没做解释的宏函数得先等等!

2.1.4 性能计数器宏

`define PERF_CACHE_ADD(dst, src, dcount, scount) \
    `PERF_COUNTER_ADD (dst, src, reads, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, writes, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, read_misses, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, write_misses, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, bank_stalls, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, mshr_stalls, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, mem_stalls, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1)) \
    `PERF_COUNTER_ADD (dst, src, crsp_stalls, `PERF_CTR_BITS, dcount, scount, (`CDIV(scount, dcount) > 1))

这里涉及两个宏:

宏变量 表达式 文件
PERF_CTR_BITS 44 VX_define.vh
CDIV `define CDIV(n,d) ((n + d - 1) / (d)) VX_platform.vh

关于性能参数观测readswritesread misseswrite missesbank stallsmshr stallsmemory stalls都容易看明白,crsp stalls则指的是当L1 cache没有命中,需要等待来自更高级别cache(如L2或L3 cache)的数据时,就可能发生CRSP stalls,用来宏观衡量缺失代价和缺失率。CDIV按照缩写和表达式大概意思就是对count的除法。

2.1.5 世界线收束1——回归VX_cache.sv的端口介绍

首先是:

module VX_cache import VX_gpu_pkg::*;

其中的import VX_gpu_pkg::*;是导入语句,它告诉编译器解释器去找到名为VX_gpu_pkg的模块或包,并将该模块或包中的所有内容(使用*表示所有)导入到当前模块VX_cache的命名空间中。这种写法的好处在于不用每次都使用VX_gpu_pkg.的前缀来访问变量或者函数。

其中

    // Number of Word requests per cycle
    parameter NUM_REQS              = 4,
    // Size of cache in bytes
    parameter CACHE_SIZE            = 4096, 
    // Size of line inside a bank in bytes
    parameter LINE_SIZE             = 64, 
    // Number of banks
    parameter NUM_BANKS             = 1,
    // Number of associative ways
    parameter NUM_WAYS              = 1,
    // Size of a word in bytes
    parameter WORD_SIZE             = `XLEN/8,

    // Core Response Queue Size
    parameter CRSQ_SIZE             = 2,
    // Miss Reserv Queue Knob
    parameter MSHR_SIZE             = 8, 
    // Memory Response Queue Size
    parameter MRSQ_SIZE             = 0,
    // Memory Request Queue Size
    parameter MREQ_SIZE             = 4,

    // Enable cache writeable
    parameter WRITE_ENABLE          = 1,

    // Request debug identifier
    parameter UUID_WIDTH            = 0,

    // core request tag size
    parameter TAG_WIDTH             = UUID_WIDTH + 1,

    // Core response output register
    parameter CORE_OUT_BUF          = 0,

    // Memory request output register
    parameter MEM_OUT_BUF           = 0

可以获知如下:

参数名及其值 含义
NUM_REQS=4 表示每个周期出现4个word请求
CACHE_SIZE=4096 表示cache大小是4KB,注意这里的cache大小不包括validdirty替换 一致性等辅助性的功能bit
LINE_SIZE=64 表示一个bank内部有64行,因此对于索引只需要6bit
NUM_BANKS=1 表示只有一个bank
NUM_WAYS=1 表示cache采用直接映射
WORD_SIZE=XLEN/8 我们可以在README文件中找到 $ ../configure --xlen=32 --tooldir=$HOME/tools,对于WORD来说是4字节挺符合常理的。
CRSQ_SIZE=2 Core Response Queue Size,定义了核心响应队列的大小。核心通常在处理请求时会生成响应,并将这些响应放入一个队列等待处理或发送给其他部件CRSQ_SIZE就是指定这个队列可以容纳的响应数量。默认为2,意味着核心响应队列最多可以存放2个响应。
MSHR_SIZE=8 Miss Reserve Queue Knob,定义了miss保留队列的大小。MSHR在计算机系统中是用来缓存处理器发出的cache miss请求的数据结构。当处理器发出的请求未能在缓存中找到所需的数据,就会生成一个未命中请求,并放入MSHR中,等待响应。默认为8,表示可以同时缓存和处理最多8个未命中请求。
MRSQ_SIZE=0 Memory Response Queue Size,定义了内存响应队列的大小。在处理器和内存交互时,内存通常会返回请求的数据或响应,这些响应被放置在内存响应队列中,等待处理器接收和处理。此处不接收来自内存应答的响应
MREQ_SIZE=4 Memory Request Queue Size,定义了内存请求队列的大小。当处理器发出请求访问内存时,这些请求可能需要先放入内存请求队列中排队,等待内存控制器的处理。默认为4,表示可以同时缓存和处理最多 4 个内存请求
WRITE_ENABLE=1 Enable cache writeable
UUID_WIDTH=0 Request debug identifier
TAG_WIDTH=UUID_WIDTH + 1 core request tag size
CORE_OUT_BUF=0 Core response output register,定义了核心响应输出缓冲区的大小。在处理器内部,当核心生成响应时,通常会将响应存储在一个输出缓冲区中,以便稍后发送到其他部件或处理器。此处表示不使用额外的缓冲区来存储核心的响应输出。
MEM_OUT_BUF=0 Memory request output register,定义了内存请求输出寄存器的大小。当处理器向内存控制器发送请求时,通常会将请求存储在一个输出寄存器中,以便稍后发送到内存控制器或内存模块。此处表示不使用额外的寄存器来存储内存请求的输出。

值得一说的是,NUM_WAYS=1这个数值是合理的。这是因为,对于现代CPU而言,为了适应Virtual AddressVIPT L1 Cache,往往考虑地址低12bit当作Virtual Tag,因此一般的L1 ICache都是4KB,和大小一样,与此同时一般VIPT L1 ICache 和 DCahe都采用32KB,由于存在比较复杂的aliasing问题(就是有可能出现多个虚拟地址映射到同一个物理地址上而出现cache一致性问题,至于解决方案就是在PIPT L2 Cache中查找到虚拟页号低位或者低2位来定位VIPT L1 DCache中映射向同一物理地址其他虚拟地址),因此会将32KB划分为若干个4KB,所以在Intel Core i7中的L1 DCache都会采用8路组相联

接下来是输入/输出端口:

    input wire clk,
    input wire reset,

    VX_mem_bus_if.slave     core_bus_if [NUM_REQS],
    VX_mem_bus_if.master    mem_bus_if

我们先看看后面两个是怎么回事儿!看下一节!

2.1.6 世界线发散1——讲解VX_mem_bus_if.sv的端口介绍定义

这里的VX_mem_bus_if可以见/mem/VX_mem_bus_if.sv,具体代码如下:

`include "VX_define.vh"

interface VX_mem_bus_if #(
    parameter DATA_SIZE  = 1,
    parameter ATYPE_WIDTH= `ADDR_TYPE_WIDTH,
    parameter TAG_WIDTH  = 1,
    parameter MEM_ADDR_WIDTH = `MEM_ADDR_WIDTH,
    parameter ADDR_WIDTH = MEM_ADDR_WIDTH - `CLOG2(DATA_SIZE)
) ();

    typedef struct packed {
        logic                   rw;
        logic [DATA_SIZE-1:0]   byteen;
        logic [ADDR_WIDTH-1:0]  addr;
        logic [ATYPE_WIDTH-1:0] atype;
        logic [DATA_SIZE*8-1:0] data;
        logic [TAG_WIDTH-1:0]   tag;
    } req_data_t;

    typedef struct packed {
        logic [DATA_SIZE*8-1:0] data;
        logic [TAG_WIDTH-1:0]   tag;
    } rsp_data_t;

    logic  req_valid;
    req_data_t req_data;
    logic  req_ready;

    logic  rsp_valid;
    rsp_data_t rsp_data;
    logic  rsp_ready;

    modport master (
        output req_valid,
        output req_data,
        input  req_ready,

        input  rsp_valid,
        input  rsp_data,
        output rsp_ready
    );

    modport slave (
        input  req_valid,
        input  req_data,
        output req_ready,

        output rsp_valid,
        output rsp_data,
        input  rsp_ready
    );

endinterface

开头的interface VX_mem_bus_if #(...)::定义了一个interface命名为 VX_mem_bus_if#(...) 表示这个接口可以接受一系列的参数。在这个例子中,定义了五个参数:

DATA_SIZE: 数据大小,默认为 1
ATYPE_WIDTH: 地址类型宽度,从头文件中引用了一个宏定义 ADDR_TYPE_WIDTH
TAG_WIDTH: 标签宽度,默认为 1
MEM_ADDR_WIDTH: 内存地址宽度,从头文件中引用了一个宏定义 MEM_ADDR_WIDTH
ADDR_WIDTH: 地址宽度,根据 MEM_ADDR_WIDTH 和 DATA_SIZE 计算得出

中间的typedef struct packed { ... } req_data_t;:定义了一个名为req_data_tpacked结构体,包含了请求数据字段:

读写信号 rw
字节使能 byteen
地址 addr
地址类型 atype
数据 data 
标签 tag

中间的typedef struct packed { ... } rsp_data_t;:定义了一个名为rsp_data_tpacked结构体,包含了响应数据字段:数据data和标签tag。

随后就是信号声明:

req_valid, req_data, req_ready: 定义了请求端的有效信号、数据结构和就绪信号。
rsp_valid, rsp_data, rsp_ready: 定义了响应端的有效信号、数据结构和就绪信号。

最后就是modport定义了接口端口映射,分为masterslave两个模式:

master模式定义了接口从主设备(如 CPU 或者主控制器)到从设备(如内存控制器)的信号传输方向。
slave模式定义了接口从从设备到主设备的信号传输方向。

2.1.7 世界线收束2——回归VX_cache.sv端口内master/slave的介绍

    input wire clk,
    input wire reset,

    VX_mem_bus_if.slave     core_bus_if [NUM_REQS],
    VX_mem_bus_if.master    mem_bus_if

结合2.1.6的内容,有如下:

VX_mem_bus_if接口的slave模式实例化为名为core_bus_if的数组,数组大小为NUM_REQS。这表示有NUM_REQS从设备接口实例,每个接口都遵循VX_mem_bus_if接口的从设备定义,即接收来自主设备(如 CPU)的请求信号和发送响应信号。

VX_mem_bus_if接口的master模式实例化为名为mem_bus_if的信号集合。这个实例用于连接到主设备(如内存控制器),接收来自主设备的请求信号并发送响应信号。

2.2 VX_cache.sv内的常量和局部变量解释

    `STATIC_ASSERT(NUM_BANKS == (1 << `CLOG2(NUM_BANKS)), ("invalid parameter"))

    localparam REQ_SEL_WIDTH   = `UP(`CS_REQ_SEL_BITS);
    localparam WORD_SEL_WIDTH  = `UP(`CS_WORD_SEL_BITS);
    localparam MSHR_ADDR_WIDTH = `LOG2UP(MSHR_SIZE);
    localparam MEM_TAG_WIDTH   = MSHR_ADDR_WIDTH + `CS_BANK_SEL_BITS;
    localparam WORDS_PER_LINE  = LINE_SIZE / WORD_SIZE;
    localparam WORD_WIDTH      = WORD_SIZE * 8;
    localparam WORD_SEL_BITS   = `CLOG2(WORDS_PER_LINE);
    localparam BANK_SEL_BITS   = `CLOG2(NUM_BANKS);
    localparam BANK_SEL_WIDTH  = `UP(BANK_SEL_BITS);
    localparam LINE_ADDR_WIDTH = (`CS_WORD_ADDR_WIDTH - BANK_SEL_BITS - WORD_SEL_BITS);
    localparam CORE_REQ_DATAW  = LINE_ADDR_WIDTH + 1 + WORD_SEL_WIDTH + WORD_SIZE + WORD_WIDTH + TAG_WIDTH;
    localparam CORE_RSP_DATAW  = WORD_WIDTH + TAG_WIDTH;

    localparam CORE_REQ_BUF_ENABLE = (NUM_BANKS != 1) || (NUM_REQS != 1);
    localparam MEM_REQ_BUF_ENABLE  = (NUM_BANKS != 1);

关于UP的介绍在hw/rtl/VX_config.vh中,其中UP的定义如下:

`ifndef UP
`define UP(x)   (((x) != 0) ? (x) : 1)
`endif

也就是说UP的结果至少为1

关于LOG2UP的介绍在hw/rtl/VX_platform.vh中,其定义如下:

`define LOG2UP(x)   (((x) > 1) ? $clog2(x) : 1)

关于地址CS_WORD_ADDR_WIDTH,按照之前的描述,从高位到低位:
| ------------1---------- | ------------2---------- | -----------3----------- | ------------4---------- |
1表示tag;2表示line索引,其实就是cache里提到的index;3表示bank,用于选择特定的cache bank;4表示wordblock offset。(以上均带有CS_前缀)

然后地址MEM_ADDR_WIDTH为:
| ------------1---------- | ------------2---------- | -----------3----------- | ------------4---------- | ------------5---------- |
1表示tag;2表示line索引,其实就是cache里提到的index;3表示bank,用于选择特定的cache bank;4表示wordblock offset;5表示word size,也就是偏移的2个bit来表示4字节取址。(以上均带有CS_前缀)

作为对比,MEM_TAG_WIDTH这里的TAG表示为:

MEM_TAG_WIDTH   = MSHR_ADDR_WIDTH + `CS_BANK_SEL_BITS;

其他还有几个对比项,分别是CORE_REQ_DATAWCORE_RSP_DATAW

CORE_REQ_DATAW  = LINE_ADDR_WIDTH + 1 + WORD_SEL_WIDTH + WORD_SIZE + WORD_WIDTH + TAG_WIDTH;

CORE_RSP_DATAW  = WORD_WIDTH + TAG_WIDTH;

这里几个核心常数的数值如下表:

常数 表达式 数值
LINE_ADDR_WIDTH LINE_ADDR_WIDTH = (CS_WORD_ADDR_WIDTH - BANK_SEL_BITS - WORD_SEL_BITS) ; define CS_WORD_ADDR_WIDTH (MEM_ADDR_WIDTH-CLOG2(WORD_SIZE)) CS_WORD_ADDR_WIDTH=32-log2(4)=30 ; BANK_SEL_BITS=log2(1)=0 ; WORD_SEL_BITS=log2(64/4)=4 ; 因此LINE_ADDR_WIDTH = 26
MEM_ADDR_WIDTH - 32
WORD_SEL_WIDTH UP(CS_WORD_SEL_BITS) ; define CS_WORD_SEL_BITS CLOG2(CS_WORDS_PER_LINE) ; define CS_WORDS_PER_LINE (LINE_SIZE / WORD_SIZE) WORD_SEL_WIDTH = log2(64/4)=4
WORD_SIZE - WORD_SIZE=4
WORD_WIDTH WORD_WIDTH = WORD_SIZE * 8 WORD_WIDTH=4*8=32
TAG_WIDTH TAG_WIDTH = UUID_WIDTH + 1 TAG_WIDTH=0+1=1

所以上述CORE_REQ_DATAWCORE_RSP_DATAW分别为26+1+4+4+32+1=6832+1=33

ps1:关于MEM_ADDR_WIDTH的定义见hw/rtl/VX_config.vh,其表达式如下:

`ifndef MEM_ADDR_WIDTH
`ifdef XLEN_64
`define MEM_ADDR_WIDTH 48
`else
`define MEM_ADDR_WIDTH 32
`endif
`endif

我们在前文提到定义XLEN32,因此MEM_ADDR_WIDTH=32

ps2:关于CS_WORDS_PER_LINE的定义见hw/rtl/cache/VX_cache_define.vh,其表达式如下:

`define CS_WORDS_PER_LINE       (LINE_SIZE / WORD_SIZE)

之后是关于接口定义部分:

    wire [NUM_REQS-1:0]                     core_req_valid;
    wire [NUM_REQS-1:0][`CS_WORD_ADDR_WIDTH-1:0] core_req_addr;
    wire [NUM_REQS-1:0]                     core_req_rw;    
    wire [NUM_REQS-1:0][WORD_SIZE-1:0]      core_req_byteen;
    wire [NUM_REQS-1:0][`CS_WORD_WIDTH-1:0] core_req_data;
    wire [NUM_REQS-1:0][TAG_WIDTH-1:0]      core_req_tag;
    wire [NUM_REQS-1:0]                     core_req_ready;

    for (genvar i = 0; i < NUM_REQS; ++i) begin
        assign core_req_valid[i]  = core_bus_if[i].req_valid;
        assign core_req_rw[i]     = core_bus_if[i].req_data.rw;
        assign core_req_byteen[i] = core_bus_if[i].req_data.byteen;
        assign core_req_addr[i]   = core_bus_if[i].req_data.addr;
        assign core_req_data[i]   = core_bus_if[i].req_data.data;
        assign core_req_tag[i]    = core_bus_if[i].req_data.tag;
        assign core_bus_if[i].req_ready = core_req_ready[i];
        `UNUSED_VAR (core_bus_if[i].req_data.atype)
    end    

还是老样子,给出相关地址长度/宽度:

常数 表达式 数值
CS_WORD_ADDR_WIDTH define CS_WORD_ADDR_WIDTH (MEM_ADDR_WIDTH-CLOG2(WORD_SIZE)) CS_WORD_ADDR_WIDTH=32-log2(4)=30
WORD_SIZE ` WORD_SIZE=4
CS_WORD_WIDTH define CS_WORD_WIDTH (8 * WORD_SIZE) CS_WORD_WIDTH=8*4=32
TAG_WIDTH TAG_WIDTH = UUID_WIDTH + 1 TAG_WIDTH=0+1=1

ps1:关于CS_WORD_ADDR_WIDTH的定义见hw/rtl/cache/VX_cache_define.vh。表达式如下:

`define CS_WORD_ADDR_WIDTH      (`MEM_ADDR_WIDTH-`CLOG2(WORD_SIZE))

ps2:关于CS_WORD_WIDTH的定义见hw/rtl/cache/VX_cache_define.vh。表达式如下:

`define CS_WORD_WIDTH           (8 * WORD_SIZE)

先到这里为止。


总结

本文主要针对VX_cache.sv分析常见常量、推导常量数值、接口定义。做好例化模块的前置工作,接下来打算逐个分析模块功能。

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 06:12:08       53 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 06:12:08       55 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 06:12:08       46 阅读
  4. Python语言-面向对象

    2024-07-11 06:12:08       56 阅读

热门阅读

  1. Flutter RSA公钥转PEM

    2024-07-11 06:12:08       21 阅读
  2. CentOS 系统监控项

    2024-07-11 06:12:08       19 阅读
  3. UCOS-III 与UCOS-III主要功能差异

    2024-07-11 06:12:08       14 阅读
  4. 用 adb 来模拟手机插上电源和拔掉电源的情形

    2024-07-11 06:12:08       19 阅读
  5. OpenResty程序如何连接开启了TLS的Redis?

    2024-07-11 06:12:08       22 阅读
  6. Jitsi Meet指定用户成为主持人

    2024-07-11 06:12:08       17 阅读
  7. Rust编程-编写自动化测试

    2024-07-11 06:12:08       24 阅读
  8. 开源大势所趋

    2024-07-11 06:12:08       21 阅读
  9. Sqlmap中文使用手册 - Target模块参数使用

    2024-07-11 06:12:08       23 阅读
  10. Grind 75 - Leetcode146 LRU缓存

    2024-07-11 06:12:08       23 阅读