看懂gem5对MSI的建模(一)
前缀知识,了解M、S、I状态对缓存行来说意味着什么,体系结构基础。
在多核中,我们先只看多L1 cache + L2 cache + 内存模型,此时架构图如下面

dictory(目录)负责存储额外信息,而L2cache负责存储数据,额外信息指的是[addr,owner,[shared],state],addr是全局物理地址的意思,owner是对享有M类型数据的独占者,shared数组是共享了S类型数据的所有人,state是这个地址在L2的状态,如果有来访发现是M,则会让独占者转发数据给来访者,如果来访发现是S,则会让共享者转发数据,遵循MSI标准
基于这个架构,我们先不考虑TBE,简单的分析一下L1cache到底会发出哪些请求(也就是基本事务的个数),gem5并不对此做定义,这是我归纳总结得来的,我会以来访者 以 通道类型 发出 事务类型 到 对象 的形式进行简化
(1)core0发出getS (2)core0发出getM (3)core0发出putS (4)core0发出putM
(1)基于CPU的load/ifetch指令,core0希望读到一个合法的数据但是不修改,但是L1里不命中,core0 L1通过request通道发出getS给dictory,dictory查找数据发现副本在内存/其他L1,于是L2找内存要数据后传给core0 L1/因为此时数据在L2说明要么这个数据已经是S或者是I,不可能是M,所以L2查找共享者名单,发送fwdgetS(本质上是getS)通过forward通道找名单上的core L1,共享者L1受到消息后发送data通过response通道发送给core0 L1,core0 L1受到数据把状态变为S,然后完成提交;(这里gem5实现可能有点差别 补充:如果是独占者拥有数据,将会发送数据给dictory然后dictory发送给内存,因此在这里某个缓存行为S,那么就说明内存一定有最新版本。
(2)基于CPU的store指令,core0希望能往一个缓存行写入新的数据,那么必须要其他缓存行对这个地址的存储的状态为I才可以进行修改,于是core0 L1通过request通道发出getM给dictory,dictory查找数据发现副本在内存/自己/其他L1(因为对于store指令来说,是先读同一个地址然后再写入一个新数值),因此如果在内存那么直接找内存要后发送data通过response给c0 L1,/如果在自己,也是直接发送即可/如果在其他L1有共享,那么需要发送inv给其他L1通过fwd通道给共享者,然后共享者收到信息后发送invack通过response给L2,L2收集了所有共享者的invack后把数据发送给(如果是S型数据,默认L2上也有)c0 L1。如果是有独占者,发送fwdgetM通过fwd通道给独占者,然后独占者发送data通过response给c0 L1并且把自己的数据变为I。
(3)core0发出putS通过request给dictory,putS就是希望把自己某个缓存行(S)给evict驱逐出去,dictory会查找共享者目录,如果说还有其他共享者,就发送putack通过response发送给core0 L1,L1收到后就把该缓存行变为I状态;如果说没有共享者了,那么在L2 dictory里标注该数据为I状态,发送putack通过response发送给core0,不需要备份,因为内存有最新版本了
(4)core0发出putM通过request给dictory,putM就是希望自己把自己独占的数据给驱逐到内存里,所以dictory就会把这数据存一份后发送给内存,然后发送成功后发送putack通过response通道给core0 L1,然后L1把M变为I状态。
此处可能有些许理解错误,总体思路没问题。后续如果发现错误会加上补充
接下来我把视角转移到CPU与L1之间
CPU内部有一些指令,可以对L1进行控制,但是要真的到达L1,需要转化为一些事务:LOAD、STORE、IFETCH、Replacement,其中load、store、ifetch说过了,replacement就是想要往一个地址写数据,发现这个地址没有注册过,addr,tag全都对不上,并且cache内部也没有可以分配给这个addr的空间,这个时候就需要通过replacement事件进行控制,先把当前位置的数据驱逐到内存然后再安全写入,之后通过mandatoryQueue发送send_eviction回去告诉CPU该缓存行地址已经被驱逐出去了。
CPU与L1,L1与L2通信类型基本都在上面了,现在再补充一些前驱知识————TBE,(MSHR基本组件)
在现代高速L1中,CPU性能极强,但是如果说以L1为单位分配一个缓存行的状态转换(因为缓存行无法做到立刻收到其他cache发来的invack之类的时间相关原因,所以无法做到立刻变化状态,需要有中间态),那么如果CPU对内存多次操作,堵塞会很严重,因此需要有一种设计能让L1接受CPU的多次内存操作————TBE。假设现在L1需要发送一次getM(a0 S),随后再来一次getM(a1 I),那么a0分配一个tbe,a1分配一个tbe,tbe记录a0地址、缓存行中间状态、ack计数次数(这个与前面第二点讲述不太符合,以后面具体代码解读为准),数据(如果别人这个时候刚好来找我要,而且我的状态合法,就能给他,没必要绕去内存或者L2拿),也会为a1分配好这些东西,但是这里可惜的是通道只有request一个,所以即便有tbe进行乱序,这里模拟器给定的request通道也得通过阻塞(现代有AXI4乱序可以传输)一个一个来才能实现操作。注:如果两次store地址一样,那么共享一个tbe. tbe由tbe table进行分配
上面基本上就是看懂gem5对MSI建模的基础知识了。后续会尽量依据这些来解读代码,并且构建出更优秀的MSI建模或者MESI
浙公网安备 33010602011771号