注意:原创文章,如需转载请务必标明转载,并附上原文出处链接!!!侵权必究!
作者 : thundersnark
邮箱 : thundersnark@gmail.com
个人邮箱,不是经常查看,如有问题联系请发邮件的同时在本文末尾回复一下!
/******************************************************************************
This Source Code Form is subject to the terms of the
Open Hardware Description License, v. 1.0. If a copy
of the OHDL was not distributed with this file, You
can obtain one at http://juliusbaxter.net/ohdl/ohdl.txt
Description: Instruction cache implementation
Copyright (C) 2012-2013
Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
Stefan Wallentowitz <stefan.wallentowitz@tum.de>
******************************************************************************/
// 关于这个mor1kx_icache module的整体性说明
// 1、这个icache实现是组相联cache结构,关于什么是组相联cache的结构,请自行查阅书籍或search internet
//
// 2、该cache结构中默认参数所实现的是 cache有2条way(路),way之间是全相连;每条way中分成了2^9个Block(块);所有way中处于
// 同一位置处的Block组成一个set(组);
// 举个例子:假设有2条way,每条way有3个Block;主存中那么cache的形式为:
// cache way0: way0_Block0, way0_Block1, way0_Block2;
// cache way1: way1_Block0, way1_Block1, way1_Block2;
// 3个set : set0 , set1 , set2 ;即set0由way0_Block0和way1_Block0组成,其余set类推
// 也就是说 cache中的set总数和一条way中的Block的数量是相同的;
// 一个set内的Block数和cache中的way数量是相同的
//
// 3、Block是主存和cache相映射的最小单元,即每当没有命中的时候cache需要更新(refill)一个Block;
// 每个Block的深度是2^5即32Byte,即8个word也就是8条指令为一个Block;主存与每条way中的Block是直接相连映射
//
// 4、具体映射方式可以这样理解:主存以一条way的大小来分割成一个个区域;每个区域中又按照Block的大小分割成Block
// 直接相连映射指的就是主存的每个区域中的Block只能放到cache的way中的相应位置的Block处(即只能放到特定的组);
// 全相连映射指的就是从主存中一个区域中取出的Block能够放入cache中任意一条way中相应Block的位置(可以任意放到组内的任意一个Block);
// 即way之间(也就是set内部)是全相连;way内部(也就是set之间)是直接映射;
// 举个例子:假设主存的结构被划分成下面这样:
// mem0_Block0, mem0_Block1, mem0_Block2; mem1_Block0,mem1_Block1,mem1_Block2; mem2_Block0,mem2_Block1,mem2_Block2;
// 其中mem0,mem1,mem2是将主存以每个way的空间大小划分成的区,区内又划分成Block,结合上面举的cache的例子,
// 那么mem1的Block2只能放到set2中,也就是只能放到way0的block2处或way1的block2处;至于是way1和way2则是任意的;
// 在这个例子中;way之间(set内部)是全相连的具体体现就是上述Block可以任意放到way1或way2;
// way内部(也就是set之间)是直接映射的具体体现就是上述Block只能放到way的Block2处;
// (注:怕讲不明白,所以写的有点啰嗦;此外网上有些博客上写的cache的组相联结构分了line,block,set,way等等,
// 写的很繁杂,绕来绕去;我没仔细去查阅相应的计算机架构的书籍,也不清楚他们是不是真正写对了,我这里写的这个按way
// 和block和set来进行组相联的cache结构仅仅是我根据mor1k的icache代码实现总结出来的,也不保证完全正确(至少我自己
// 现在认为没问题!)如果有什么问题可以在下面留言我看到的话会回答^_^,但平时比较忙不一定会及时哈~)
// 4、这个代码的主要状态分为三个即 1、cpu读指令 2、cpu读指令miss的情况下进行cache refill 3、写spr寄存器清空cache中的Block
//
// 5、关于cache和immu相关联的地方主要有两处 1、cpu读指令的时候地址有两个,cpu_adr_i和cpu_adr_match_i分别是虚拟地址和物理地址
// 当然,前提是immu使能了,如果immu不是能的话两个都是虚拟地址,也可以理解成两个都是物理地址;2、refill cache的wadr_i
// 从这里可以看处写cache的时候只用了一个地址,而读cache的时候用了两个地址;从fetch模块的代码中可以看出wadr_i在immu使能
// 的时候写入cache的是物理地址,而immu不是能的时候写入的是虚拟地址;这就存在一个问题,也就是说当immu使能的时候,写入icache
// 的地址时物理地址,tag mem中存放的也是物理地址的tag;而采用cpu_adr_i这个虚拟地址对tag mem和way mem进行索引,再讲索引的结果
// 与cpu_adr_match_i这个物理地址的tag进行比较;这时不合理的;为了解释这个问题,观察代码中用到cpu_adr_i的地方,可以看出,
// 对tag mem和way mem进行索引的时候只用到了cpu_adr_i[WAY_WIDTH-1:OPTION_ICACHE_BLOCK_WIDTH]这一部分,
// 所以我猜测,不管immu怎么样,虚拟地址和物理地址的[WAY_WIDTH-1:0]这一区域都是相同的,不同的仅仅是两个地址的tag部分;
// 需要等到我看完immu指令部分的代码,再回头确认这个问题!!!
//
`include "mor1kx-defines.v"
module mor1kx_icache
#(
parameter OPTION_OPERAND_WIDTH = 32, //指令宽度
parameter OPTION_ICACHE_BLOCK_WIDTH = 5, //block(块)的宽度,5bit,即每个块由2^5即32个Byte组成
//块是cache中存放的主存数据的最小单位
parameter OPTION_ICACHE_SET_WIDTH = 9, //
parameter OPTION_ICACHE_WAYS = 2,
parameter OPTION_ICACHE_LIMIT_WIDTH = 32
)
(
input clk,
input rst,
//这里应该是指令的prefetch模块跟icache的接口
input ic_imem_err_i, //instruction memory err
input ic_access_i, //指示cpu是否请求读icache
output refill_o, //指示当前icache模块处于refill(重填)某一个way中的某一Block的状态
output refill_req_o, //需要refill icache;此信号的assert会从read不命中开始直到refill Block结束
output refill_done_o, //refill结束
output invalidate_o, //处于通过写spt寄存器使cache的set invalidate的状态
output cache_hit_o, //cache命中
// CPU Interface
// CPU对于cache的操作分为两种,一种从cache中取指令的操作,令一种是将指令写
// 入cache的操作,也就是refill cache
// 取指令操作对应的信号如下:
output cpu_ack_o,
output reg [`OR1K_INSN_WIDTH-1:0] cpu_dat_o, //输出给CPU的指令
input [OPTION_OPERAND_WIDTH-1:0] cpu_adr_i, // CPU读指令时给出的虚拟地址,这个地址用来索引icache的tag mem和way mem
in