直接映射

先看懂cache的映射原理,根据cache大小与主存大小来计算各个信号线的位数

各个信号线位数

计算机组成实验—Cache的实现插图

主存地址在逻辑上分为区号、块号、块内地址

计算机组成实验—Cache的实现插图(1)

Cache结构

计算机组成实验—Cache的实现插图(2)

Cache访问原理

计算机组成实验—Cache的实现插图(3)

基本过程

计算机组成实验—Cache的实现插图(4)

状态机:“三段式”实现 6.3 Verilog 状态机 | 菜鸟教程 (runoob.com)

// TODO: 编写状态机现态的更新逻辑

// TODO: 编写状态机的状态转移逻辑

// TODO: 生成状态机的输出信号

状态机设计

计算机组成实验—Cache的实现插图(5)

再分析并确定各个状态下的输入输出,完善状态机。

编写状态机更新逻辑

这个类似模板了

// TODO: 编写状态机现态的更新逻辑
    always @(posedge cpu_clk or posedge cpu_rst) begin
        if(cpu_rst) begin
            sta <= 2'b0;
        end
        else begin
            sta <= nex_sta;
        end
    end
编写状态机状态转移信号

需要以下变量:新的访问请求信号、命中信号、缺失(不命中)、主存数据已返回

    // TODO: 编写状态机的状态转移逻辑
    always@(*) begin 
        if(cpu_rst) begin
            nex_sta = IDLE;
        end
        else begin
            case(sta) 
            IDLE: begin 
                if(inst_rreq == 1'b1) begin 
                    nex_sta = TAG_CHECK;
                end
                else begin 
                    nex_sta = IDLE;
                end
            end
            TAG_CHECK:begin 
                if(hit) begin
                    nex_sta = IDLE;
                end
                else begin 
                    nex_sta = REFILL;
                end
            end
            REFILL: begin
                if(mem_rvalid) begin
                    nex_sta = TAG_CHECK;
                end
                else begin 
                    nex_sta = REFILL;
                end
            end
            default: nex_sta = IDLE;
            endcase
        end
    end
编写状态机输出信号

分为向主存输出和向cpu输出

IDLE状态下是赋默认值

TAG_CHECK状态下是看否命中,来确定输出(若未命中,要从主存里读东西出来,放到cache里面,再从cahce里来一次是否命中的判断)

REFILL状态下,如果主存准备信号(mem_rrdy)为真,则可以发送主存读所需要的地址和使能信号(注意只提供一个时钟周期)

错误总结:

  1. 一直在思考如何让发送给主存的信号只有一个时钟周期,由于有 mem_rrdy 的限制,以及不符合打两拍的场景,所以打两拍操作最后是不行的(打两拍可参照inst_valid和inst_out单周期有效的方法,打两拍的应用场景:要在单位信号从0到1,的属于1的那个周期里,发送一个周期的有效信号)
  2. 还有,块是整块取,块的取址地址要是模四为零的。
  3. 忘了mem_addr的有效时间只有一个周期,在后面也使用到了它,哭哭。

贴一个完整代码

`timescale 1ns / 1ps

 `define BLK_LEN  4
 `define BLK_SIZE (`BLK_LEN*32)


module ICache(
    input  wire         cpu_clk,
    input  wire         cpu_rst,        // high active
    // Interface to CPU
    input  wire         inst_rreq,      // 来自CPU的取指请求
    input  wire [31:0]  inst_addr,      // 来自CPU的取指地址
    output reg          inst_valid,     // 输出给CPU的指令有效信号(读指令命中)
    output reg  [31:0]  inst_out,       // 输出给CPU的指令
    // Interface to Read Bus
    input  wire         mem_rrdy,       // 主存就绪信号(高电平表示主存可接收ICache的读请求)
    output reg  [ 3:0]  mem_ren,        // 输出给主存的读使能信号
    output reg  [31:0]  mem_raddr,      // 输出给主存的读地址
    input  wire         mem_rvalid,     // 来自主存的数据有效信号
    input  wire [`BLK_SIZE-1:0] mem_rdata   // 来自主存的读数据
);

`ifdef ENABLE_ICACHE    /******** 不要修改此行代码 ********/

    wire [4:0] tag_from_cpu   = inst_addr[14:10];    // 主存地址的TAG
    wire [3:0] offset         = inst_addr[3:0];    // 32位字偏移量
    wire       valid_bit      = cache_line_r[`BLK_SIZE + 5];    // Cache行的有效位
    wire [4:0] tag_from_cache = cache_line_r[`BLK_SIZE + 4 : `BLK_SIZE];    // Cache行的TAG

    // TODO: 定义ICache状态机的状态变量
    parameter IDLE = 2'b00;
    parameter TAG_CHECK = 2'b01;
    parameter REFILL = 2'b10;
    reg [1:0] sta, nex_sta;


    wire hit = (sta == TAG_CHECK) ? (valid_bit && (tag_from_cache == tag_from_cpu)) : 1'b0;
    wire[6:0] offset_bit = {offset,3'b000};
    wire[`BLK_SIZE + 5:0] data_out = cache_line_r >> offset_bit;

    always @(*) begin
        if(hit & hit_n) begin 
            inst_valid = 1'b1;
            inst_out   = data_out[31:0];
        /* TODO: 根据字偏移,选择Cache行中的某个32位字输出指令 */
        end
        else begin 
            inst_valid = 1'b0;
            inst_out = 32'b0;
        end
    end

    reg hit_n ;
    always@(posedge cpu_clk) begin 
        hit_n <= ~hit;
    end
    
    reg inst_addr_reg;
    

    wire       cache_we     = mem_rvalid;     // ICache存储体的写使能信号
    wire [5:0] cache_index  = inst_addr[9:4];     // 主存地址的Cache索引 / ICache存储体的地址
    wire [`BLK_SIZE + 5:0] cache_line_w = {1'b1,inst_addr[14:10],mem_rdata};     // 待写入ICache的Cache行
    wire [`BLK_SIZE + 5:0] cache_line_r;                  // 从ICache读出的Cache行
    //135 = 1(有效位) + 7(TAG的位数) +  128(数据位数)


    // ICache存储体:Block MEM IP核
    blk_mem_gen_1 U_isram (
        .clka   (cpu_clk),
        .wea    (cache_we),
        .addra  (cache_index),
        .dina   (cache_line_w),
        .douta  (cache_line_r)
    );

    // TODO: 编写状态机现态的更新逻辑
    always @(posedge cpu_clk or posedge cpu_rst) begin
        if(cpu_rst) begin
            sta <= 2'b0;
        end
        else begin
            sta <= nex_sta;
        end
    end


    // TODO: 编写状态机的状态转移逻辑
    always@(*) begin 
        if(cpu_rst) begin
            nex_sta = IDLE;
        end
        else begin
            case(sta) 
            IDLE: begin 
                if(inst_rreq == 1'b1) begin 
                    nex_sta = TAG_CHECK;
                end
                else begin 
                    nex_sta = IDLE;
                end
            end
            TAG_CHECK:begin 
                if(hit) begin
                    nex_sta = IDLE;
                end
                else begin 
                    nex_sta = REFILL;
                end
            end
            REFILL: begin
                if(mem_rvalid) begin
                    nex_sta = TAG_CHECK;
                end
                else begin 
                    nex_sta = REFILL;
                end
            end
            default: nex_sta = IDLE;
            endcase
        end
    end

   // reg mem_rrdy_n;
   reg mem_ren_pulse;
   always @(posedge cpu_clk or posedge cpu_rst) begin 
        if(cpu_rst) begin 
        mem_ren_pulse <= 1'b0;
        end
        else if(sta == REFILL) begin 
            if(mem_rrdy && !mem_ren_pulse) begin 
                mem_ren_pulse <= 1'b1;
            end
        end 
        else begin
            mem_ren_pulse <= 1'b0;
        end      
   end 

    // TODO: 生成状态机的输出信号
    always @(*) begin
        if(cpu_rst) begin 
            mem_ren = 4'b0;
            mem_raddr = 32'b0;
           // mem_rrdy_n <= 1'b1;
        end
        else begin 
            case(sta) 
            IDLE : begin
                mem_ren = 4'b0;
                mem_raddr = 32'b0;
               // mem_rrdy_n <= 1'b1;
            end
            TAG_CHECK : begin 
                mem_ren = 4'b0;
                mem_raddr = 32'b0;
               // mem_rrdy_n <= 1'b1;
            end
            REFILL: begin
                if(mem_rrdy && !mem_ren_pulse) begin 
                    mem_ren = 4'b1111;
                    mem_raddr = {inst_addr[31:4],4'b0000}; 
                   // mem_rrdy_n <= 1'b0;
                end
                else begin 
                    mem_ren = 4'b0;
                    mem_raddr = 32'b0;
                end               
            end
            default: begin 
                mem_ren = 4'b0;
                mem_raddr = 32'b0;
               // mem_rrdy_n <= 1'b1;
            end
            endcase
        end

    end


    /******** 不要修改以下代码 ********/
`else

    localparam IDLE  = 2'b00;
    localparam STAT0 = 2'b01;
    localparam STAT1 = 2'b11;
    reg [1:0] state, nstat;

    always @(posedge cpu_clk or posedge cpu_rst) begin
        state <= cpu_rst ? IDLE : nstat;
    end

    always @(*) begin
        case (state)
            IDLE:    nstat = inst_rreq ? (mem_rrdy ? STAT1 : STAT0) : IDLE;
            STAT0:   nstat = mem_rrdy ? STAT1 : STAT0;
            STAT1:   nstat = mem_rvalid ? IDLE : STAT1;
            default: nstat = IDLE;
        endcase
    end

    always @(posedge cpu_clk or posedge cpu_rst) begin
        if (cpu_rst) begin
            inst_valid <= 1'b0;
            mem_ren    <= 4'h0;
        end else begin
            case (state)
                IDLE: begin
                    inst_valid <= 1'b0;
                    mem_ren    <= (inst_rreq & mem_rrdy) ? 4'hF : 4'h0;
                    mem_raddr  <= inst_rreq ? inst_addr : 32'h0;
                end
                STAT0: begin
                    mem_ren    <= mem_rrdy ? 4'hF : 4'h0;
                end
                STAT1: begin
                    mem_ren    <= 4'h0;
                    inst_valid <= mem_rvalid ? 1'b1 : 1'b0;
                    inst_out   <= mem_rvalid ? mem_rdata[31:0] : 32'h0;
                end
                default: begin
                    inst_valid <= 1'b0;
                    mem_ren    <= 4'h0;
                end
            endcase
        end
    end

`endif

endmodule

本站无任何商业行为
个人在线分享 » 计算机组成实验—Cache的实现
E-->