源码太多,基于 gpt 5.4 理解
LSU¶
一般 load/store:AG(Addr gen)->DC(Data Cache)->DA(Data Align,以及 store-load forwarding,来自 sq/wmb)->WB(Write back,写回 reg)
可参考:https://zhuanlan.zhihu.com/p/492879610
- lsu 以 16bytes 的粒度访问 cache,有 8 个 bank,每个 bank 4bytes,一个 cacheline 是 64bytes
wmb¶
store 所经过的层次:SQ->wmb_ce->wmb-> biu/L1D/VB
SQ commit 之后 pop 到 wmb_ce,wmb_ce 是用于 create wmb entry 的模块,VB 是 victim buffer
暂时无法在飞书文档外展示此内容
920 中所有的 store 种类:
-
Atomic:原子 store/Store conditional
-
Icc:icache、dcache、l2cache、tlb 等涉及缓存一致性的 store
-
sync/fence
-
除以上几类之外的都是普通 store,每种普通 store 又分为 cacheable 和 uncacheable
-
Weak order
-
Strong order/device store
-
wmb 入口¶
wmb merge 针对的 store 种类:merge **只针对 weak order 开启,cacheable/uncacheable 都可能 merge,**源码是 wmb_ce_merge_en = wmb_ce_wo_st。
但是上述所有 store 都能且会进入 wmb,只是没有 merge 这一 feature。
只有一种特殊 store 不会进来,dcache all 这种“复用 store pipe 的维护操作”不应简单等同于普通 store,它会先经过缓存维护控制那条握手路径;先向 ICC 申请 sq_icc_req,等 icc_sq_grnt 且 icc_idle 后,再继续走 sq_wmb_pop_to_ce_req 这条路
wmb 内部¶
merge 指的是,把落在同一个 16B wmb data block 的 store 合并到同一个 entry,来减少写请求次数
merge 要求:weak order 以及 addr[PA-1:4] 相同
-
Wmb 一共有 8 个 entry,每个 entry 控制 16B,一共 8*16B
-
merge 窗口 是 16B
-
覆盖粒度 是 byte
-
允许字节重叠
-
重叠时 后来的 store 覆盖前面的同字节数据(一般来说只有 weak order 可以,strong order 不能这样做)
Merge example:
假设两个 wo_st:
-
S0: 写 0x1000 ~ 0x1007,bytes_vld = 16'h00ff
-
S1: 写 0x1008 ~ 0x100f,bytes_vld = 16'hff00
注意掩码,是以 bytes 为单位,而不是 bits
合并:
-
对每个 byte,若 wmb_ce_bytes_vld[i]=1,就用新 store 的 byte 覆盖旧 byte
-
否则保留旧数据
-
多个可以 merge 的 store,各自的 bytes_vld 做按位 OR
所以这个例子最后:
-
原来 entry0.bytes_vld = 16'h00ff
-
merge 后变成 16'hffff
-
entry0.data[63:0] 来自 S0
-
entry0.data[127:64] 来自 S1
这个 bytes_vld 是如何生成的?bytes_vld 本质上是一个 16 bit 掩码,表示在当前这个 addr[PA-1:4] 对应的 16 字节窗口里,哪些 byte 被这条 store 写到。
- 在 lsu 时,会根据写 byte 起始位置,store 写多少字节,算出结束位置,然后把[start,end)变为一个 16bits 的掩码,当然这个过程有可能会跨 16bytes,需要两次写,对应两个掩码
问题 1:store 的 merge 会不会导致 load 读到错误值
是否会因为 wmb 把两个 store 把合并(因为其地址相同),导致 load 读到了合并后的结果
答:不会,由于 store 的顺序是先进入 SQ,然后从 SQ pop 时才进入 wmb。而又因为 store 指令只有 ROB commit 时才能出 SQ,这保证了 store2 指令是最新提交的,不可能有前面的 load1 在该条 store2 指令之后
问题 2:为什么 merge 时允许新的 store 直接覆盖同 byte 的数据,这不会造成内存状态的错误吗?
和上一个问题,其实是类似的。先想想,什么情况这种错误会有影响,那一定是在两个 store 中间穿插了一个 load,或者以某种别的方法要观测两个合并 store 的中间情况。但是由于进入 wmb 的 store,一定已经处在“对当前 load 来说足够老”的阶段,不可能会观测到这种中间情况。
所以说为什么允许合并,因为被覆盖的值还没成为**必须被外界观察到的状态,**这是最关键的,虽然错了一瞬间但是外界看不到
这也是为什么要求 weak order 才能合并,从内存状态更新的角度来看,确实少了一次旧值,如果真的 debug 时在这个地方有问题,那么软件开发者一定不知道是什么地方出错了。但是从程序执行的角度,执行结果还是正确的不会变。也就是 weak order 只保证程序结果正确,但是不保证中间每一个结果,内存都是正常更新
wmb 出口¶
出口:
-
Biu
-
Read(shareable cacheable store 的前置读/一部分 store conditinoal)
-
write
-
-
L1 Dcache(cacheable 命中或可直接更新的普通 store)
-
Victim buffer(部分 dcache 维护类操作,比如 clear/invalid)
为什么 write merge 会有 read?
- 有些请求会先走“对外读地址通道”拿权限或做维护事务,然后才在另外三类里选一个真正落地的写出口。真正的“最终写落点”主要是后面三类:外部总线写、一级数据缓存、本地被替换缓存行缓冲区。
wmb 出口具体来说:
- 送到总线接口单元的“读地址通道”。
作用是“先读一下”或者“发缓存维护控制事务”,不是最终写数据。会走这里的类型有:可共享且可缓存的普通存储,前提是目标缓存行当前是共享状态或者本地无效;可共享且可缓存的存储条件指令;按地址操作的数据缓存维护;地址翻译缓冲区失效、指令缓存维护、二级缓存维护这类控制事务。
-
送到总线接口单元的“写地址通道和写数据通道”。
-
这是往核心外部真正发写请求的出口。会走这里的类型有:强序设备存储;不能在一级数据缓存内直接完成的普通存储;同步和栅栏;不能在一级数据缓存内直接完成的原子存储;不能在一级数据缓存内直接完成的存储条件指令。
-
直接更新一级数据缓存。
-
这是“本地完成写入”的出口。会走这里的类型有:可缓存而且目标缓存行当前在一级数据缓存中有效的普通存储;可缓存而且目标缓存行当前有效的原子存储;可缓存而且目标缓存行当前有效的存储条件指令;一部分单行“只做失效”的数据缓存维护操作。
-
送到“被替换缓存行缓冲区”。
-
这不是直接出核,而是先交给本地缓冲区,后面再由它决定是否要对外吐出、写回、清除或者失效。会走这里的类型主要是:可缓存、目标缓存行当前有效、并且属于“单行清除类”的数据缓存维护操作。
Wmb 什么时候往外发¶
每个entry会顺序经历五个阶段:biu read->biu write->wirte VB->write dcache->data(resp)
发出是看ptr落在哪个entry,并且满足一定条件,该entry就发出去,多个ptr功能独立,每周期都可以往外发
如果某一个entry不需要某一阶段,比如dcahce不需要经过biu,可以通过immediate skip跳过一些阶段
还有create_ptr
entry释放条件:
// ct_lsu_wmb_entry.v:2179
wmb_entry_pop_vld = vld // entry 有效
&& read_resp // BIU R 响应已收到
&& (write_resp || write_resp_set // 写响应已收到
|| mem_set_req && !w_last) // 或 burst 中间 beat
&& (data_req_success || data_req_success_set) // 数据已发送
&& wb_cmplt_success // 写回完成确认
&& wb_data_success; // 写回数据完成
没有仲裁 — 所有满足条件的 entry 同时 pop。 entry 自身把 vld 清零即可。
多个entry的ptr是否会同时valid:不会。ptr是独热码