【计算机组织与体系结构】实验四:指令 CACHE 的设计与实现

本文详细介绍了使用Vivado和Verilog实现指令Cache的过程,包括实验目的、环境、内容和设计思想。实验内容涵盖指令Cache的模块设计、类Sram和AXI协议的实现,以及状态自动机的建立。通过实现二/四路组相联的Cache,加深了对Cache结构和工作原理的理解。最后,进行了功能测试和实验总结。
摘要由CSDN通过智能技术生成

系列文章目录

【计算机组织与体系结构】实验一:算术逻辑单元的实现
【计算机组织与体系结构】实验二:给定指令系统的处理器设计
【计算机组织与体系结构】实验三:流水线处理器
【计算机组织与体系结构】实验四:指令 CACHE 的设计与实现



一、实验目的

  • 掌握 Vivado 集成开发环境
  • 掌握 Verilog 语言
  • 掌握 FPGA 编程方法及硬件调试手段
  • 深刻理解指令 Cache 的结构和整体工作原理

二、实验环境

Vivado 集成开发环境

三、实验内容

根据课程第八章所讲的存储系统的相关知识,自行设计一个指令 cache,并使用 Verilog 语言实现之。要求设计的指令 Cache 可以是 2 路或 4 路,每路 128 行,每行 32 字节,最终实现的 cache 能够通过所提供的自动测试环境。

1、指令 Cache 各模块及工作过程的介绍

指令 Cache 介于 CPU 和主存之间。其使用类 Sram 接口与 CPU 进行通信,当 CPU 需要使用数据时,将 Cache内缓存的数据发送给 CPU;使用 AXI 接口与主存进行通信,当Cache 发生不命中时,将主存中的数据替换到Cache 的数据块内。因此,要实现一个指令 Cache ,一方面我们需要知道其内部的构成(在本节中将会详细阐述),另一方面我们需要知道 Cache 和 CPU 以及 AXI 总线是如何相互配合工作的(在后两节实现类 Sram协议和实现 AXI 协议中会详细阐述)

如下图所示,一个简单的指令 Cache 内部分主要由两级流水段和控制模块构成,本次实验已经提供了目录表tagv_ram 以及数据块 data_ram 的实现,同学们主要需要在已给的 Cache 代码框架(见文件./cache.v)中实现第一级流水的请求缓存 request_buffer 以及控制模块部分。当然,对于学有余力的同学可以尝试自己实现设计并实现 Cache 主模块、目录表和数据块。
在这里插入图片描述
接下来,我们将具体介绍 Cache 两级流水段和控制模块的工作原理和实现。

两级流水段

指令 Cache 支持两级流水段的流水访存。两级流水段为地址流水段和数据流水段,分别与 CPU 的地址握手阶段和数据握手阶段相配合。Cache 在地址流水段从 CPU 接收并存储访存地址,在数据流水段查找并返回数
据。

对于地址流水段,主要模块是一个请求缓存 request_buffer ,其由多个用于缓存访存地址等信息的寄存器构成。在 CPU 将 cpu_req 拉高且 Cache 未阻塞时,请求缓存会解析出访存地址的 tag、index、offset 等信息。其中,index 和 tag 会被送到下一级流水段中的目录表(图中 Tagv Ram )中,index 和 offset 会被送到下一级流水段中的数据块中(图中 Data Ram )中。

请求缓存将这些信息以及访存地址缓存到寄存器中,同时将原本的访存地址缓存在一个额外的寄存器ll_address 中。其之所以要缓存上上次的访存地址,是因为此时 Cache 刚刚判断出上上次的访存是否命中,若未命中则需要利用 ll_address 中缓存的上上次访存地址向内存中读取数据。

Note: 关于地址的解析,由于本实验要求的 Cache 每路为 128 行,因此 index 需要 7 位,即 2^7 = 128;每行由 8 个块构成,每个块 32 位(4 个字节),因此 offset 需要 5 位,即 2^5 = 8 * 4;地址其余 20 位作为 tag 。
在这里插入图片描述
对于数据流水段,主要有两个模块,分别为目录表和数据块。这两个模块均已给出 verilog 实现示例,同学们可以直接使用或根据自己的设计进行适当修改,也可以尝试自己实现。

目录表用于存储缓存数据内存地址的 tag ,当 CPU 发起访存请求时,Cache 通过到目录表查找访存地址的 tag来判断缓存是否命中。根据本实验要求,Cache 每路共有 128 行,每行一个 tag ,故每路目录表需要维护一个128 * 20 bit 的 tag 数组,同时,每行还需要额外一位有效位来标记该行是否有缓存数据,故每路目录表需要维护一个 128 * (20+1) bit 大小的数组。此外,目录表还需要支持写入,在 Cache 未命中从内存读取访存数据时,需要对目录表进行更新。以下给出一个使用 Verilog 实现的二路 Cache 的目录表示例供大家参考。

/* 该目录表仅缓存了一路的 tag,对于两路的设计需要例化两个该目录表 */
/* 该目录表需要一拍时间对访存是否命中进行判断 */
/* 该目录表需要一拍时间进行 tag 的写入 */
module icache_tagv(
input clk, // 时钟信号
input wen, // 写使能
input valid_wdata, // 写入有效位的值,在重启刷新 cache 时为 0,其他情况为 1
input [6 :0] index, // 查找 tag 或写入时所用的索引
input [19:0] tag, // CPU 访存地址的 tag
output hit // 命中结果
);
/* --------TagV Ram------- */
// | tag | valid |
// |20 1|0 0|
reg [20:0] tagv_ram[127:0];
/* --------Write-------- */
always @(posedge clk) begin
if (wen) begin
tagv_ram[index] <= {
   tag, valid_wdata};
end
end
/* --------Read-------- */
reg [20:0] reg_tagv;
reg [19:0] reg_tag;
always @(posedge clk) begin
reg_tagv = tagv_ram[index];
reg_tag = tag;
end
assign hit = (reg_tag == reg_tagv[20:1]) && reg_tagv[0];
endmodule

数据块中则真正缓存了数据,其根据 index 查找到对应的 Cache 行,根据 offset 最终确定访问的数据。根据本实验要求,Cache 每路共有 128 行,每行 32 字节,故每路数据块大小为 128 * 32 B 。由于数据块较大,建议使用 Vivado 提供的 block memory generator 自动生成 ram ip 核来实现。

以实现上述一路数据块为例,生成 ip 核步骤如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最后给出一个使用 Verilog 实现的二路 Cache 的数据块示例供大家参考。
Note: 在使用数据块实例模块时,需要根据上文在实验项目中添加 ram ip 核,但在设置 read width 和write width 时需要改为 32 而非 256 。

/* 该数据块仅缓存了一路的数据,对于两路的设计需要例化两个该数据块 */
/* 该数据块的写入和读取数据均需要一拍的时间 */
module icache_data(
input clk, // 时钟信号
input [31 :0] wen, // 按字节写使能,如 wen = 32'hf000000,则只写入目标行的 [31:0]
input [6 :0] index, // 访存或写入的索引
input [4 :0] offset, // 访存的偏移量
input [255:0] wdata, // 写入的数据
output [31 :0] rdata // 访存读出的数据
);
// 由于 Cache 一次读一行,故需要缓存 offset 在读出一行后利用其确定最终的 4 字节
reg [4:0] last_offset;
always @(posedge clk) begin
last_offset <= offset;
end
//-----调用 IP 核搭建 Cache 的数据存储器-----
wire [31:0] bank_douta [7:0];
/*
Cache_Data_RAM: 128 行,每行 32bit,共 8 个 ram
接口信号含义: clka:时钟信号
ena: 使能信号,控制整个 ip 核是否工作
wea:按字节写使能信号,每次写 4 字节,故 wea 有 4 位
addra:地址信号,说明读/写的地址
dina:需要写入的数据,仅在 wea == 1 时有效
douta:读取的数据,在 wea == 0 时有效,从地址 addra 处读取出数据
*/
generate
genvar i;
for (i = 0 ; i < 8 ; i = i + 1) begin
inst_ram BANK(
.clka(clk),
.ena(1'b1),
.wea(wen[i*4+3:i*4]),
.addra(index),
.dina(wdata[i*32+31:i*32]),
.douta(bank_douta[7-i])
);
end
endgenerate
assign rdata = bank_douta[last_offset[`ICACHE_OFFSET_WIDTH-1:2]];
endmodule

Note: 对于指令 Cache 数据块的写入仅在 Cache 未命中从内存读取数据时才会发生,由于本实验中 AXI 总线单次仅返回 4 字节,故每次对于数据块也只写入 4 字节,这里给出一个简单的示例说明如何使用 icache_data模块实现特定 4 字节的写入。

该模块涉及写入的接口为 wen、index、wdata,若需要写入的数据 index = 7’h00, offset = 5’h04,data = 32’h12345678,则对接口赋值 wen = 32’h0f000000, index =

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值