Xuantie C910
Reference:
Xuantie C910/C920 为两级 cache 的结构,其中 L1 Cache 私有, L2 Cache 多核共享,并采用 MOESI 协议维护多个处理器核心的 cache 一致性。
Xuantie C910 的标量部分开源到了 open-c910 github 仓库,且其通过 AMBA ACE 总线实现 MOESI 协议
处理器核发出的请求事务
l1icache / ifu
in ifu/rtl/ct_ifu_ipb.v
l1icache miss 或者 prefetch 时向总线发起请求
1 | assign pref_tsize = 1'b1; |
- 如果 Cacheable && Cache Enable (
ipb_tsize) 和请求地址所在的页是共享的(ipb_page_share),则发出 ReadShared 事务 - 反之则发出 ReadNoSnoop 事务
l1dcache / lsu
l1dcache: Read Buffer
l1dcache miss 时 lsu 中的 read buffer 需要向总线发起请求获取数据 refill l1dcache
in lsu/rtl/ct_lsu_rb.v
1 | ... |
- 原子指令的读请求(
rb_atomic_readunique): ReadUnique 事务 - 请求地址 cacheable 和 shareable,起因是 store 指令引起的 cache miss: ReadUnique 事务
- 将其它核的私有缓存中对应的缓存行无效
- 请求地址 cacheable 和 shareable,起因是 load 指令(
!rb_biu_req_st)引起的 cache miss: ReadShared 事务 - 请求地址非 cacheable : ReadNoSnoop事务
- 请求地址 cacheable 和 sharable,但是该处理器核的缓存不使能: ReadOnce 事务
- 读取的数据不能放入缓存中
l1dcache: Prefetch Unit
lsu 触发时预取也需要向总线发出请求:
in lsu/rtl/ct_lsu_pfu.v
1 | assign pfu_biu_ar_snoop[3:0] = pfu_biu_req_page_share |
- 如果请求地址所在的页是共享的(
pfu_biu_req_page_share),则发出 ReadShared 事务 - 否则发出 ReadNoSnoop 事务
l1dcache: Victim Buffer
dcache 的 linefill buffer 写回替换时需要通过总线向下一级写入踢出的 cacheline, C910 的 l1dcache 在写回时采用了 victim buffer 的设计,因此对总线的写入由 victim buffer 负责:
in lsu/rtl/ct_lsu_vb.v
1 | ... |
- 要替换写回的 cache line 如果不为脏(
!vb_biu_aw_req_dirty)则向总线发起 writeevict 事务- 为什么还需要写入?
- 要替换写回的 cache line 如果为脏(
vb_biu_aw_req_dirty)则向总线发起 writeback 事务 - 如果是清除脏位且无效缓存行的操作,则发起writeback事务将脏的缓存行写回下一级内存中。
- 如果是清除脏位但是可以保留缓存行的副本,则发起writeclean事务将脏的缓存行写入下一级内存中。
l1dcache: Write Memory Buffer
dcache 的 Write Memory Buffer 负责将数据写回缓存或者下一级缓存
in lsu/rtl/ct_lsu_wmb.v
1 | ... |
- cache 指令通过
ar_snoop发送一致性请求- 对于清除脏位的 cache 指令(``),发出 cleanshared 事务
将脏的缓存行写回下一级缓存 - 对于无效某个缓存行的 cache 指令,发出 makeinvalid 事务,将该缓存行无效
- 对于清除脏位且无效的 cache 指令,发出 cleaninvalid 事务
- 对于无效 tlb 或者 icache 的指令,发出 DVM 事务
- 对于清除脏位的 cache 指令(``),发出 cleanshared 事务
- 对于普通的读请求,
ar_snoop发出 cleanunique 事务
将其它核中该 cacheline 副本的脏位清除并无效 - 对于写操作,通过
aw_snoop发送一致性请求- 请求地址页面属性是 cacheable 且请求写整个 cacheline,发起 WriteLineUnique 事务
传到其它核时会将该事务会被当作 MakeInvaild 事务处理,直接将 cacheline 的副本删除 - 请求地址页面属性是 uncacheabel 则发起 WriteNoSnoop 事务
- 请求写部分 cacheline,则发起 WriteUnique 事务
传到其它核时会将该事务当作 CleanInvalid 处理,将脏的 cache 写回后将副本删除 - 如果是内存屏障指令,如 fence 指令,则发起 Barrier 事务
- 请求地址页面属性是 cacheable 且请求写整个 cacheline,发起 WriteLineUnique 事务
l1dcache snoop 总线仲裁:
- AR 通道仲裁优先级: wmb(Write Memory Buffer) > rb(Read Buffer) > pfu(Prefetch Unit)
in lsu/rtl/ct_lsu_bus_arb.v
1 | //priority: WMB > RB > pfu |
- AW 通道仲裁优先级:vb(Victim Buffer) > wmb(Write Memory Buffer)
in biu/rtl/ct_biu_write_channel.v
1 | always @(...) |
l1icache 与 l1dcache 之间的 snoop 总线请求会在 AR 通道上存在仲裁:
in biu/rtl/ct_biu_req_arbiter.v
1 | always @(...) |
处理器核对请求事务的响应处理
- C910 的 L1 Cache 采用 MESI 协议来维护缓存一致性
- L2 Cache 采用 MOESI 协议维护缓存一致性
L1 Cache
- 对于 DVM 事务使用 ctc queue 进行处理
- 对于其它事务使用 snoop queue 处理
当接收到来自 AC 通道的请求后,仲裁器根据请求的事务类型(即 snoop 信号)将请求分派给 ctc queue / snoop queue.
in lsu/rtl/ct_lsu_snoop_req_arbiter.v
1 | assign biu_lsu_snp_req = biu_lsu_ac_req |
DVM 事务: ctc queue
- 对于 DVM 事务,从 AC 通道传来的 snoop 信号为
4‘b1111,由 AC 通道的 addr 传递具体的请求 - DVM 事务主要包括 tlb 和 icache 的无效请求
DVM 事务请求类型从 ac_addr 中解析得到:
Addr[14:12] |
Addr[6:5] |
Addr[0] |
Req Type |
|---|---|---|---|
000 |
00 |
0 |
TLBI_ALL |
000 |
00 |
1 |
TLBI_VA_ALL |
000 |
01 |
0 |
TLBI_ASID_ALL |
000 |
01 |
1 |
TLBI_VA_ASID |
010 |
11 |
1 |
ICI_VA |
010 |
00 |
0 |
ICI_ALL |
- 对于 TLBI_VA_ALL, TLBI_VA_ASID, ICI_VA 这三种(
ac_addr[0] == 1)需要根据完整的地址来进行无效操作的请求- AC 通道需要两次传输,第二次传输负责传输地址信息
- 对于 TLBI_VA 请求,第二次传输的是虚拟地址
- 对于 ICI_VA 请求,第二次传输的是物理地址
- 对于 TLBI_ASID_ALL 和 TLBI_VA_ASID 这两种(
ac_addr[6:5] == 01)需要根据进程号无效 TLB 的请求- 第一次传输时
ac_addr[23:16]为 ASID 进程号
- 第一次传输时
- 对于 ICI_VA 需要根据 VA 和 PA 无效 icache 的请求
- 第一次传输时
ac_addr[PA_WIDTH-1:16]为 VA - 第二次传输时
ac_addr为 PA
- 第一次传输时
in lsu/rtl/ct_lsu_snoop_req_arbiter.v
1 | //========================================================== |
- ctc queue 共有 6 项,即最多可以接受 6 个 DVM 请求
- 当接受请求后,根据请求的类型将无效请求和请求地址发送到 MMU 或者 icache 中进行无效化操作
- MMU / ICACHE 在无效化处理完成后,会发出完成信号通知 ctc queue
- 当该请求是最旧的请求时(连同 snoop queue 中的请求一起排序),则通过 CR 通道给出响应
其余事务: snoop queue
snoop queue 的任务:负责将互连网络传来的消息(通过 AC 通道)进行处理
- 访问 dcache(or victim buffer / linefill buffer) 来判断请求的 cacheline 的状态
- 根据请求事务的类型,更新 cacheline 的状态
- 对于需要获取 cacheline 数据的事务,将访问 dcache 读取数据后,通过 CR 和 CD 通道做出响应
snoop queue 共有 6 项,即最多可以接受 6 个 snoop 请求
snoop queue entry:
snp_vld: validsnp_addr: AC 通道传输的地址信息snp_type: AC 通道传输的 snoop 信息,表示请求事务类型snq_depd:表明snp_addr是否与 victim buffer(vb) 和 write memory buffer(wmb) 中的地址存在依赖关系- 如果存在依赖: 需要等待 vb 处理完成或者 wmb 写回完成后,snoop queue 才能开始进行处理
snq_way:请求的 cacheline 位于 cache 中的哪一路snq_resp:将要通过 CR 通道做出的响应:cr_resp
snoop queue 与 victim cache / linefill buffer 的 bypass:
- victim buffer: 当 snoop queue entry 请求的地址与 victim buffer 中存在的数据地址相同时,如果 snoop queue entry 需要响应返回数据,则可以直接旁路 victim buffer 中的数据
- linefill buffer:当 linefill buffer 存在的地址与 snoop queue entry 请求地址相同时:
- 如果需要更改缓存行的状态,则可以通过旁路直接给到 linefill buffer, 由 linefill buffer 之后写回缓存中
- 由于 linefill buffer 中的数据为 refill cache 的数据,此时它的状态为 E 态 / S 态,更新状态时只可能是将它从 E 态 $\rightarrow$ S 态 / I 态
snoop 状态机
---
title: Snoop 状态机
---
stateDiagram
_DEFAULT --> SNPT_IDLE
SNPT_IDLE --> SNPT_WAIT_RESP
SNPT_WAIT_RESP --> SNPT_IDLE
SNPT_WAIT_RESP --> SNPT_WAIT_DATA_BUFFER
SNPT_WAIT_RESP --> SNPT_WAIT_POP
SNPT_WAIT_RESP --> SNPT_SDT_REQ
SNPT_WAIT_DATA_BUFFER --> SNPT_WAIT_POP
SNPT_WAIT_POP --> SNPT_IDLE
SNPT_SDT_REQ --> SNPT_WAIT_SDT_CMPLT
SNPT_SDT_REQ --> SNPT_WAIT_POP
SNPT_WAIT_SDT_CMPLT --> SNPT_WAIT_DATA_BUFFER
SNPT_WAIT_SDT_CMPLT --> SNPT_WAIT_POP
- SNPT_IDLE: 初始状态
- 当 snoop entry 与 vb 和 wmb 没有依赖关系时,可以借用 store 流水线判断 cacheline 位于哪一路,将该 cacheline 的 share 位和 dirty 位读出
- 当向 store 流水线发出请求后可以进入 SNPT_WAIT_RESP 状态等待响应
- SNPT_WAIT_RESP: 在该状态下等待访问 dcache 的响应
- 根据响应结果和请求的事务类型来判断是否需要读取 cacheline 数据或者改变 cacheline 的状态
- 如果需要读取数据或改变状态则需要跳转到 SNPT_SDT_REQ
- 如果不需要读取数据和改变缓存行状态,则判断此时与 victim buffer 是否还有依赖
- 如果存在依赖: 进入 SNPT_WAIT_DATA_BUFFER 状态等待 victim buffer 处理完成
- 否则进入 SNPT_WAIT_POP 向 CR 通道做出响应
- SNPT_WAIT_DATA_BUFFER:等待 victim buffer 处理完成
- 当 victim buffer 中不再有依赖时,跳转到 SNPT_WAIT_POP 向 CR 通道做出响应
- SNPT_SDT_REQ:当需要读取 cacheline 数据或者改变 cacheline 状态时,会在该状态下等待当前 snoop 请求成为 snoop queue 中最老的一项时, 发出访问 dcache 的请求 (端口限制)
- 如果 victim buffer / linefill buffer 中有 entry 匹配
- victim buffer 匹配且不需要改变 cacheline 状态时: 跳转到 SNPT_WAIT_POP 向 CR 通道发出响应
- linefill buffer 匹配: 跳转到 SNPT_WAIT_POP 向 CR 通道发出响应
- 否则跳转到 SNPT_WAIT_SDT_CMPLT 等待请求完成
- 如果 victim buffer / linefill buffer 中有 entry 匹配
- SNPT_SDT_CMPLT:该状态下等待访问 dcache 读取数据或者改变 cacheline 的状态
- 当 dcache 仲裁器允许 snoop queue 访问时,则跳转到 SNPT_WAIT_POP 向 CR 和 CD 通道做出响应
- SNPT_WAIT_POP:该状态下向 CR 和 CD 通道做出响应,等待总线接受响应
- 当 CR 通道 ready 时(响应传输完成),即可跳转回初始状态 SNPT_WAIT_POP
访问 dcache 的响应:(in lsu/rtl/ct_lsu_snq_entry)
- 响应来源主要有三个:dcache, linefill buffer 旁路, victim buffer 旁路
- 主要的响应为三个位:valid, dirty, share
1
2
3
4
5
6
7assign dcache_snq_tag_resp_valid_final = dcache_snq_tag_resp_valid || lfb_bypass_mode || vb_bypass_mode;
assign dcache_snq_tag_resp_dirty_final = dcache_snq_tag_resp_valid
&& dcache_snq_tag_resp_dirty
|| vb_bypass_mode;
assign dcache_snq_tag_resp_share_final = dcache_snq_tag_resp_valid
? dcache_snq_tag_resp_share
: lfb_bypass_mode && snq_entry_bypass_dcache_share;- 在 dirty 位的响应中不需要考虑 linefill buffer 的旁路
(在 linefill buffer 中的 cacheline 一定不处于 M 态) - 当 vb_bypass_mode 为高时,该 cacheline 的响应必定为脏
(在 victim buffer 中的 cacheline 需要写回并且一定为脏)
- 在 dirty 位的响应中不需要考虑 linefill buffer 的旁路
判断是否需要读取 cacheline 数据的逻辑:
只有在 M 态下,且请求的事务需要读取或清除脏位( ReadShared / ReadUnique / ReadOnce / CleanInvalid / CleanShared) 时需要读取 cacheline 数据到 vb_sdb_data (同时充当 snoop queue 和 victim buffer 的 data buffer),当 snoop 进入 SNPT_WAIT_POP 状态后将数据通过 CD 通道返回给请求的核心或者写回下一级内存
判断是否需要改变 cacheline 状态:
根据访问 cache 得到的响应从而得到 cacheline 的状态,然后根据请求事务类型来判断是否需要改变状态:
| Cacheline State | 请求事务类型 | 状态迁移 | 迁移原因 |
|---|---|---|---|
| M | CleanShared / ReadShared | M $\rightarrow$ S | 别的核心需要共享该 cacheline (ReadShared),或者需要清除 cache 的脏位并保留副本(CleanShared) |
| E | CleanShared, ReadShared | E $\rightarrow$ S | 其他核需要共享该 cacheline |
| M/E/S | ReadUnique / CleanInvalid / MakeInvalid | M/E/S $\rightarrow$ I | 需要将该缓存行无效化 |
| I | - | - | - |
通过 CR 通道的响应 snoop 请求:
- WasUnique (C910 中不支持该信号因此恒为0)
- IsShared: 代表在 snoop 过程完成后,该 cache 还保留着旧的副本
- 对于 ReadShared 事务,snoop 过程后,该缓存行处于 S 态,保留着副本
- 对于 ReadOnce 事务,不会改变 cacheline 的状态
- 对于 CleanShared 事务,会清除 cacheline 的脏位,但是可以保留副本
- PassDirty: 代表在 snoop 前 cacheline 为 M 态,且在 snoop 后需要将写回下一级内存的责任传递给发起 snoop 请求的核心或者互连网络
- 除 MakeInvalid 外,其它会更改 M 态的事务(CleanShared / ReadShared / ReadUnique / CleanInvalid)都会将 pass_dirty 信号拉高
- Error (C910 中不支持该信号因此恒为0)
- DataTransfer: 代表是否需要传输数据 (同是否需要读取 cacheline 数据的判断)
in lsu/rtl/ct_lsu_snq_entry
1 | assign data_transfer = (dcache_snq_tag_resp_valid_final && dcache_snq_tag_resp_dirty_final) //(M) |
L2 Cache
todo…