访存模块验证文档
Mem Block
- 1: LSQ
- 1.1: LoadQueueRAR
- 1.2: LoadQueueRAW
- 1.3: LoadQueueReplay
- 1.4: LoadQueueUncache
- 1.5: StoreQueue
- 1.6: VirtualLoadQueue
- 2: Shield-XS:基于Bitmap的安全隔离机制
- 2.1: Shield-XS Bitmap 基础知识
- 2.1.1: 背景描述
- 2.1.2: 香山 bitmap 防御原理
- 2.1.3: bitmap的工作流程
- 2.1.4: 香山bitmap 应用场景
- 2.2: Shield-XS Bitmap总体设计
- 2.2.1: 机器模式Bitmap控制状态寄存器
- 2.2.2: Shield-Bitmap Cache
- 2.2.3: Shield-Bitmap 地址翻译
- 2.2.4: Shield-Bitmap 地址翻译2
- 2.3: Shield-XS Bitmap 硬件设计
- 2.3.1: Bitmap Checker
- 2.3.2: Bitmap Cache
- 2.3.3: Bitmap 与L2TLB交互
- 2.3.4: Bitmap接口时序图
- 2.3.5: 开销评估
- 2.4: Shield-XS_Bitmap 单元验证
- 2.4.1: 单元验证
1.1 - LoadQueueRAR
本文档参考香山LSQ设计文档写成
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
LoadQueueRAR 简介
LoadQueueRAR用于保存已经完成的load指令的用于load to load违例检测的信息。
多核环境下会出现load to load违例。单核环境下相同地址的load乱序执行本来是不关心的,但是如果两个load之间有另外一个核做了相同地址的store,并且本身这个核的两个load做了乱序调度,就有可能导致新的load没有看到store更新的结果,但是旧的load看到了,出现了顺序错误。
多核环境下的load-load违例有一个特征,当前DCache一定会收到L2 cache发来的Probe请求,使得DCache主动释放掉这个数据副本,这时DCache会通知load queue,将相同地址的load queue中已经完成访存的项做一个release标记。后续发往流水线的load指令会查询load queue中在它之后相同地址的load指令,如果存在release标记,就发生了load-load违例。
术语说明
名称 | 描述 |
---|---|
L2Cache | 二级高速缓存 |
DCache | 数据缓存 |
ROB | 重排序缓冲区 |
CAM | 内容可寻址存储器 |
FTQ | 取指目标队列 |
ld-ld违例
多核环境下,可能会出现load to load违例:在单核环境中,相同地址的load乱序执行通常不被关注,因为它们在同一核内执行,不会影响其他核的状态,也不会被其他核的操作影响。但是,当两个load操作之间有另一个核对相同地址进行了store操作,情况就变得复杂。
考虑以下指令序列:
load1(core1)
store(core2)
load2(core1)
指令的实际执行顺序为:
load2(core1)
store(core2)
load1(core1)
由于指令的乱序执行,可能导致以下情况:旧的 load1 指令在执行时读取到了 store 修改后的新数据,而新的 load2 指令却读取到了未被修改的旧数据。这种执行顺序的变化会导致数据的不一致性,进而引发访存错误。
因此,在多核环境中,正确处理指令的执行顺序和内存一致性是至关重要的,以确保所有核都能看到一致的内存状态。
整体框图
图1:LoadQueueRAR结构示意图
LoadQueueRAR最多能够存储72条指令(为了同VirtualLoadQueue的大小保持一致),每条指令占用一个条目。每个条目包含指令的物理地址(paddr)、与指令相关的信息(uop)、以及标记为已释放(released)和已分配(allocated)的状态寄存器。
该模块通过 FreeList 子模块管理 entry 资源,FreeList 中存储的是 entry 的编号。当一条指令满足入队条件时,FreeList 会为其分配一个 entry 编号,并将该指令存放在相应的 entry 中。指令出队时,需要释放所占用的 entry 资源,并将条目编号重新放回 FreeList 中,以供后续指令使用。
PaddrModule 的实现基于内容可寻址存储器(CAM),其深度为 72,数据宽度为 48。CAM 为每条流水线提供一个写端口,其中物理地址(paddr)作为写数据(wdata),条目编号作为写地址(waddr)。此外,CAM 还为每条流水线提供了一个地址查询端口(releaseViolationMdata),并为数据缓存(DCache)提供另一个地址查询端口(releaseMdata)。
模块功能说明
功能1:发生ld-ld违例的指令请求入队
当query到达load流水线的s2时,判断是否满足入队条件,如果在当前load指令之前有未完成的load指令,且当前指令没有被flush时,当前load可以入队。
具体入队条件如下:
-
指令的入队请求必须有效,具体通过检查
query.req.valid
是否等于 1。如果该条件满足,系统将继续处理指令的入队。 -
指令必须确认尚未写回到重排序缓冲区(ROB)。这一条件通过比较指令在 VirtualLoadQueue 中的写回指针与该指令分配的
lqIdx
来验证。指令只有在到达 VirtualLoadQueue 的队头,并且其地址和数据均已准备好后,才能被写回到 ROB。这一机制确保了指令执行的顺序性和数据的有效性。 -
指令不能处于冲刷状态。为此,系统需要比较重定向指针所指向的指令与该指令的
robIdx
、ftqidx
及 FTQ 内的偏移(ftqoffset
)。如果两者不相同,则说明该指令可以安全入队,从而避免潜在的冲突和数据不一致性。
在 LoadQueueRAR 指令成功入队后,系统会执行一系列响应操作,以确保指令被正确管理和处理。具体的入队响应操作如下:
-
拉高 allocated 寄存器。系统将指令的
allocated
寄存器设置为高电平。这一操作的目的是明确标识该指令已成功分配到 LoadQueueRAR 中。通过将allocated
寄存器拉高,后续的处理逻辑能够迅速识别出该指令的状态,从而避免对未分配指令的误操作。 -
写入指令相关信息到 uop。指令的相关信息将被写入到微操作(
uop
)中。这些信息包括指令的类型、目标寄存器、操作数等关键信息。将这些信息存储在uop
中,确保后续的执行阶段能够准确获取和使用这些数据,从而执行相应的操作。这一过程对于指令的正确执行至关重要。 -
物理地址写入 PaddrModule。指令的物理地址将被写入到 PaddrModule 中。这一操作的主要目的是为后续的地址查询和管理提供支持。
-
检测 Release 的 Valid 信号。系统将检测
release
的有效信号是否被拉高。如果该信号有效,将进一步比较物理地址是否相同。如果物理地址一致,则对应条目的released
信号将被设置为高电平,可以用于后续操作。
功能2:检测ld-ld违例条件
在 Load 指令的处理过程中,为了确保数据的一致性和正确性,系统需要检测潜在的 Load-Load 违例。当 load 到达流水线的 s2 时,会检查RAR队列中是否存在与当前load指令物理地址相同且比当前指令年轻的load指令,如果这些 load 已经拿到了数据,并且被标记了release,说明发生 load - load 违例,被标记release的指令需要从取指重发。 该检测过程主要涉及将查询指令的物理地址和相关信息与队列中存储的指令进行对比。具体流程如下:
- 对比 ROB 索引。通过对比两条指令的robidx识别队列中是否存在比查询指令更年轻的指令。
- 物理地址匹配。检查这两条指令的物理地址是否相同。这一对比通过
releaseViolationMmask(w)(i)
来进行,以确定两条指令是否访问了相同的内存位置。 - 检查 Released 标记。如果该条指令的
released
寄存器被拉高,表明该指令已被标记为释放,说明它可以被重新使用。
一旦检测到 Load-Load 违例,系统将在下一个时钟周期内将 resp.rep_rm_fetch
信号拉高,以通知其他组件发生了违例。触发 Load-Load 违例的 Load 指令将被标记为需要重新从取指阶段执行。重定向请求将在这些指令到达 ROB 队列的尾部时发出,确保指令能够在合适的时机得到正确的处理。
该过程分为两个时钟周期进行:
- 第一拍进行条件匹配,对比物理地址和指令状态,得到mask。
- 第二拍生成是否发生违例的响应信号(
resp.rep_rm_fetch
)。
由于 Load-Load 违例出现的频率相对较低,因此系统会选择在指令到达 ROB 的头部时才进行处理。这种处理方式类似于异常处理,确保系统能够在合适的时机对潜在的违例情况进行响应。
功能3:released寄存器更新
released寄存器需要更新的三种情况:
- missQueue模块的replace_req在mainpipe流水线的s3栈发起release释放dcache块,release信号在下一拍进入loadqueue。
- probeQueue模块的probe_req在mainpipe流水线的s3栈发起release释放dcache块,release信号在下一拍进入loadqueue。
- atomicsUnit模块的请求在mainpipe流水线的s3栈发生miss时需要释放dcache块,release信号在下一拍进入loadQueue。
release信号的到达时机可以分为以下两种情况:
- 指令入队时到达。如果查询指令传来的paddr的高42位信号与paddr的高位信号相同,并且该指令能够成功入队将对应entry的released寄存器信号拉高
- 指令入队后到达。如果paddrmodule中存放的paddr的高42位信号与paddr的高位信号相同,将对应的released寄存器信号拉高
值得注意的是,dcache release 信号在更新 load queue 中 released
状态位时, 会与正常 load 流水线中的 load-load 违例检查争用 load paddr cam 端口. release 信号更新 load queue 有更高的优先级. 如果争用不到资源, 流水线中的 load 指令将立刻被从保留站重发.
功能4:指令的出队
Load指令的出队需要满足以下条件其中之一:
- 当比队列entry中存放的指令更老的指令已经全部写回到ROB时,该指令可以出队。
- 当这条指令需要被冲刷时,通常是出现数据依赖性问题、预测错误、异常或错误的情况下,迫使系统强制性地移除该指令,以保证处理器能够恢复到一个稳定的状态。
出队执行的操作:
- 将指令对应的
allocated
寄存器设置为低电平。这一操作的目的是标识该指令不再占用 LoadQueueRAR 的资源,从而为后续指令的入队和处理腾出空间。 - 将entry对应的
free
掩码拉高,表示该条目已被释放并可供后续使用。
在load流水线的s3阶段可以向队列发送revoke信号撤销上一拍的请求。如果指令当前周期的revoke信号拉高(revoke ==1),并且在上一个周期已经入队,需要执行撤销操作:
- 该entry对应的allocated寄存器清零
- 该entry对应的free掩码拉高
接口说明
name | I/O | width | Description | |
---|---|---|---|---|
redirect | ||||
io.redirect.valid | input | 1 | 后端重定向的有效位 | |
io.redirect.bits.robIdx.flag | input | 1 | 后端重定向的flag,用于在循环列表中判断先后 | |
io.redirect.bits.robIdx.value | input | 8 | 后端重定向的位置value | |
io.redirect.bits.level | ||||
input | 1 | 后端重定向的level: | ||
1’b0:冲刷之后的指令; | ||||
1‘b1:冲刷这条指令本身 | ||||
vecFeedback | io.vecFeedback_0/1.valid | input | 1 | 来自两条流水线的向量反馈信息有效位 |
io.vecFeedback_0/1.bits | input | 17 | 来自两条流水线的向量反馈信息 | |
query | io.query_0/1/2.req.ready | output | 1 | 能否接收3条数据通路中load违例检查请求 |
io.query_0/1/2.req.valid | input | 1 | 3条数据通路中load违例检查有效位 | |
io.query_0/1/2.req.bits.uop.robIdx.flag | input | 1 | 3条数据通路中load违例检查uop在rob中的flag | |
io.query_0/1/2.req.bits.uop.robIdx.value | input | 8 | 3条数据通路中load违例检查uop在rob中的value | |
io.query_0/1/2.req.bits.uop.lqIdx.flag | input | 1 | 3条数据通路中load违例检查uop在LoadQueue中的flag | |
io.query_0/1/2.req.bits.uop.lqIdx.value | input | 7 | 3条数据通路中load违例检查uop在LoadQueue中的value | |
io.query_0/1/2.req.bits.paddr | input | 48 | 3条数据通路中load违例检查的物理地址 | |
io.query_0/1/2.req.bits.data.valid | input | 1 | 3条数据通路中load违例检查data的有效 | |
io.query_0/1/2.resp.valid | output | 1 | 3条数据通路中load违例检查响应的有效位 | |
io.query_0/1/2.resp.bits.rep.frm.fetch | output | 1 | 3条数据通路中load违例检查的响应 | |
io.query_0/1/2.revoke | input | 1 | 3条数据通路中load违例检查的撤销 | |
release | io.release.valid | input | 1 | Dcache释放块有效位 |
io.release.bits.paddr | input | 48 | Dcache释放块的物理地址 | |
ldwbptr | io.ldWbPtr.flag | input | 1 | VirtualLoadQueue中writeback的flag |
io.ldWbPtr.value | input | 7 | VirtualLoadQueue中writeback的位置value | |
Lqfull | io.lqFull | output | 1 | 表示loadqueue RAR满了 |
performance | io.perf_0/1_value | output | 6 | 性能计数器 |
1.2 - LoadQueueRAW
本文档参考香山LSQ设计文档写成
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
LoadQueueRAW 简介
LoadQueueRAW是用于处理store-load违例的。由于load和store在流水线中都是乱序执行,会经常出现load越过了更老的相同地址的store,即这条load本应该前递store的数据,但是由于store地址或者数据没有准备好,导致这条load没有前递到store的数据就已经提交,后续使用这条load结果的指令也都发生了错误,于是产生store to load forwarding违例。
当store address通过STA保留站发射出来进入store流水线时,会去查询LQRAW中在这条store后面的所有已经完成访存的相同地址的load,以及load流水线中正在进行的在该条store之后的相同地址的load,一旦发现有,就发生了store to load forwarding违例,可能有多个load发生了违例,需要找到离store最近的load,也就是最老的违例的load,然后给RedirectGenerator部件发送重定向请求,冲刷最老的违例的load及之后的所有指令。
当store流水线执行cbo zero指令时,也需要进行store-load违例检查。
st-ld违例
在现代处理器中,Load 和 Store 指令通常采用乱序执行的方式进行处理。这种执行策略旨在提高处理器的并行性和整体性能。然而,由于 Load 和 Store 指令在流水线中的乱序执行,常常会出现 Load 指令越过更早的相同地址的 Store 指令的情况。这意味着,Load 指令本应通过前递(forwarding)机制从 Store 指令获取数据,但由于 Store 指令的地址或数据尚未准备好,导致 Load 指令未能成功前递到 Store 的数据,而 Store 指令已被提交。由此,后续依赖于该 Load 指令结果的指令可能会出现错误,这就是 st-ld 违例。
考虑以下伪代码示例:
ST R1, 0(R2) ; 将 R1 的值存储到 R2 指向的内存地址
LD R3, 0(R2) ; 从 R2 指向的内存地址加载值到 R3
ADD R4, R3, R5 ; 使用 R3 的值进行计算
假设在这个过程中,Store 指令由于某种原因(如缓存未命中)未能及时完成,而 Load 指令已经执行并读取了旧的数据(例如,从内存中读取到的值为 0
)。此时,Load 指令并未获得 Store 指令更新后的值,导致后续计算的数据错误。
通过上述例子,可以清楚地看到 Store-to-Load 违例如何在乱序执行的环境中导致数据一致性问题。这种问题强调了在指令调度和执行过程中,确保正确的数据流动的重要性。现代处理器通过多种机制来检测和解决这种违例,以维护程序的正确性和稳定性。
整体框图
图1:LoadQueueRAW结构示意图
LoadQueueRAW最多能够存储64条指令,通过FreeList子模块管理空闲资源。FreeList 中存储的是 entry 的编号。当一条指令满足入队条件时,FreeList 会为其分配一个 entry 编号,并将该指令存放在相应的 entry 中。指令出队时,需要释放所占用的 entry 资源,并将条目编号重新放回 FreeList 中,以供后续指令使用。Load指令在s2阶段在 LoadQueueRAR 中查询 store-to-load 违例,在s3阶段返回响应。
模块功能说明
功能1:发生st-ld违例的指令请求入队
当query到达load流水线的s2时,判断是否满足入队条件,如果在当前load指令之前有地址未准备好的store指令,且当前指令没有被flush时,当前load可以入队。具体流程如下:
- 判断入队条件:检查在当前 Load 指令之前是否存在未准备好的 Store 指令。如果存在这样的 Store 指令,并且当前 Load 指令尚未被冲刷(flush),则当前 Load 指令可以入队。
- 分配 Entry 和 Index:在 Freelist 中,系统将获得一个可分配的 Entry 及其对应的 Index,以便为 Load 指令分配资源。
- 保存物理地址:在 PaddrModule 中将入队的 Load 指令的物理地址保存到对应的 Entry。这一操作确保在后续访问中能够正确引用该地址。
- 保存掩码信息:在 MaskModule 中,系统将入队的 Load 指令的掩码信息保存到对应的 Entry。掩码信息用于后续的地址匹配和数据访问。
- 写入uop:将 Load 指令的uop信息写入到相应的结构中,以完成入队过程。
功能2:检测st-ld违例条件
在 Store 指令到达 Store 流水线的 s1 阶段时,系统会进行 Store-to-Load 违例检查。此时,Store 指令需要与 Load Queue 中已经完成访存的 Load 指令,以及在 Load 流水线 s1 和 s2 阶段正在访存的 Load 指令进行比较。这些 Load 指令可能尚未通过前递(forwarding)机制获取 Store 指令执行的结果。
具体的违例检查流程如下:
- 物理地址匹配:在第一拍中,系统将进行物理地址匹配,并检查条件。此时,将匹配在当前 Store 指令之后的所有新的 Load 指令。如果这些 Load 指令已经成功获取了数据(
datavalid
),或者由于缓存未命中正在等待数据回填(dcache miss
),则可以确定这些 Load 指令不会将数据前递给当前的 Store 指令。 - 匹配 Load 指令:在第二拍中,Store 流水线中的 Store 指令根据匹配结果中的掩码(mask),在 Load Queue 的 RAW(Read After Write)结构中查找所有匹配的 Load 指令。Load Queue 中共有 32 项,这些项将被平分为4组。每组从 8 项中选出一个最老的 Load 指令,最多可得到 4 个候选最老的 Load 指令。
- 选择最老的 Load:在第三拍中,从上述 4 个候选最老的 Load 指令中,系统将选出一个最老的 Load 指令,作为最终的目标。
- 处理违例情况:在第四拍中,如果在两条 Store 流水线中均发生了 Store-to-Load 违例,系统将从各自的 Queue 中匹配到的最老 Load 指令中选出一个更老的 Load 指令,以产生回滚请求并发送给重定向模块(Redirect)。此时,违例的条件包括:
- Load 和 Store 的地址相同。
- Load 指令比 Store 指令年轻。
- Load 指令已经成功获取了数据。
功能3:指令的出队
Load指令的出队需要满足以下条件其中之一:
- 当比队列entry中存放的指令更老的指令已经全部写回到ROB时,该指令可以出队。
- 当这条指令需要被冲刷时,通常是出现数据依赖性问题、预测错误、异常或错误的情况下,迫使系统强制性地移除该指令,以保证处理器能够恢复到一个稳定的状态。
出队执行的操作:
- 将指令对应的
allocated
寄存器设置为低电平。这一操作的目的是标识该指令不再占用 LoadQueueRAR 的资源,从而为后续指令的入队和处理腾出空间。 - 将entry对应的
free
掩码拉高,表示该条目已被释放并可供后续使用。
在load流水线的s3阶段可以向队列发送revoke信号撤销上一拍的请求。如果指令当前周期的revoke信号拉高(revoke ==1),并且在上一个周期已经入队,需要执行撤销操作:
- 该entry对应的allocated寄存器清零
- 该entry对应的free掩码拉高
接口说明
name | I/O | width | Description | |
---|---|---|---|---|
redirect | io.redirect.valid | input | 1 | 后端重定向的有效位 |
io.redirect.bits.robIdx.flag | input | 1 | 后端重定向的flag,用于在循环列表中判断先后 | |
io.redirect.bits.robIdx.value | input | 8 | 后端重定向的位置value | |
io.redirect.bits.level | input | 1 | 后端重定向的level:1’b0:冲刷之后的指令;1‘b1:冲刷这条指令本身 | |
vecFeedback | io.vecFeedback_0/1.valid | input | 1 | 来自两条流水线的向量反馈信息有效位 |
io.vecFeedback_0/1.bits | input | 17 | 来自两条流水线的向量反馈信息 | |
query | io.query_0/1/2.req.ready | output | 1 | 能否接收3条数据通路中load违例检查请求 |
io.query_0/1/2.req.valid | input | 1 | 3条数据通路中load违例检查有效位 | |
io.query_0/1/2.req.bits.uop.robIdx.flag | input | 1 | 3条数据通路中load违例检查uop在rob中的flag | |
io.query_0/1/2.req.bits.uop.robIdx.value | input | 8 | 3条数据通路中load违例检查uop在rob中的value | |
io.query_0/1/2.req.bits.uop.lqIdx.flag | input | 1 | 3条数据通路中load违例检查uop在LoadQueue中的flag | |
io.query_0/1/2.req.bits.uop.lqIdx.value | input | 7 | 3条数据通路中load违例检查uop在LoadQueue中的value | |
io.query_0/1/2.req.bits.paddr | input | 48 | 3条数据通路中load违例检查的物理地址 | |
io.query_0/1/2.req.bits.data.valid | input | 1 | 3条数据通路中load违例检查data的有效 | |
io.query_0/1/2.revoke | input | 1 | 3条数据通路中load违例检查的撤销 | |
storeIn | storeIn_0/1.bits | input | 84 | 两条store流水线store指令相关信息 |
storeIn_0/1.valid | input | 1 | 两条store流水线store指令相关信息有效位 | |
rollback | rollback_0/1.valid | output | 1 | 两条store流水线回滚信息的有效性 |
rollback_0/1.bits | output | 31 | 两条store流水线回滚信息 | |
stAddrReadySqPtr | stAddrReadySqPtr | input | 7 | 指向 store 队列中已准备好的地址条目 |
stIssuePtr | stIssuePtr | input | 7 | 指向 store 队列中准备发射执行的指令条目 |
lqFull | lqFull | output | 1 | 判断队列是否满 |
1.3 - LoadQueueReplay
本文档参考香山LSQ设计文档写成
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
LoadQueueReplay 简介
LoadQueueReplay 模块是现代处理器架构中用于处理 Load 指令重发的重要组成部分。它负责管理因各种原因而需要重发的 Load 指令,确保指令执行的正确性和高效性。
整体框图

图1:LoadQueueReplay结构示意图
LoadQueueReplay 最多存放72条指令,涉及多个状态和存储的信息。其关键组成部分如下:
- Allocated:
- 表示某个 Load 重发队列项是否已经被分配,反映该项的有效性。
- Scheduled:
- 指示某个 Load 重发队列项是否已被调度,意味着该项已经被选出,并将被发送至 Load Unit 进行重发。
- Uop:
- 该队列项对应的 Load 指令执行信息,包括微操作(uop)。
- Cause:指示该 Load 指令重发的原因,主要包括以下几种情况:
- C_MA:存储-加载(st-ld)违反重新执行。
- C_TM:TLB(翻译后备页表)缺失。
- C_FF:存储-加载转发。
- C_DR:数据缓存(dcache)需要重发。
- C_DM:数据缓存缺失。
- C_WF:路径预测失败。
- C_BC:数据缓存路径冲突。
- C_RAR:读取-读取(RAR)队列无法接收。
- C_RAW:读取-写入(RAW)队列无法接收。
- C_NK:存储-加载违反。
- Blocking:指示该 Load 指令因等待条件而被阻塞,不能被调度重发。阻塞的原因和解除阻塞的条件包括:
- C_MA:存储指令的地址就绪。
- C_TM:TLB 回填完毕,并发送 Hint 信号。
- C_FF:存储指令的数据就绪。
- C_DM:数据缓存回填完毕。
- C_RAR:RAR 队列未满,且 Load 指令比 Load Queue 的写回项更老。
- C_RAW:RAW 队列未满,且 Load 指令比 Store Queue 中所有地址准备好的项都更老。
LoadReplayQueue 通过 FreeList 管理队列的空闲状态。FreeList 的大小等于 LoadReplayQueue 的项数,分配宽度为3(Load Unit 的数量),释放宽度为 4。同时,Free List 可以反馈 Load Replay Queue 的空余项数量以及是否已满的信息。除了FreeList,LoadQueueReplay还包含两个子模块:AgeDetector 和 LqVAddrModule,其中 AgeDetector 用于寻找一系列load replay queue项中最早入队的一项。
例如昆明湖V1的Load宽度为2,则会将load replay queue分为两半,从偶数项和奇数项中分别挑选一项最老的进行重发。LqVAddrModule 用于保存load replay queue项数个虚拟地址,读口和写口的数量均为Load的宽度(LoadUnit的数量)。
LoadQueueReplay 存储信息
Field | Description |
---|---|
allocated | 是否已经被分配,也代表是否该项是否有效。 |
scheduled | 是否已经被调度,代表该项已经被选出,已经或即将被发送至LoadUnit进行重发。 |
uop | load指令执行包括的uop信息。 |
vecReplay | 向量load指令相关信息。 |
vaddrModule | Load指令的虚拟地址。 |
cause | 某load replay queue项对应load指令重发的原因,包括: - C_MA(位0): store-load预测违例 - C_TM(位1): tlb miss - C_FF(位2): store-to-load-forwarding store数据为准备好,导致失败 - C_DR(位3): 出现DCache miss,但是无法分配MSHR - C_DM(位4): 出现DCache miss - C_WF(位5): 路预测器预测错误 - C_BC(位6): Bank冲突 - C_RAR(位7): LoadQueueRAR没有空间接受指令 - C_RAR(位8): LoadQueueRAW没有空间接受指令 - C_NK(位9): LoadUnit监测到store-to-load-forwarding违例 - C_MF(位10): LoadMisalignBuffer没用空间接受指令 |
blocking | Load指令正在被阻塞。 |
strict | 访存依赖预测器判断指令是否需要等待它之前的所有store指令执行完毕进入调度阶段。 |
blockSqIdx | 与load指令有相关性的store指令的StoreQueue Index。 |
missMSHRId | load指令的dcache miss请求接受ID。 |
tlbHintId | load指令的tlb miss请求接受ID。 |
replacementUpdated | DCache的替换算法是否已经更新。 |
replayCarry | DCache的路预测器预测信息。 |
missDbUpdated | ChiselDB中Miss相关情况更新。 |
dataInLastBeatReg | Load指令需要的数据在两笔回填请求的最后一笔。 |
功能简介
模块功能说明
功能1:需要重发的指令请求入队
根据是否满足以下条件以及freelist是否可以分配的空闲槽位决定能否直接入队:
enq_X.valid
信号有效。- 即将入队的项不需要重定向。
- 该项被标记为需要重发(
enq.bits.rep_info.need_rep
)。 - 没有异常。在入队时,必须确保没有异常发生。如果当前指令处于异常状态,入队操作应被禁止,以防止无效指令的执行。
功能2:指令重发解锁
LoadQueueReplay 中的指令出队分三拍:
在重发过程中,根据重发的原因和当前条件解锁相应项。在不满足解锁条件时,将会被阻塞,无法参与重发仲裁。其中C_BC(Dcache 块冲突)、C_NK(oad_unit 在 S1、S2 阶段发生 store-load 违例)、C_DR(Dcache miss 且 MSHR 满)、C_WF(路径预测失败)无需条件即可立即重发。
其他重发原因和对应的解锁条件如下:
- C_MA(store_load 预测违例):已经被分配入队并且store准备好了相应的地址,具体如下:
- store unit 的地址信号有效,且
sqIdx
与被阻塞的Idx
相同,store 地址未发生 TLB miss。 - 被阻塞的
SeqIdx
在 store_queue 发送的stAddrReadySqPtr
之前。 - 非严格阻塞,且阻塞的
SeqIdx
在stAddrReadyVec
的向量组内。 - store queue 为空,无未处理项。
- store unit 的地址信号有效,且
- C_TM(TLB Miss):resp信号有效,并且输入的id号等于Tlb Hint的id号,或者replay_all信号有效。
- C_FF(store_load 数据前递失败):因为数据前递失败导致指令重发的释放条件有下面四条:
- store unit 的数据信号有效,且
sqIdx
与被阻塞的Idx
相同。 - 被阻塞的
SqIdx
在 store_queue 发送的stDataReadySqPtr
之前。 - 阻塞的
SeqIdx
在stDataReadyVec
的向量组内。 - store queue 为空,无未处理项。
- store unit 的数据信号有效,且
- C_DM(Dcache Miss):Dcache 的信号有效,且
tl_d_channel.mshrid
与阻塞的missMSHRId
相同。 - C_RAR(RAR queue 没有回应):RAR 未满,或
lqIdx
在ldWbPtr
之前。 - C_RAW(RAW 没有回应):RAW 未满,或
lqIdx
在stAddrReadySqPtr
之前。
功能3:指令重发优先级
LoadQueueReplay有3种选择调度方式:
-
根据入队年龄
LoadQueueReplay使用3个年龄矩阵(每一个Bank对应一个年龄矩阵),来记录入队的时间。年龄矩阵会从已经准备好可以重发的指令中,选择一个入队时间最长的指令调度重发。
-
根据Load指令的年龄
LoadQueuReplay可以根据LqPtr判断靠近最老的load指令重发,判断宽度为OldestSelectStride=4。
-
DCache数据相关的load指令优先调度
-
LoadQueueReply首先调度因L2 Hint调度的重发(当dcache miss后,需要继续查询下级缓存L2 Cache。在L2 Cache回填前的2或3拍,L2 Cache会提前给LoadQueueReplay唤醒信号,称为L2 Hint)当收到L2 Hint后,LoadQueueReplay可以更早地唤醒这条因dcache miss而阻塞的Load指令进行重发。
-
如果不存在L2 Hint情况,会将其余Load Replay的原因分为高优先级和低优先级。高优先级包括因dcache缺失或st-ld forward导致的重发,而将其他原因归纳为低优先级。如果能够从LoadQueueReplay中找出一条满足重发条件的Load指令(有效、未被调度、且不被阻塞等待唤醒),则选择该Load指令重发,否则按照入队顺序,通过AgeDetector模块寻找一系列load replay queue项中最早入队的一项进行重发。
-
功能4:指令重发逻辑
-
Load_unit s3过来的请求根据enq.bits.isLoadReplay判断是否是已经从replay_queue出队的序列,如果是已经出队的序列,根据是否needReplay和有异常做下一步的判断,如果有异常或者不需要重发则释放这个槽位,并从agedetector里面把该项出队,如果需要重发则将这个项对应的scheduled位置为false来参与后续的出队仲裁竞争。
-
从freelist中选出发给load unit的有效项,项数为load unit的宽度(即有几条load unit的流水线),根据优先级来进行出队。
-
第0拍将数据传递给第1拍由s0_can_go控制,当s0_can_go为1时才能将0拍得到的数据发给第一拍,s0_can_go有效的条件是s0被重定向或者s1_can_go为1。
-
第一拍从vaddr内部取出需要的虚拟地址,发给下一拍流水线。 ColdCouter的值在0到12之间,上一拍没有被阻塞并且整个过程没有发生重定向的时候,向load unit发送请求。
-
发送给下一拍流水线的数据受s1_can_go控制,s1_can_go为1的条件是:
-
ColdCouter的值在0到12之间 且 上一拍完成操作(未被阻塞)或者不需要发送数据两者之一。
-
发生数据的重定向。
- 第二拍将收到第一拍的数据发送给对应的load unit,获取仲裁权限,完成重发指令的任务。
接口说明
name | I/O | description |
---|---|---|
redirect | input | 后端重定向相关信息 |
vecFeedback | input | 来自两条流水线的向量反馈信息 |
enq | input | 表示外部模块希望将 load 指令传递给当前模块,来自 load 指令流水线的 s3 级 |
storeAddrIn | input | 在一个时钟周期内接收多条 store 指令的地址信息,用于判断指令存储的地址是否已经准备好 |
storeDataIn | input | 在一个时钟周期内接收多条 store 指令的数据信息,用于判断指令存储的数据是否已准备好 |
replay | output | 用于处理 load 指令的重发请求,每个元素对应一个重发接口 |
tl_d_channel | input | 用于接收来自数据缓存(Dcache)的信息,在处理 load 指令时会使用该端口进行数据转发 |
stAddrReadySqPtr | input | 指向当前准备好地址的 store 指令 |
stAddrReadyVec | input | 向量中对应 store 指令的地址是否已经准备好 |
stDataReadySqPtr | input | 指向当前准备好数据的 store 指令 |
stDataReadyVec | input | 向量中对应 store 指令的数据是否已经准备好 |
sqEmpty | input | 当前 store 队列是否为空 |
lqFull | output | 当前 load 队列是否已满 |
ldWbPtr | input | 指向当前写回的load指令 |
rarFull | input | rar 队列是否已满 |
rawFull | input | raw 队列是否已满 |
l2_hint | input | 当 dcache miss 后,需要继续查询下级缓存 L2 Cache。在 L2 Cache 回填前的 2 或 3 拍,L2 Cache 会提前给 LoadQueueReplay 唤醒信号,称为 L2 Hint |
tlb_hint | input | 作用类似于 l2_hint,接收当前的 TLB 提示信息 |
tlbReplayDelayCycleCtrl | input | 控制 TLB 重发的延迟周期 |
1.4 - LoadQueueUncache
本文档参考香山LSQ设计文档写成
本文档撰写的内容截至[66e9b546]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
LoadQueueUncache 简介
LoadQueueUncache 和 Uncache 模块,对于 uncache load 访问请求来说,起到一个从 LoadUnit 流水线到总线访问的中间站作用。其中 Uncache 模块,作为靠近总线的一方,主要用于处理 uncache 访问到总线的请求和响应。LoadQueueUncache 作为靠近流水线的一方,需要承担以下责任:
-
接收 LoadUnit 流水线传过来的 uncache load 请求。
-
选择已准备好 uncache 访问的 uncache load 请求 发送到 Uncache Buffer。
-
接收来自 Uncache Buffer 的处理完的 uncache load 请求。
-
将处理完的 uncache load 请求 返回给 LoadUnit。
LoadQueueUncache 结构上,目前有 4 项(项数可配)UncacheEntry,每一项独立负责一个请求并利用一组状态寄存器控制其具体处理流程;有一个 FreeList,管理各项分配和回收的情况。而 LoadQueueUncache 主要是协同 4 项的新项分配、请求选择、响应分派、出队等统筹逻辑。
整体框图
图1:LoadQueueUncache结构示意图
UnCacheBuffer 最多存放4条指令,除了 FreeList 之外,另一个重要的子模块是 UncacheEntry,管理每个Uncahce请求,负责发起Uncache,写回Uncache数据。每个Entry内维护一个用于发起Uncache请求的状态机,状态机的状态转换图如下:

图2:UncacheEntry状态转换图
-
s_idl:该项还未发起一个MMIO请求。
-
s_req:向uncache模块发起MMIO请求,等待请求被接收。
-
s_resp:等待uncache模块的MMIO响应。
-
s_wait:等待将MMIO结果写回流水线。
功能简介
模块功能说明
功能1:Uncache指令请求入队
LoadQueueUncache 负责接收来自 LoadUnit 0、1、2 三个模块的请求,这些请求可以是 MMIO 请求,也可以是 NC 请求。
-
首先,系统会根据请求的 robIdx 按照时间顺序(从最老到最新)对请求进行排序,以确保最早的请求能优先分配到空闲项,避免特殊情况下因老项回滚(rollback)而导致死锁。
-
进入入队处理的条件是:请求没有重发、没有异常,并且系统会根据 FreeList 中可分配的空闲项依次为请求分配项。
-
当 LoadQueueUncache 达到容量上限,且仍有请求未分配到项时,系统会从这些未分配的请求中选择最早的请求进行 rollback。
UncacheBuffer 的入队分为 s1 和 s2 两个阶段:
s1:
-
请求收集:通过
io.req.map(_.bits)
收集所有请求的内容,形成s1_req
向量。 -
有效性标记:通过
io.req.map(_.valid)
收集所有请求的有效性,形成s1_valid
向量。
s2:
执行入队操作,主要分为以下几步:
-
使用
RegEnable
将 s1 阶段的请求s1_req
注册到s2_req
,确保在请求有效时保持其状态。 -
通过以下条件生成
s2_valid
向量,判断每个请求是否有效:-
RegNext(s1_valid(i))
:确保请求在 s1 阶段有效。 -
!s2_req(i).uop.robIdx.needFlush(RegNext(io.redirect))
:确保请求的 ROB 索引不需要因重定向而被刷新。 -
!s2_req(i).uop.robIdx.needFlush(io.redirect)
:确保请求的 ROB 索引不需要因当前重定向而被刷新。
-
-
检查每个请求是否需要重发,结果存储在
s2_need_replay
向量中。 -
在 s2 阶段,使用
s2_enqueue
向量来决定哪些请求成功入队。入队条件包括:-
s2_valid(w)
:请求在 s2 阶段有效。 -
!s2_has_exception(w)
:请求没有异常。 -
!s2_need_replay(w)
:请求不需要重发。 -
s2_req(w).mmio
:请求是一个内存映射 IO(MMIO)请求。
-
-
通过
enqValidVec
和enqIndexVec
的有效管理,确保每个加载请求在满足有效性和可分配条件时能够正确地申请和分配FreeList槽位。
功能2:Uncache指令的出队
-
当一个项完成 Uncache 访问操作并返回给 LoadUnit ,或被 redirect 刷新时,则该项出队并释放 FreeList 中该项的标志。
具体流程如下:
-
计算
freeMaskVec
掩码,用于标记每个槽位的释放状态,指示相应槽位是否可用。 -
如果当前条目被选择 (
e.io.select
) 且其输出信号有效 (e.io.ldout.fire
),则对应槽位的释放状态被标记为true
,表示该槽位可用。 -
如果接收到刷新信号 (
e.io.flush
),同样将对应槽位的释放状态设置为true
。
-
-
同一拍可能有多个项出队。返回给 LoadUnit 的请求,会在第一拍中选出,第二拍返回。
-
其中,可供处理 uncache 返回请求的 LoadUnit 端口是预先设定的。当前,MMIO 只返回到 LoadUnit 2;NC 可返回到 LoadUnit 1\2。在多个端口返回的情况下,利用 uncache entry id 与端口数的余数,来指定每个项可以返回到的 LoadUnit 端口,并从该端口的候选项中选择一个项进行返回。
功能3:Uncache交互逻辑
- 发送 req
第一拍先从当前已准备好 uncache 访问中选择一个,第二拍将其发送给 Uncache Buffer。发送的请求中,会标记选中项的 id,称为 mid 。其中是否被成功接收,可根据 req.ready 判断。
- 接收 idResp
如果发送的请求被 Uncache Buffer 接收,那么会在接收的下一拍收到 Uncache 的 idResp。该响应中,包含 mid 和 Uncache Buffer 为该请求分配 entry id(称为 sid)。LoadQueueUncache 利用 mid 找到内部对应的项,并将 sid 存储在该项中。
- 接收 resp
待 Uncache Buffer 完成该请求的总线访问后,会将访问结果返回给 LoadQueueUncache。该响应中,包含 sid。考虑到 Uncache Buffer 的合并特性(详细入队合并逻辑见 Uncache),一个 sid 可能对应 LoadQueueUncache 的多个项。LoadQueueUncache 利用 sid 找到内部所有相关项,并将访问结果传递给这些项。
功能4:Uncache回滚检测
freelist 没有空闲表现导致 MMIO Load 进入 UncacheBuffer 失败时需要进行 rollback,此 时需要根据 robidx 选择不能入队的 MMIO 中最老的指令进行 rollback。整个流程分为以下几个周期:
-
Cycle 0:进行 uncache 请求入队。
-
Cycle 1:选择最旧的 uncache 加载请求。
-
Cycle 2:发出重定向请求。
-
从 load 流水线中选择最旧的 load 请求。
-
根据检测到的拒绝情况准备重定向请求。
-
如果重定向请求有效,则发出请求。
-
使用 selectOldestRedirect
函数来选择最旧的重定向请求,具体步骤如下:
-
比较向量生成:
- 创建一个比较向量
compareVec
,用于判断请求的顺序,比较每个请求的 ROB 索引。
- 创建一个比较向量
-
生成独热编码结果:
resultOnehot
向量根据有效性和比较结果生成,标记出最旧的可重定向请求。
接口说明
name | I/O | description |
---|---|---|
redirect | input | 后端重定向相关信息 |
req | input | 接收写入请求 |
ldout | output | 写回 MMIO 数据接口,输出 MemExuOutput 类型的数据,处理与 MMIO 的写回操作 |
ld_raw_data | output | 读取原始数据输出接口 |
rob | input | 接收来自 ROB 的信号或数据 |
uncache | output | 发送数据或信号给 uncache 模块 |
rollback | output | 当 uncache 缓存满时,从前端进行回滚 |
1.5 - StoreQueue
本文档参考香山LSQ设计文档写成
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
StoreQueue 简介
StoreQueue是一个队列,用来装所有的 store 指令,功能如下:
-
在跟踪 store 指令的执行状态
-
存储 store 的数据,跟踪数据的状态(是否到达)
-
为load提供查询接口,让load可以forward相同地址的store
-
负责 MMIO store和NonCacheable store的执行
-
将被 ROB 提交的 store 写到 sbuffer 中
-
维护地址和数据就绪指针,用于LoadQueueRAW的释放和LoadQueueReplay的唤醒
store进行了地址与数据分离发射的优化,即 StoreUnit 是 store 的地址发射出来走的流水线,StdExeUnit 是 store 的数据发射出来走的流水线,是两个不同的保留站,store 的数据就绪了就可以发射到 StdExeUnit,store 的地址就绪了就可以发射到 StoreUnit。
整体框图
图1:StoreQueue结构示意图
StoreQueue最多可以存放64条指令,store queue 中重要的状态位有:
- allocated:RS在storeQueue队列有空闲时,会设置这个entry的allocated状态,开始记录这条store 的生命周期。同时发射到StoreUnit/ StdExeUnit 2条流水。当这条store指令被提交到Sbuffer时,allocated状态被清除。
- addrvalid:在StoreUnit的S1更新,表示是否已经经过了地址转换得到物理地址,用于 load forward 检查时的 cam 比较。
- datavalid:在StdExeUnit 的S1更新,表示store 的数据是否已经被发射出来,是否已经可用
- committed:在store 是否已经被 ROB commit 了
- pending:在StoreUnit的S2更新,在这条 store 是否是 MMIO 空间的 store,主要是用于控制 MMIO 的状态机
- mmio:在StoreUnit的S2更新,这条 store 是否是 MMIO 空间的 store,主要是用于控制对 sbuffer 的写
非对齐store指令
StoreQueue支持处理非对齐的Store指令,每一个非对齐的Store指令占用一项,并在写入dataBuffer对地址和数据对齐后写入。
向量store指令
如图2所示,StoreQueue会给向量store指令预分配一些项。
StoreQueue通过vecMbCommit控制向量store的提交:
-
针对每个 store,从反馈向量 fbk 中获取相应的信息。
-
判断该 store 是否符合提交条件(valid 且标记为 commit 或 flush),并且检查该 store 是否与 uop(i) 对应的指令匹配(通过 robIdx 和 uopIdx)。只有当满足所有条件时,才会将该 store 标记为提交。判断VecStorePipelineWidth内是否有指令满足条件,满足则判断该向量store提交,否则不提交。
-
特殊情况处理(跨页 store 指令):
在特殊情况下(当 store 跨页且 storeMisalignBuffer 中有相同的 uop),如果该 store 符合条件io.maControl.toStoreQueue.withSameUop
,会强制将 vecMbCommit设置为 true,表示该 store 无论如何都已提交。
图2:向量store指令
功能简介
模块功能说明
功能1:store指令请求入队
-
StoreQueue 每次最多会有 2 个 entry 入队,通过入队指针 enqPtrExt 控制。在 dispatch 阶段最多可以分配2个 entry,指针每次右移 1 位或 2 位。
-
通过比较入队指针 enqPtrExt 和出队指针 deqPtrExt 得出已经在队列中有效 entry。只有空闲的 entry 大于需要请求入队的指令时才会分配 entry 入队。
-
入队时设置 entry 的状态位 allocated 为 true,其他状态位都为 false。
功能2:指令的出队
-
StoreQueue 每次最多会有2个 entry 出队释放,通过输出指针 deqPtrExt 控制,每次指针右移一位或 2 位。
-
STQ 出队的触发信号是isbuffer(i).fire延后一拍的信号,因为 sbuffer 的写动作要用 2 拍完成,在 sbuffer 写完成之前 entry 不释放可以继续 forward 数据。
功能3:从store的地址流水线写回结果
store 的地址从保留站发出来后会经过 StoreUnit 流水线,通过lsq/lsq_replenish总线接口在S1/S2把地址信息更新到store queue 中:
-
在store流水线s1阶段,获得 DTLB hit/miss 的信息, 以及指令的虚拟地址vaddr和物理地址paddr
-
在store流水线s2阶段,获得 mmio/pmp 信息,以及是否是mmio地址空间操作等信息
功能4:接收 store 的数据到STQ 的Datamodule
store 的数据是从与地址不同的保留站发出来的后经过StdExeUnit
流水线,通过storeDataIn
接口在S0/S1把数据写到对应的entry的datamodule
里:
-
S0:给
datamodule
发写请求 -
S1:写入数据到
datamodule
同时更新 entry 的datavalid
属性为True,接收 store 的mask到STQ 的Datamodule
store 的地址从保留站发出来之后会经过StoreUnit
流水线,s0_mask_out
在S0把地址中的mask信息更新到对应entry的datamodule
里。
功能5:为 load 提供 forward 查询
- load 需要查询 store queue 来找到在它之前相同地址的与它最近的那个 store 的数据。
- 查询总线(
io.forwrd.sqIdx
) 和 StoreQueue 的出栈指针比较,找出所有比 load 指令老的 storeQueue 中的 entry。以 flag 相同或不同分为2种情况:
- 查询总线(
(1)same flag-> older Store范围是 (tail, sqIdx),如图3(a)所示
(2)different flags-> older Store范围是(tail, VirtualLoadQueueSize) +(0, sqIdx),如图3(b)所示
图3:StoreQueue前递范围生成
-
查询总线用va 和pa同时查询,如果发现物理地址匹配但是虚拟地址不匹配;或者虚拟地址匹配但是物理地址不匹配的情况就需要将那条 load 设置为 replayInst,等 load 到 ROB head 后replay。
-
如果只发现一笔 entry 匹配且数据准备好,则直接 forward
-
如果只发现一笔 entry 匹配且数据没有准备好,就需要让保留站负责重发
-
如果发现多笔匹配,则选择最老的一笔 store forward,StoreQueue以1字节为单位,采用树形数据选择逻辑,如图4
图4:StoreQueue前递数据选择
-
store 指令能被 load forward的条件:
- allocated:这条 store 还在 store queue 内,还没有写到 sbuffer
- datavalid:这条 store 的数据已经就绪
- addrvalid:这条 store 已经完成了虚实地址转换,得到了物理地址
-
SSID (Store-Set-ID) 标记了之前 load 预测执行失败历史信息,如果当前 load 命中之前历史中的SSID,会等之前所有 older 的 store 都执行完;如果没有命中就只会等pa相同的 older Store 执行完成。
功能6:MMIO与NonCacheable Store指令
- MMIO Store指令执行:
-
MMIO 空间的 store 也只能等它到达 ROB 的 head 时才能执行,但是跟 load 稍微有些不同,store 到达 ROB 的 head 时,它不一定位于 store queue 的尾部,有可能有的 store 已经提交,但是还在 store queue 中没有写入到 sbuffer,需要等待这些 store 写到 sbuffer 之后,才能让这条 MMIO 的 store 去执行。
-
利用一个状态机去控制MMIO的store执行
-
s_idle:空闲状态,接收到MMIO的store请求后进入到s_req状态;
-
s_req:给MMIO通道发请求,请求被MMIO通道接受后进入s_resp状态;
-
s_resp:MMIO通道返回响应,接收后记录是否产生异常,并进入到 s_wb 状态
-
s_wb:将结果转化为内部信号,写回给 ROB,成功后,如果有异常,则进入s_idle, 否则进入到 s_wait 状态
-
s_wait:等待 ROB 将这条 store 指令提交,提交后重新回到 s_idle 状态
-
- NonCacheable Store指令执行:
-
NonCacheable空间的store指令,需要等待上一个NonCacheable Store指令提交之后,才能从StoreQueue按序发送请求
-
利用一个状态机去控制NonCacheable的store执行
-
nc_idle:空闲状态,接收到NonCacheable的store请求后进入到nc_req状态;
-
nc_req:给NonCacheable通道发请求,请求被NonCachable通道接受后, 如果启用uncacheOutstanding功能,则进入nc_idle,否则进入nc_resp状态;
-
nc_resp:接受NonCacheable通道返回响应,并进入到nc_idle状态
-
功能7:store指令提交以及写入SBuffer
StoreQueue采用提前提交的方式进行提交。
- 提前提交规则:
-
检查进入提交阶段的条件
(1)指令有效。
(2)指令的ROB对头指针不超过待提交指针。
(3)指令不需要取消。
(4)指令不等待Store操作完成,或者是向量指令
-
如果是CommitGroup的第一条指令, 则
(1)检查MMIO状态: 没有MMIO操作或者有MMIO操作并且MMIO store以及提交。
(2)如果是向量指令,需满足vecMbCommit条件。
-
如果不是CommitGroup的第一条指令,则:
(1)提交状态依赖于前一条指令的提交状态。
(2)如果是向量指令,需满足vecMbCommit条件。
提交之后可以按顺序写到 sbuffer, 先将这些 store 写到 dataBuffer 中,dataBuffer 是一个两项的缓冲区(0,1通道),用来处理从大项数 store queue 中的读出延迟。只有0通道可以编写未对齐的指令,同时为了简化设计,即使两个端口出现异常,但仍然只有一个未对齐出队。
- 写入sbuffer的过程:
-
写入有效信号生成
-
0通道指令存在非对齐且跨越16字节边界时:
(1) 0通道的指令已分配和提交
(2) dataBuffer的0,1通道能同时接受指令,
(3) 0通道的指令不是向量指令,并且地址和数据有效;或者是向量且vsMergeBuffer以及提交。
(4) 没有跨越4K页表;或者跨越4K页表但是可以被出队,并且1)如果是0通道:允许有异常的数据写入; 2)如果是1通道:不允许有异常的数据写入。
(5) 之前的指令没有NonCacheable指令,如果是第一条指令,自身不能是Noncacheable指令
-
否则,需要满足:
(1) 指令已分配和提交。
(2) 不是向量且地址和数据有效,或者是向量且vsMergeBuffer以及提交。
(3) 之前的指令没有NonCacheable和MMIO指令,如果是第一条指令,自身不能是Noncacheable和MMIO指令。
(4) 如果未对齐store,则不能跨越16字节边界,且地址和数据有效或有异常
- 地址和数据生成:
-
地址拆分为高低两部分:
(1) 低位地址:8字节对齐地址
(2) 高位地址:低位地址加上8偏移量
-
数据拆分为高低两部分:
(1) 跨16字节边界数据:原始数据左移地址低4位偏移量包含的字节数
(2) 低位数据:跨16字节边界数据的低128位;
(3) 高位数据:跨16字节边界数据的高128位;
-
写入选择逻辑:
如果dataBuffer能接受非对齐指令写入,通道0的指令是非对齐并且跨越了16字节边界,则检查:
(1) 是否跨4K页表同时跨4K页表且可以出队: 通道0使用低位地址和低位数据写入dataBuffer; 通道1使用StoreMisaligBuffer的物理地址和高位数据写入dataBuffer
(2) 否则: 通道0使用低位地址和低位数据写入dataBuffer; 通道1使用高位地址和高位数据写入dataBuffer
(3) 如果通道指令没有跨越16字节并且非对齐,则使用16字节对齐地址和对齐数据写入dataBuffer
(4) 否则,将原始数据和地址写给dataBuffer
功能8:强制刷新sbuffer
StoreQueue采用双阈值的方法控制强制刷新Sbuffer:上阈值和下阈值。
- 当StoreQueue的有效项数大于上阈值时, StoreQueue强制刷新Sbuffer
- 直到StoreQueue的有效项数小于下阈值时,停止刷新Sbuffer。
接口说明
name | description |
---|---|
enq | 接收来自外部模块的信息,包含入队请求、控制信号等 |
brqRedirect | 分支重定向信号 |
vecFeedback | 向量反馈信息 |
storeAddrIn | store指令的地址 |
storeAddrInRe | store指令的地址,用于处理MMIO 和异常情况 |
storeDataIn | store指令的数据 |
storeMaskIn | 传递store掩码,从保留站(RS)发送到 Store Queue(SQ)。store掩码通常用于指示哪些字节在store操作中是有效的。 |
sbuffer | 存储已提交的 Store 请求到sbuffer |
uncacheOutstanding | 指示是否有未完成的uncached请求 |
cmoOpReg | 发送缓存管理操作请求 |
cmoOpResp | 接收缓存管理操作的响应 |
mmioStout | 写回uncache的存储操作的结果 |
forward | 查询forwarding信息 |
rob | 接收来自 ROB 的信号或数据 |
uncache | 发送数据或信号给 uncache 模块 |
flushSbuffer | 冲刷sbuffer缓冲区 |
sqEmpty | 标识store queue为空 |
stAddrReadySqPtr | 指向当前准备好地址的 store 指令 |
stAddrReadyVec | 向量中对应 store 指令的地址是否已经准备好 |
stDataReadySqPtr | 指向当前准备好数据的 store 指令 |
stDataReadyVec | 向量中对应 store 指令的数据是否已经准备好 |
stIssuePtr | 跟踪当前发出的store请求 |
sqCancelCnt | 指示在store queue中可以被取消的请求数量 |
sqDeq | 当前store queue中出队的请求位置 |
force_write | 是否强制写入存储操作 |
maControl | 与存储管理缓冲区(MA)进行控制信号的交互 |
1.6 - VirtualLoadQueue
本文档参考香山LSQ设计文档写成
本文档撰写的内容截至[ca892e73]
请注意,本文档撰写的测试点仅供参考,如能补充更多测试点,最终获得的奖励可能更高!
VirtualLoadQueue 简介
Virtualloadqueue是一个队列,用于存储所有load指令的微操作(MicroOp),并维护这些load指令之间的顺序,它的功能类似于重排序缓冲区(Reorder Buffer, ROB),但专注于load指令的管理。其主要功能是跟踪Load指令执行状态,以确保在并发执行的环境中,加载操作能够正确、有序地完成。
整体框图

图1:VirtualLoadQueue结构示意图
Virtualloadqueue最多可以存放72条指令,dispatch阶段最多支持6条指令同时入队,最多支持8条指令出队。Virtualloadqueue对于每一个 entry 中的 load 指令都有若干状态位来标识这个 load 处于什么状态:
allocated:该项是否分配了load,用于确定load指令的生命周期。
isvec:该指令是否是向量load指令。
committed: 该项是否提交。
功能简介
模块功能说明
功能1:load指令请求入队
在调度阶段,保留站通过入队(enq)总线向VirtualLoadQueue发起入队请求,最多支持六组并发请求。成功入队的条件包括以下几点:
- StoreQueue 和 LoadQueue 有预留空间:确保LoadQueue有足够的容量来接收新的加载指令,以避免队列溢出。确保StoreQueu有预留空间则是基于数据一致性和避免指令阻塞的考虑,因为store指令入队阻塞可能会导致load指令无法正确读取或forward到数据。
- 入队请求有效:入队请求必须是合法的,确保指令在调度过程中可以被正确处理。
- 指令未被冲刷:确保指令在入队时没有被系统标记为无效或被撤销。
成功入队之后,系统会执行以下操作:
- 将指令的lqidx作为索引,将对应的allocated寄存器置1,bits信息写入uop寄存器。
- 计算新的lqidx值,作为enq_resp传送给保留站。
功能2:接收load流水线写回的数据
在 load 流水线的s3阶段,load unit会将指令执行的信息通过总线 ldin 写回到 VirtualLoadQueue。具体写回信息包括:
- 是否发生了异常以及异常类型
- dcache是否命中
- tlb是否命中
- 是否为mmio指令
- 是否为软件预取或者硬件预取
- 是否需要重发以及重发的原因
- 写uop的使能信号
写回需要满足的条件如下:
- ldin 总线的 valid 信号需要拉高,表明当前正在进行有效的数据传输。
- 指令不应需要重发(即
need_rep
信号为 0),否则将影响写回的正常进行。
在满足写回条件后,系统将生成相应的写回响应,具体包括以下几个方面:
- 如果在执行过程中发生了异常、TLB命中或软件预取操作,
addrvalid
信号将被置为 1,表示地址信息有效。 - 如果在执行过程中发生了异常、MMIO操作、DCACHE命中并且不需要重发,或是软件预取操作,
datavalid
信号将被置为 1,表示数据有效。 - 指令在流水线的 S3 阶段有效(注意:不能是硬件预取指令)。当
ldin
总线的写使能信号data_wen_dup
拉高时,将更新队列中的uop信息,以确保指令的状态及时反映。
系统将addrvalid
和datavalid
分开进行处理是考虑到在一些情况下,地址可以被重用,而数据可能需要重新请求(如dcache miss/mmio/软件预取等)。分开标识可以减少流水线停顿,允许处理器在地址有效时继续执行其他指令,而不必等待数据有效性确认,从而优化整体性能。
功能3:load指令的出队(提交)
- 出队时机:当被分配的entries(allocated为高)到达队头,同时allocated与committed都为1时,表示可以出队,如果是向量load,需要每个元素都committed。
接口说明
name | I/O | width | description | |
---|---|---|---|---|
redirect | io.redirect.valid | input | 1 | 后端重定向有效位 |
io.redirect.bits.robIdx.flag | input | 1 | 后端重定向相关信息 | |
io.redirect.bits.robIdx.value | input | 8 | 后端重定向相关信息 | |
io.redirect.bits.level | input | 1 | 后端重定向相关信息 | |
enq | io.enq.canAccept | output | 1 | Lq能否接收派遣指令 |
io.enq.sqcanAccept | input | 1 | sq能否接收派遣至零 | |
io.enq.needAlloc_0~5 | input | 1 | ||
io.enq.req_0~5.valid | input | 1 | 入队请求的有效信号 | |
io.enq.req_0~5.bits.robIdx.flag | input | 1 | 入队请求ROB指针的flag | |
io.enq.req_0~5.bits.robIdx.value | input | 8 | 入队请求ROB指针的value | |
io.enq.req_0~5.bits.lqIdx.value | input | 7 | 入队请求lqidx的value | |
io.enq.req_0~5.bits.numLsElem | input | 5 | 1. 向量寄存器的总位宽为128位,每个向量元素的大小为8位,因此每个向量寄存器可以存储16个,numLsElem表示向量寄存器中元素的个数,因此位宽为5。 2. 如果是标量值零,numLsElem的值恒为5‘b1 3. 如果是向量指令,每个端口的numLsElem的最大值为[16 2 2 2 2 2] | |
ldin | io.ldin_0/1/2.valid | input | 1 | load写回到loadqueue的信息有效 |
io.ldin_0/1/2.bits.uop.cf.exceptionVec_3/4/5/13/21 | input | 1 | Load写回到流水线的指令发生异常 | |
io.ldin_0/1/2.bits.uop.robIdx_flag | input | 1 | load写回lq指令的rob指针的flag | |
io.ldin_0/1/2.bits.uop.robIdx_value | input | 8 | load写回lq指令的rob指针的value | |
io.ldin_0/1/2.bits.uop.lqIdx.value | input | 7 | load写回lq指令的lq指针的value | |
io.ldin_0/1/2.bits.miss | input | 1 | Load写回到Lq的指令发生cacheMiss | |
io.ldin_0/1.bits.tlbMiss | input | 1 | Load写回到Lq的指令发生tlbMiss | |
io.ldin_0/1/2.bits.mmio | input | 1 | Load写回到Lq的指令是MMIO指令 | |
io.ldin_0/1/2.bits.isPrefetch | input | 1 | 指令为预取操作,预取分为软件预取和硬件预取 | |
io.ldin_0/1/2.bits.isHWPrefetch | input | 1 | 指令为硬件预取 | |
io.ldin_0/1/2.bits.dcacheRequireReplay | input | 1 | Load写回到Lq的指令需要replay | |
io.ldin_0/1/2.bits.rep.info.cause_0~9 | input | 1 | Load写回到Lq的指令需要replay的原因: =0:st-ld violention predirect =1:tlb miss =2:st-ld forward =3:dcache replay =4:dcache miss =5:wpu predict fail =6:dcache bank conflict =7:RAR queue nack =8:RAW queue nack =9:st-ld violention | |
io.ldin_0/1/2.bits.data_wen_dup_1 | input | 1 | uop信息的写入使能信号 | |
ldWbPtr | io.ldWbPtr.flag | output | 1 | writeback指针的flag |
io.ldWbPtr.value | output | 7 | writeback指针的value | |
lqEmpty | io.lqEmpty | output | 1 | Lq是否空 |
lqDeq | io.lqDeq | output | 3 | 出队表项数量 |
lqCancelCnt | io.lqCancelCnt | output | 7 | 后端发生重定向时取消的load数量 |
2 - Shield-XS:基于Bitmap的安全隔离机制
在香山中,什么是香山bitmap 隔离模块,如何验证?
Location: MMU-L2TLB-Bitmap
2.1 - Shield-XS Bitmap 基础知识
本节将介绍 bitmap 的一些基础知识,有助于理解为什么我们需要 bitmap,本节包含:
- 背景描述
- 威胁模型
- 防御原理
- 工作流程
- 应用场景
术语描述
缩写 | 全名 | 含义 |
---|---|---|
TCB | Trusted Computing Base | 可信计算基,负责底层硬件的安全可信操作 |
TEE | Trusted Execution Environment | 可信执行环境 |
MMU | Memory Management Unit | 内存管理单元 |
RDSM | Root Domain Security Mananger | 根域权限管理器 |
C-SDSM | Confidencial Supervisor Domain Security Manager | 可信监督域 权限管理器 |
APLIC | advanced platform level interrupt controller | 平台级中断控制器 |
LLC | Last Lavel Cache | 末级缓存 |
DMA | Direct Memory Access | 直接内存访问 |
CVM | Confidencial Virtual Machine | 可信虚拟机 |
TLB | Translation Lookaside Buffer | 页表缓存 |
MBMC | machine level bitmap check | bitmap CSR 特殊状态寄存器 |
BMA | Bitmap Adress | Shield Bitmap 专属内存区域基地址 |
CMODE | Confidencial Mode | 开启bitmap后当前模式是否是可信 |
BME | Bitmap Enable | 是否开启 Bitmap |
PTW | Page Table Walker | 页表遍历器 |
HPTW | Hypervisor PTW | 监督域页表遍历器 |
LLPTW | Last Level PTW | 末级页表遍历器 |
2.1.1 - 背景描述
背景描述
自计算机问世以来,数据安全始终是至关重要的议题。在个人计算机时代,数据安全主要集中在单机硬盘等存储设备的安全性上。随着互联网时代的到来,数据安全的范畴扩展到了计算机之间数据传输的安全性,促使了安全传输协议的设计与发展。如今,在云计算时代,用户的大量数据被存储和处理在云端。在云环境中存储、共享和计算的数据面临着多重安全威胁。在云计算环境下,不同类型的负载面临各自的威胁模型包括但不限于:
计算型负载:恶意软件可能侵入操作系统或固件,攻击 CPU 和内存资源,导致数据泄露或系统性能下降。
内存型负载:恶意软件或进程可能试图访问内存型负载使用的内存区域,导致数据泄露或篡改。
存储型负载:存储设备及其 DMA 功能可能被攻击者利用,直接访问或篡改存储设备内存,绕过传统安全检查。
网络型负载:网络攻击可能利用网络接口控制器(NIC)等网络设备漏洞入侵系统,通过 DMA 功能访问或篡改内存中的敏感数据。
不同工作负载面临的安全威胁
威胁模型
类别 | 安全准则 | 描述 |
---|---|---|
内存分配 | 动态分配安全/非安全内存的能力 | 安全内存应根据需求动态分配或释放。 |
内存机密性 | 内存隔离 | 防止非可信计算基(non-TCB)组件读取可信执行环境(TEE)的内存。 |
抵御软件攻击的内存完整性 | 防止软件攻击(如内存重映射、别名攻击、重放攻击、数据破坏等)。 | |
共享内存 | TEE控制与非TCB组件的数据共享 | 防止非TCB代码在未经TEE同意的情况下泄露信息。 |
TEE控制与其他TEE的数据共DUT 享 | 支持TEE之间安全共享内存的能力。 | |
I/O保护 | 防止非TCB设备通过DMA访问TEE内存 | 禁止未被TCB接纳的外设设备访问TEE内存。 |
来自TCB内设备的可信I/O | 通过准入控制将设备绑定到TEE。 |
2.1.2 - 香山 bitmap 防御原理
防御原理
针对日益复杂的负载的安全威胁,香山轻量动态隔离模型为经典负载机密虚拟机和容器设计了一种动态隔离机制,用来保护正在使用中的数据的机密性和完整性。依据不同的应用场景,将负载从安全的维度进行划分,分为安全敏感型负载和非安全敏感型负载。
安全敏感型负载需要运行在可信执行环境下,用来保护正在使用中数据和代码的机密性和完整性。非安全敏感型负载对数据没有安全性保护需求,执行在开放的执行环境下,保证执行的效率。
基于bitmap的安全隔离的防御机制,其核心原理是通过可信根在软件层面的标记(Shield-bit)来隔离和保护敏感工作负载(Sensitive Workloads)与普通工作负载(Normal Workloads),从而增强系统的安全性。以下是其防御原理的详细描述:
1. 分类与标记
工作负载分类:系统将工作负载分为两类:
-
敏感工作负载(Sensitive Workloads):需要高安全级别的任务,如机密虚拟机、安全容器的任务(隐私数据的访问、处理)等。
-
普通工作负载(Normal Workloads):常规任务,代表系统中普通的应用程序或进程,安全要求较低。
Shield-bit 标记:通过 “Shield-bit”(每比特代表 4KB)对敏感工作负载使用的资源进行标记,依据需此标记动态调整资源访问权限:
-
Shield-bit = 1
表示安全敏感资源 -
Shield-bit = 0
表示非安全敏感资源
2. 资源隔离
资源池划分:图中显示资源池中包含混合的敏感和普通工作负载,但通过 Shield-bit 实现逻辑隔离。
资源类型保护:敏感工作负载访问特定资源(如内存、IO设备、中断等)时,Shield-bit 会触发保护机制,防止普通工作负载或恶意程序越权访问。例如:
-
内存隔离:敏感数据的内存区域仅对标记为敏感的工作负载可访问。
-
设备与中断保护:关键外设或中断仅允许敏感工作负载调用。
3. Bypass 机制
Bypass Shield-bit:在单向隔离的情况下(仅对普通负载的资源访问作限制),安全敏感负载可绕过 Shield-bit 的机制直接访问资源提升性能。
4. 防御目标
防止横向渗透:攻击者通过普通工作负载漏洞无法访问敏感资源。
最小权限原则:每个工作负载仅能访问其必需的资源,降低攻击面。
动态安全调整:根据工作负载的敏感程度动态切换保护状态。
5. 技术实现
硬件支持:依赖内存管理单元(MMU)的扩展功能。
软件协同:由可信计算基负责标记管理和资源调度。
香山Shield-XS 轻量隔离模型
6. 核心工作流程
Shield-XS 隔离模型工作流程
A. 启动
系统初始化,加载固件(Firmware TCB)和可信执行环境(TEE TCB)。
开启Bitmap安全检测功能。
B. 配置
设定安全策略,例如定义受保护资源(内存、IO、中断等)访问权限。
TEE-TCB 依据应用需求配置不同资源的安全/非安全属性。
C. 检测
硬件监控访问行为,识别异常或非法访问请求。
通过 Bitmap 或其他机制标记不信任的访问行为。
D. 故障处理
当检测到违规操作(如未授权的内存访问)时,触发安全机制。
7. 关键安全机制
支持对所有地址映射类资源(内存、I/O、中断)的动态隔离。
资源隔离支持按需分配的,而非静态划分(区别于PMP)。
使用位图(Bitmap)标记资源的信任状态,快速过滤不信任的访问请求。
8. 技术特点
灵活性
动态隔离允许根据运行时需求调整安全策略,适应不同场景。
细粒度控制
支持多种资源类型(内存、I/O、中断)的精细化权限管理。
轻量级检测
通过Bitmap等高效数据结构实现低开销的安全监控。
2.1.3 - bitmap的工作流程
bitmap的工作流程是怎样的?
1. 识别负载类型
根据应用场景和安全需求,将负载分为安全敏感型负载和非安全敏感型负载:
-
安全敏感型负载:需要在可信执行环境中运行,以保护数据和代码的机密性和完整性。
-
非安全敏感型负载:可以在开放的执行环境中运行,主要关注执行效率。
2. 设置隔离属性
定义隔离属性:为不同类型负载设置隔离属性,包括单向隔离和双向隔离。
-
单向隔离:允许安全敏感型负载访问非安全敏感型负载的资源,但不允许反向访问。
-
双向隔离:安全敏感型负载和非安全敏感型负载之间互相不能访问对方的资源。
3. 划分资源池
划分物理内存资源:将物理内存划分为不同的资源池以支持不同负载的隔离需求。
-
安全资源池:分配给安全敏感型负载。
-
非安全资源池:分配给非安全敏感型负载。
4. 配置安全属性
设置内存页安全状态:使用Bitmap数据结构标记每个物理内存页的安全状态。
-
安全页:标记为1,表示该页属于安全资源池。
-
非安全页:标记为0,表示该页属于非安全资源池。
5. 动态分配资源
按需分配内存:根据负载的启动和运行时需求,动态分配相应的内存资源。
-
安全敏感型负载:从安全资源池分配内存,并确保其访问受限于安全页。
-
非安全敏感型负载:从非安全资源池分配内存,允许其访问非安全页。
6. 隔离检测判断
判断隔离类型:判断当前负载是否需要单向隔离或者双向隔离。
-
单向隔离:仅对非安全敏感型负载进行安全性检测,确保其不访问安全资源。
-
双向隔离:对所有负载类型进行隔离检测,确保不同负载之间互不干扰。
7. 安全性检测
执行访问控制检查:
-
对于单向隔离情况,检查非安全敏感型负载是否试图访问安全资源。
-
对于双向隔离情况,检查所有负载类型是否试图越界访问其他类型的资源。
8. 动态回收资源
释放不再使用的资源:负载执行完毕后,其占用的内存资源被回收并返回到相应的资源池中,以便后续其他负载的使用。
2.1.4 - 香山bitmap 应用场景
应用场景
1. 机密虚拟机
Shield-XS用于普通虚拟机和机密虚拟机的隔离。在机密虚拟机(Confidential VM, CVM)环境中,bitmap 用于标记和隔离安全内存区域。
普通虚拟机在访问内存时,硬件通过 bitmap 检查机制确保其只能访问被授权的内存区域,从而实现虚拟机之间的内存隔离。
注:机密虚拟机之间的隔离通过MMU 完成,不需要经过Bitmap 的检查机制。
CVM Bitmap 的资源的标记和分配由C-SDSM (Confidential Supervisor Domain Security Manager) 完成,Hypervisor 和 CVM 通过TEECall 和 TEEResume 进行安全世界的交互。
TEECall
- 从普通世界(Normal World)切换到安全世界(TEE),进入敏感工作负载执行流程。
TEEResume
- 从 TEE 返回普通世界,恢复 Normal Workloads 的执行。
2. 安全增强容器
上图显示了bitmap在容器中的应用场景。安全容器和普通容器都位于可信计算基的保护范围内。在安全容器中运行的应用程序可能包含敏感数据或关键业务逻辑。为了防止这些数据被普通容器或恶意软件访问,采用通过 bitmap 机制进行隔离。
Bitmap 数据结构用于标记哪些物理页属于安全容器的存储空间,通过将对应该容器内存区域的位图数据写为1,确保普通容器无法访问这些存储空间。只有安全容器内的应用程可以访问这些被标记为安全的内存页。
普通容器中的应用程序可能来自不可信的来源。通过 bitmap 机制,普通容器的内存访问被限制在非安全内存区域。任何试图访问安全内存的请求都会被硬件拦截,并触发Access Fault。
在上述场景的具体的实施细节中,软件模块负责Bitmap管理和配置 , 硬件机制负责Bitmap检查和同步。
2.2 - Shield-XS Bitmap总体设计
本节介绍bitmap的总体设计,不包括具体的硬件实现,只解释概念,包含:
- 机器模式 Bitmap控制状态寄存器
- Shield-Bitmap Cache 加速查表
- 虚拟化两阶段内存地址翻译转换原理
- 加入了Shield-XS安全检查后的内存地址转换流程
2.2.1 - 机器模式Bitmap控制状态寄存器
机器模式 Bitmap控制状态寄存器
基本信息
寄存器名称 | MBMC(Machine BitMap Control) |
---|---|
特权模式 | 机器模式 |
寄存器编号 | 0xBC2 |
读写权限 | 可读可写 |
功能描述 | 用来控制Shield-bitmap 的使能、同步、Shield-XS Bitmap 基地址等 |
字段描述
地址 | 字段 | 描述 |
---|---|---|
[61:3] | BMA (Bitmap Address) | Shield-XS Bitmap 基地址指定 Bitmap 数据结构在物理内存中的起始地址。 |
2 | CMODE (Current Mode) | 表明当前执行模式CMODE = 1,表示安全模式CMODE = 0, 表示非安全模式。 |
1 | BCLEAR (Bitmap Clear) | Shield-XS Bitmap 同步位BCLEAR = 1,表示刷新所有Shield-bit 副本。 注:实际使用过程,可结合HFence 指令 |
0 | BME (Bitmap Enable) | Shield-XS 使能位当 BME 置为 1 时,启用 Bitmap 功能。一旦启用,无法关闭,也无法修改 Bitmap 的基地址。 |
数据结构
Shield-XS Bitmap数据结构
上图为Shield-XS Bitmap的数据结构,所有的Shield-XS Bitmap权限数据都被存放在一块连续的物理内存区域中。其中基地址是存放Shield-XS Bitmap 数据结构的内存区域的起始物理地址。这个地址可以通过MBMC寄存器中的 BMA字段进行配置。
Shield-XS Bitmap 数据结构的大小取决于系统内存的大小。每个4k物理页对应一个比特位。一个4k页对应的权限根据其物理地址存放在Shield-XS Bitmap 数据结构中,其位置可以通过基地址加上该4k页物理地址的偏移量计算得出。
当BME =1 (开启bitmap) 且CMODE= 0 (当前模式处于 非安全模式)时,会进行bitmap检查。当bitmap 检查 当前4k页 bitmap属性 为1( 安全页面)时,会触发访问错误(access fault)。
当BME =1 (开启bitmap) 且CMODE= 1 (当前模式处于 安全模式)。无论安全还是非安全页面,当前状态都允许访问,因此无需进行bitmap检查。
当BME =0 (关闭bitmap)无需进行bitmap检查。
2.2.2 - Shield-Bitmap Cache
Shield-Bitmap Cache 性能加速
当访问一个物理地址 (PA) 时,硬件通过查找 Shield-XS Bitmap 数据结构中对应的位置来确定该页是否具有安全属性。
查找安全属性的物理地址计算公式如下:
即 Shield-Bitmap 访问地址 = MBMC.BMA + PA[ XLEN-1 : 12 + log2(XLEN/8) ]
随后用选出对应该 4KB 页的权限。如果对应比特位为 1 ,则表示该页具有安全属性,只能被安全敏感型负载访问;如果为 0 ,则表示该页不具有安全属性,可以被非安全敏感型负载访问。
Shield-XS 直接从内存中读取安全属性
开启Shield-XS 隔离之后,CPU 内部发起的任何一笔访存操作,都需要对标记安全属性的存储空间发起访问,从内存中获取安全属性的延迟过长,使得系统的性能变差。
为了Shield-XS 隔离带来的性能损失,利用了程序的局部性,增加了Shield-Bitmap Cache, 当任何一笔访存需要获取安全属性时,优先从Shield-Bitmap Cache 中读取,只有Shield-Bitmap Cache Miss时,才会发起访存请求。
Shield-XS优先从Shield-BitMap Cache中读取安全属性
2.2.3 - Shield-Bitmap 地址翻译
虚拟化两阶段地址翻译流程
bitmap和MMU-L2TLB耦合,会在虚拟化两阶段地址走表翻译完成后才进行隔离检查。如果bitmap检查没有通过,就向MMU-L1TLB 发送af。因此对L1TLB以及其上面的模块来说,bitmap是透明的。所以,在进一步介绍前,需要先了解MMU处理虚拟化两阶段地址的流程。
基本概念
- 两阶段翻译:Guest Virtual Address (GVA) → Guest Physical Address (GPA) → Host Physical Address (HPA)
- 关键寄存器 :
- hgatp :控制G-stage(客户机阶段)页表根地址
- vsatp :控制VS-stage(虚拟化监督模式阶段)页表根地址
1. VAPT (Virtual Address Protection and Translation)
作用 :管理客户机虚拟地址(GVA)到客户机物理地址(GPA)的第一阶段翻译(VS-stage)。
字段名 | 位宽 | 描述 |
---|---|---|
MODE | 4 | 页表模式:
|
ASID | 16 | 地址空间标识符(Address Space ID),隔离不同客户机的地址空间 |
PPN | 44 | 物理页号(Physical Page Number),指向VS-stage页表的根页表地址 |
RESERVED | 8 | 保留位,必须写0 |
G | 1 | 全局映射标志(Global bit),若为1则忽略ASID匹配 |
2. SVAPT (Supervisor Virtual Address Protection and Translation)
作用 :在Hypervisor模式下管理宿主机虚拟地址(HVA)到宿主机物理地址(HPA)的翻译(HS-stage)。
字段名 | 位宽 | 描述 |
---|---|---|
MODE | 4 | 页表模式:
|
ASID | 16 | 宿主机地址空间标识符 |
PPN | 44 | 指向HS-stage页表的根页表地址 |
V | 1 | 虚拟化启用标志:
|
RESERVED | 7 | 保留位 |
3. HGAPT (Hypervisor Guest Address Protection and Translation)
作用 :控制客户机物理地址(GPA)到宿主机物理地址(HPA)的第二阶段翻译(G-stage)。
字段名 | 位宽 | 描述 |
---|---|---|
MODE | 4 | G-stage页表模式:
|
VMID | 14 | 虚拟机标识符(Virtual Machine ID),隔离不同虚拟机的G-stage页表 |
PPN | 44 | 指向G-stage页表的根页表地址 |
GST | 1 | 客户机软件TLB失效指令使能:
|
RESERVED | 5 | 保留位 |
关键差异总结
寄存器 | 控制阶段 | 核心功能 | 特权级 |
---|---|---|---|
VAPT | VS-stage | GVA→GPA翻译(客户机视角) | VS-mode |
SVAPT | HS-stage | HVA→HPA翻译(宿主机视角) | HS-mode |
HGAPT | G-stage | GPA→HPA翻译(硬件辅助虚拟化) | M-mode/HS-mode |
- 特权级 :
- VS-stage :由Hypervisor管理,处理客户机虚拟地址
- G-stage :由客户机OS管理,处理客户机物理地址
翻译过程
第一阶段:VS-stage (GVA → GPA)
VPN部分 | VS-stage页表 | 依赖的G-stage页表 | 说明 |
---|---|---|---|
VPN[3] | VS-L3 | 通过hgatp访问G-L3 | 最高级页表,需G-stage辅助查询 |
VPN[2] | VS-L2 | 通过hgatp访问G-L2/G-L1/G-L0 | 中间级页表,需G-stage多级支持 |
VPN[1] | VS-L1 | 通过hgatp访问G-L1/G-L0 | 次末级页表 |
VPN[0] | VS-L0 | 通过hgatp访问G-L0 | 最后级页表,直接指向GPA |
第二阶段:G-stage (GPA → HPA)
页表层级 | 作用 |
---|---|
G-L3 | 顶级页表,由hgatp寄存器指向 |
G-L2 | 中间级页表 |
G-L1 | 次末级页表 |
G-L0 | 最后级页表,与offset拼接生成HPA |
- 任一阶段页表访问失败会触发异常:
- VS-stage异常 → Hypervisor处理
- G-stage异常 → 客户机Page Fault
L2TLB table walker 查表流程说明:
- 请求首先进入PageCache查询第一阶段页表
- 若第一阶段命中:
- 直接由PageTableWalker处理第二阶段
- 若第一阶段未命中:
- 根据命中级别选择PageTableWalker或LastLevelPageTableWalker
- 第二阶段处理:
- 先在PageCache中查询
- 未命中时转交HypervisorPageTableWalker
- 翻译结果返回PageCache后完成流程
关键路径:
- 快路径:PageCache(阶段1)→PageTableWalker→PageCache(阶段2)
- 慢路径:PageCache(阶段1)→LastLevelPTW/PTW→PageCache(阶段2)→HypervisorPTW
MMU-L2TLB 地址翻译流程
1. L1TLB向L2TLB发送请求
-
非两阶段翻译请求:
- 首先访问PageCache。
- 若命中叶子节点,直接返回结果给L1TLB。
- 若未命中叶子节点:
- 根据PageCache命中的页表等级,结合PageTableWalker (PTW) 和 LastLevelPageTableWalker (LLPTW) 的空闲情况:
- 进入PTW、LLPTW 或 Miss Queue。
- 根据PageCache命中的页表等级,结合PageTableWalker (PTW) 和 LastLevelPageTableWalker (LLPTW) 的空闲情况:
-
两阶段地址翻译请求:
- PageCache每次只能处理一个查询请求。
- 首先查询第一阶段的页表:
- 若第一阶段命中,请求发送给PTW进行第二阶段的翻译。
- 若第一阶段未命中:
- 根据命中的页表级别,发送给PTW或LLPTW,在这两个模块中完成第二阶段的翻译。
-
page Cache 访问流程:
2. PTW和LLPTW的第二阶段翻译
-
PTW和LLPTW发送的第二阶段翻译请求会先发送到PageCache查询:
- 若命中,PageCache直接返回结果给对应模块(PTW或LLPTW)。
- 若未命中,发送给HypervisorPageTableWalker (HPTW) 进行翻译,结果直接返回给PTW或LLPTW
3. PageTableWalker (PTW) 处理
-
PTW同时只能处理一个请求,进行HardwarePageTableWalk:
- 访问内存中前两级页表(不访问4KB页表)。
- 可能的结果:
- 访问到2MB或1GB的叶子节点。
- 发生Pagefault或Access fault。
- 以上情况直接返回给L1TLB。
- 否则,请求送往LLPTW访问最后一级(4KB)页表。
PTW 处理流程
4. HypervisorPageTableWalker (HPTW) 处理
- HPTW每次只能处理一个请求:
- LLPTW中第二阶段翻译的请求串行发送给HPTW。
- HPTW访问可能触发Pagefault或Access fault,返回给PTW或LLPTW,最终返回给L1TLB。
5. 内存访问流程
- PTW、LLPTW、HPTW均可向内存发送请求访问页表内容。
- 物理地址访问内存前需通过PMP和PMA模块检查:
- 若发生Access fault,不向内存发送请求。
- 请求经过仲裁后,通过TileLink总线向L2Cache发送:
- L2Cache访存宽度为512bits,每次返回8项页表。
6. 页表压缩机制
- L2TLB:
- 命中4KB页时,返回至多8项连续的页表项(虚拟页号高位相同且物理页号高位和属性相同)。
- 在H拓展中,与虚拟化相关的页表仍采用压缩机制。
- L1TLB:
- 在H拓展中,与虚拟化相关的页表压缩机制无效,视为单个页表。
7.异常处理
- 各级Walker(PTW、LLPTW、HPTW)访问中可能触发:
- Pagefault或Access fault,逐级返回至L1TLB。
8. 关键限制
- PTW和HPTW均单请求处理,串行化。
- PageCache单查询请求处理,两阶段翻译需分步完成。
2.2.4 - Shield-Bitmap 地址翻译2
加入了Shield-XS安全检查后的内存地址转换流程
如前文所述, bitmap和MMU耦合。现在将介绍增加了bitmap后的MMU地址翻译流程。
从 客户虚拟地址 到 主机物理地址 的转换过程,同时涉及 安全属性检查 和 Shield-Bitmap高速缓存访问。以下是详细步骤:
-
客户虚拟地址(Guest Virtual Address): 流程开始于计算单元(如CPU)使用客户虚拟地址发出内存访问请求。
-
访问一级快表(L1TLB Lookup): 首先查询一级快表(L1TLB),检查是否已缓存该地址的映射。只有经历过bitmap检查才会出现在L1TLB Cache中。因此和L2TLB Cache不同,L1TLB Cache 不会存储bitmap检查位。
-
一级快表缓存命中?
-
是 → 直接从L1TLB Cache获取主机物理地址(Host Physical Address)。
-
否 → 进入下一级查询(访问二级快表)。
-
-
访问二级快表(L2TLB Lookup): 如果一级快表未命中,继续查询二级快表(L2TLB),检查二级快表是否有对应的映射。L2TLB Cache 存储安全允许位(cf)表示 是否经过bitmap 检查。
-
二级快表缓存命中?
-
cf 和 L2TLB 页表项均命中 → 获取主机物理地址,并回填一级快表(更新L1TLB)。
-
L2TLB 页表项命中,bitmap cf 未设→ 发送请求至bitmap,进行权限检查。
-
否 → 触发 页表遍历(Page Table Walker),从内存中加载页表映射关系,随后进行bitmap检查。
-
-
-
进行页表遍历:从内存中加载页表映射关系
-
主机虚拟地址 → 主机物理地址转换:通过页表遍历获取主机物理地址(Host Physical Address)。
-
安全性检查允许访问(Security Check): 对物理地址进行 安全属性检查。如果检查失败,可能触发访问错误(Access Fault)。
-
访问(安全属性高速缓存)Shield-Bitmap Cache:查询Shield-Bitmap Cache是否已缓存目标数据。
-
Shield-Bitmap Cache 命中?
-
是 → 返回数据。
-
否 → 继续访问主存Shield-Bitmap 专属内存区域,读取权限。
-
-
-
访存请求合并(Shield-Bitmap Memory Request Merging):如果多个请求访问同一地址,可能合并访存请求以提高效率。
-
访问错误:如果访问不被允许,触发访问错误。
异常处理描述
RISC-V 特权手册规定的同步异常处理优先级
优先级处理如下:
序号 | 描述 | 触发异常 |
---|---|---|
1 | PTE.V == Invalid | Page fault |
2 | PTE.V == Invalid && Page Table Walker (PMP || Bitmap) Check Fail |
Access Fault |
3 | PTE.V valid && PTE.Permission (R/W/X) Check Fail | Page fault |
4 | PTE.V valid && PMP Check Fail | Access Fault |
2.3 - Shield-XS Bitmap 硬件设计
Shield-XS Bitmap 硬件设计
在硬件实现层面,Bitmap 机制由两个关键组件构成,即 Bitmap Checker 和 Bitmap Cache。其中,Checker 的职责是读取内存中的权限信息,以确保内存访问的安全性;而 Cache 则旨在加速查找过程,提升整体性能。需要指出的是,当前实现仅支持单向隔离功能。 这意味着在实际应用场景中,它能够有效地防止非安全敏感型负载对安全内存区域的非法访问,但尚未支持更高阶的双向隔离功能,即安全与非安全负载之间的互相访问限制。
Shield-XS Bitmap 硬件示意图
上图展示了一次虚拟地址到物理地址转换过程中如何结合Shield-Bitmap安全机制进行访问权限检查。 以及bitmap cache hit 和miss的不同处理。在L1TLB hit时,无需进行bitmap检查,因为L1TLB只会存储bitmap 检查为 allow的项。 如果miss,在L2TLB的page cache中查找,如果页表项和对应的bitmap 均未命中,则先进行查表,后进行bitmap检查并返回结果。如果页表项命中但未进行过bitmap 检查,则只进行bitmap检查。 如果都命中,则直接返回。
2.3.1 - Bitmap Checker
Bitmap Checker硬件模块
Bitmap checker简介
Bitmap checker 的作用是将来自外部(ptw/lptw/hptw)的请求发送至cache,并根据是否命中进行内存访问查权限。最后将cache返回的或者内存访问得到的权限发送回请求源。
此外,bitmap(walker)支持non blocking 特性,每一个请求来源都有FSM负责录入请求进行处理。但是一次只能有一个fsm进行cache访问。
状态机描述
为了保持non blocking,有8个独立的状态机(entries)并行运行。每个 entry 维护独立的状态和数据处理。当有请求进入时,从下到上依次将fsm填满,由于总共就8个请求来源,因此不会出现无空闲fsm可用的情况。
当entry的PA重复时,仅有一个fsm会进行一次查cache 或者访问memory,其余重复fsm项的状态会被部分跳过。重复表示PA的tag位[47:18] 一致。
PA | |||
---|---|---|---|
段 | tag | Bitmap offset | Page offset |
位 | [47:18] | [17:12] | [11:0] |
Bitmap checker 模块状态机
状态机状态
状态 | 说明 |
---|---|
state_idle | 标明该fsm状态为空,可以录入新请求 当io.req.fire时,切换到新状态 转换条件:
|
state_addr_check | 进行pmp检查 转换条件:
|
state_cache_req | 将cachereq拉高,fire后→ state_cache_resp |
state_cache_resp | Cache resp fire后更新:
|
state_mem_req | 拉高valid 并等待,mem req fire时,将所有重复项目的id跟新为本fsm id,并将所有重复和本机 state 设置为mem wait |
state_mem_waiting | Fire时→state_mem_out,并将所有的符合id项目内值全部跟新为mem返回值 |
state_mem_out | 拉高 resp valid ,fire时→ state_idle |
接口信号
信号 | 位宽 | 描述 |
---|---|---|
Io.mem | 内存访问相关信号 | |
io.mem.resp.bits.id | 4 | memory 响应返回的 ID(需为bitmap编号) |
io.mem.resp.bits.value | 512 | memory 返回的 bitmap 数据块 |
io.mem.req_mask | 20 | Memory 请求屏蔽位 |
io.mem.req.bits.addr | 56 | memory 请求的 bitmap 数据地址 |
io.mem.req.bits.id | 4 | memory 请求的编号(恒定为bitmap编号) |
io.mem.req.bits.hptw_bypassed | 1 | (和bitmap 模块内部无关) |
Io.Req | 请求信号 | |
io.req.bits.bmppn | 27 | 被检查的物理页号 PPN |
io.req.bits.id | 4 | 请求编号,用于标识请求来源(和bitmap 模块内部无关) |
io.req.bits.vpn | 27 | 对应虚拟页号VPN,用于唤醒pagecache(和bitmap 模块内部无关) |
io.req.bits.level | 2 | 所查询页表的级别信息(0/1/2),用于唤醒pagecache(和bitmap 模块内部无关) |
io.req.bits.way_info | 8 | TLB way 编号用于唤醒pagecache(和bitmap 模块内部无关) |
io.req.bits.hptw_bypassed | 1 | 用于唤醒pagecache(和bitmap 模块内部无关) |
Io.resp | 返回结果 | |
io.resp.bits.cf | 1 | 检查权限是否允许访问 |
io.resp.bits.cfs | 8 | 相邻8个(3bit地址空间)的权限 |
io.resp.bits.id | 4 | 响应对应的请求id(和bitmap 模块内部无关) |
Io.pmp | Pmp查 | |
io.pmp.req.bits.addr | 56 | 进行PMP检查的物理地址 |
io.pmp.req.bits.cmd | 2 | 读/写权限请求类型(恒定为读) |
io.pmp.req.bits.size | 3 | 请求访问大小(恒定) |
io.pmp.resp.ld | 1 | PMP Load 权限检查结果 |
io.pmp.resp.mmio | 1 | PMP MMIO 检查结果 |
Io.wakeup | Resp时且非hptw bypassed 进行重填pagecache | |
io.wakeup.bits.setIndex | 4 | 唤醒用的setIndex(和bitmap 模块内部无关) |
io.wakeup.bits.tag | 4 | 唤醒tag(VPN高位)(和bitmap 模块内部无关) |
io.wakeup.bits.isSp | 1 | 是否为superpage(和bitmap 模块内部无关) |
io.wakeup.bits.way_info | 8 | TLB对应的way 信息(和bitmap 模块内部无关) |
io.wakeup.bits.pte_index | 6 | PTE 在段页表中的索引位置(和bitmap 模块内部无关) |
io.wakeup.bits.check_success | 1 | 是否 bitmap 检查通过 |
Refill | ||
io.refill.bits.data | 64 | 要写入cache 的bitmap 数据 |
CSR | ||
io_sfence_valid | 1 | SFENCE 操作有效信号(为高刷新fsm) |
io_csr_satp_changed | 1 | SATP 寄存器变更标志(为高刷新fsm) |
io_csr_vsatp_changed | 1 | VSATP 寄存器变更标志(为高刷新fsm) |
io_csr_hgatp_changed | 1 | HGATP 寄存器变更标志(为高刷新fsm) |
io_csr_mbmc_BMA | 58 | Bitmap 基址寄存器值 |
2.3.2 - Bitmap Cache
Bitmap Cache硬件模块
Bitmap cache简介
Bitmap cache用于缓存 bitmap 数据块以减少 memory 访问延迟,存储最近访问的 bitmap 数据,共16个entry。每个 entry 存储一个 64-bit 数据段。使用plru替换策略。
Bitmap模块结构
Cache 一回合出结果,不需要pipeline。此外,refill也只需要一回合。Refill使用plru进行充填。
Bitmap cache接口
io_req | 位宽 | Bm 发起请求 |
---|---|---|
io_req_bits_tag | 36 | Tag for cache lookup ([35:6] = tag) |
io_req_bits_order | 8 | 发起请求的Fsm编号 |
Io resp | 返回bm请求 | |
io_resp_bits_hit | 1 | 是否hit cache |
io_resp_bits_order | 8 | 发起请求的Fsm编号 |
io_resp_bits_cfs | 8 | 相邻8个的权限 |
Io refill | Refill接口,来自bm,bm resp valid时发起重填 | |
io_refill_bits_tag | 36 | Tag for cache refill ([35:6] = tag) |
io_refill_bits_data | 64 | Data to refill into cache |
io_resp_bits_hit | 1 | 是否hit cache |
CSR | ||
io_sfence_valid | 1 | 同步刷新请求有效(触发缓存刷新) |
io_csr_satp_changed | 1 | SATP CSR 变更标志(触发缓存刷新) |
io_csr_vsatp_changed | 1 | VSATP CSR 变更标志(触发缓存刷新) |
io_csr_hgatp_changed | 1 | HGATP CSR 变更标志(触发缓存刷新) |
io_csr_mbmc_BCLEAR | 1 | 缓存清除信号(触发缓存刷新) |
2.3.3 - Bitmap 与L2TLB交互
Bitmap 与L2TLB内的交互
Page Cache 与 Bitmap 检测机制的交互
新增信号
发向 PTW 的 bitmap check 信号:用于触发 PTW 进行 bitmap 检测。
发向 HPTW 的 bitmap check 信号:用于触发 HPTW 进行 bitmap 检测。
接收来自 bitmap 的重填信号(bitmap wakeup):用于接收 bitmap 检测结果并更新缓存。
Bitmap Wakeup接口: refill bitmap
功能描述:当接收到 wake up valid 信号时,将 check_success 结果写入对应的 sp 或 l0 的 cache bitmap reg 中。
工作原理:cache bitmap reg 用于标识缓存项是否通过 bitmap 检测。值为 1 表示已通过检测;值为 0 表示检测未通过或尚未检测。如果发现 PtwCache 命中的表项未通过检测,则触发 Bitmap 检测流程,并通过 bitmap wakeup 更新缓存项。此外,在走表过程中所有bitmap返回的项都会回填page cache。

Ptw/llptw接口:refill data 后第一次伪hit发起bitmap请求
功能描述:当缓存命中且 bitmap valid = 0 时,首次命中不直接返回 L1TLB,而是返回响应请求源并发起 bitmap 请求。
工作原理:使用 is_hptw 判断请求源。请求源在获取 bitmap 权限后,将结果重新填充到 Page Cache 中。
Page Table walker 交互
状态机更新
新增状态:PTW、LLPTW 和 HPTW 的状态机中新增了 state_bitmap_check。
工作流程:在 PTW、LLPTW 和 HPTW 的状态机中,于 state_mem_resp 阶段进行 bitmap 检测,并将 bitmap 检测的使能信号传递给这些部件。如果满足 bitmap 检测条件,则进入 state_bitmap_check 并获取检测结果。如果检测失败,则触发访问故障(Access Fault)并将结果返回。
触发条件
PTW:仅在未开启虚拟化且检测到巨页(hugepage)时进行 bitmap 检测。
LLPTW:仅在请求未开启虚拟化(即进行 VA 到 PA 的地址转换时)进行 bitmap 检测。如果请求通过 HPTW,则 HPTW 已在工作过程中进行了 bitmap 检测。
HPTW:在遍历到最后一级页表时,于 mem_resp 阶段进行 bitmap 检测。
新接口
Req_bitmapcheck 接口:用于在 Page Cache 首次命中时发起 bitmap 检测。仅在 PTW 和 LLPTW 上实现。如果有效,则直接接收一个 PTE 并检查权限。状态机直接跳转到 state_bitmap_check,获取权限后直接返回 pagecache。
Bitmap 接口:用于在 state_bitmap_check 阶段发送 bitmap 请求,并检查权限是否通过。如果检测失败,则触发访问故障。HPTW 和 LLPTW 均具备此接口。
刷新
bitmap 依赖软件辅助刷新,硬件刷新不完整。在刷新前,需依次sfence 和 hfence L1 和L2TLB内所有项目,然后才可以拉高 CSR_MBMC_BCLEAR 进行bitmap cache刷新。
2.3.4 - Bitmap接口时序图
Bitmap接口时序图
信号 | 描述 |
---|---|
io_req_ready | 8个fsm中有至少一个idle时为高,可以视为常态高 |
io_req_valid | 新请求进入时高,平时为低 |
io_resp_ready | 当请求源(ptw hptw llptw)发送请求,等待返回时会拉高,平时无请求时为低 |
io_resp_valid | 当返回查询结果时拉高,平时为低 |
io_mem_req_ready | 有其它mem请求时(ptw llptw hptw)为低,平时为高 |
io_mem_req_valid | cache miss时发起mem请求拉高,平时为低 |
io_mem_resp_valid | mem 返回结果拉高,平时为低 |
io_cache_req_valid | bimap fsm 发起 cache 请求拉高,平时为低 |
io_cache_req_ready | 常态高 |
io_cache_resp_valid | io_cache_req_valid下一clk 拉高平时低 |
io_cache_resp_ready | io_cache_req_valid 下一clk 拉高平时低 |
2.3.5 - 开销评估
开销评估
1. 基本配置
类别 | 配置项 | 参数**/**设置 |
---|---|---|
Shield-Bit 配置 | 有效 Shield-XS 隔离模型 | - |
设置 Shield-Bitmap | _ | |
Shield-Bitmap缓存大小 | 128 × 8 Bytes | |
KunminghuV2 配置 | TileLink Prototype | - |
缓存层级配置 | L1 指令/数据缓存大小 | 64KB |
L1 指令/数据 TLB | 48-全关联(Full Association) | |
L2 缓存大小 | 1MB | |
L3 缓存大小 | 16MB |
2. SPEC2006 性能数据
SPECInt2006 Simpoint est.@3GHz GEOMEAN 44.62 -> 44.29 (0.72% )
图 9.1 SPEC2006 性能开销
性能开销与DTLB Miss-rate 呈正比。有效的减少 DTLB 和 Shield-bitmap Cache 的miss-rate, 可以进一步提升性能。例如将缓存从 16 项扩展到 128 项,可使 GemsFDTD 的性能开销从 6.51% 降低至 2.36%。
3. 硬件开销
采用7纳米工艺制程,硬件面积开销仅为0.2%。
工艺 | 子模块前 (单位: μm2) | 子模块后 (单位: μm2) | 百分比 |
---|---|---|---|
T7 | Memblock.withoutBitmap:462415.887238 | Memblock.withBitmap:471410.993566 | +1.94524% |
T7 | L2TLB.withoutBitmap: 41538.554989 | L2TLB.withBitmap : 50843.978450 | +22.4% |
时序违例
模块路径 | clock period | clock uncertainty | data arrival time | setup time | slack |
---|---|---|---|---|---|
bitmap FSM -> bitmap Cache Data Reg | 0.333 ns | 0.1 ns | 0.2724 ns | 0.0107 ns | -0.0501 ns |
2.4 - Shield-XS_Bitmap 单元验证
本文档将会对需要验证的点进行说明
2.4.1 - 单元验证
单元验证
TLB 相关功能验证
验证功能编号 | 验证描述 | 覆盖方法 | 排列组合示例 |
---|---|---|---|
TLB_001 | ITLB hit/miss 场景验证 | 设计测试用例覆盖 ITLB 命中和未命中 | 4K/2M/1G/512G 页大小 |
TLB_002 | DTLB hit/miss 场景验证 | 设计测试用例覆盖 DTLB 命中和未命中 | 4K/2M/1G/512G 页大小 |
TLB_003 | L2TLB hit/miss 场景验证 | 设计测试用例覆盖 L2TLB 命中和替换 | 4K/2M/1G/512G 页大小 |
TLB_004 | L2TLB 压缩功能验证 注:仅支持一阶段地址转换压缩,最多压缩8项 |
测试TLB 压缩场景下,Bitmap 查询结果是否正确 | TLB 压缩启用 + 4K 页大小 |
Bitmap Cache 相关功能验证
验证功能编号 | 验证描述 | 覆盖方法 | 排列组合示例 |
---|---|---|---|
BITMAP_001 | Bitmap Cache hit/miss 场景验证 | 设计测试用例覆盖命中和未命中 | Bitmap Y/N + 跨bitmap cache line |
BITMAP_002 | Bitmap check 功能验证 | 验证 bitmap check 的正确性 | Bitmap check 启用 + 4K 页大小 |
BITMAP_003 | 跨bitmap cache line 场景验证 | 测试跨 cache line 的访问行为 | 跨bitmap cache line + 2M 页大小 |
BITMAP_004 | NAPOT 拓展开启,验证Bitmap 的行为 | 开启NAPOT,设置PTE.n 位,验证Bitmap 检测流程 | 跨bitmap cache line + 64K 页大小切换PTE.n 位 |
Bitmap Cache 和 TLB 组合相关功能验证
验证功能编号 | 验证描述 | 覆盖方法 | 排列组合示例 |
---|---|---|---|
TLB_BITMAP_001 | Bitmap与TLB混合命中场景验证 | 组合Bitmap命中与各级TLB命中,验证多级缓存协同 | Bitmap hit + ITLB/DTLB/L2TLB全命中 |
TLB_BITMAP_002 | 全未命中极端场景验证 | 设计冷启动或冲刷缓存后首次访问的全未命中用例 | Bitmap miss + ITLB/DTLB/L2TLB全miss + 触发页表遍历 |
TLB_BITMAP_003 | Bitmap跨行与TLB替换场景验证 | 强制Bitmap跨cache line访问并触发TLB替换(如duplicate access或页表更新) | Bitmap跨行 + DTLB miss + L2TLB替换 |
TLB_BITMAP_004 | 重复访问与Cache/TLB交互验证 | 通过相同地址重复访问验证Bitmap/TLB的重复访问优化 | Bitmap重复命中 + ITLB重复命中 + 无替换 Bitmap重复Miss + ITLB重复Miss + 替换 |
页表遍历(PTW)相关功能验证
验证功能编号 | 验证描述 | 覆盖方法 | 排列组合示例 |
---|---|---|---|
PTW_001 | PTW/HPTW/LLPTW 优先级验证 | 测试多级页表遍历的优先级 | PTW + HPTW 并发 |
PTW_002 | PTW 返回异常(access fault)验证 | 模拟 PTW 返回异常的场景 | PTW return af + 跨Page |
PTW_003 | PTE 合法性验证 | 测试非法 PTE 的处理 | 非法 PTE + PMP check |
异常与重放(Replay)功能验证
验证功能编号 | 验证描述 | 覆盖方法 | 排列组合示例 |
---|---|---|---|
REPLAY_001 | 标量 replay 场景验证 | 测试标量指令重放行为 | 标量 replay + access fault |
REPLAY_002 | 向量访存 replay 场景验证 | 测试向量指令重放行为 | 向量 replay + 跨MMO |
EXCEPT_001 | 异常优先级验证(access/page fault/guest page fault) | 验证异常触发的优先级 | page fault/guest page fault page table walker 过程中的PMP/bitmap 检测失败 转换之后的物理地址PMP 检测失败 |
特权级与扩展功能验证
验证功能编号 | 验证描述 | 覆盖方法 | 排列组合示例 |
---|---|---|---|
PRIV_001 | U/S/M 特权级切换验证 | 测试不同特权级下的访问权限 | U/S/M + 4K/2M 页大小 |
EXT_001 | H-extension 功能验证 | 测试 H-extension 启用和禁用场景 | H-extension + 跨tlb entry |
PMP_001 | PMP check 功能验证 | 测试bitmap 内PMP 权限检查 | PMP Y/N + 跨Page |
Fense验证
验证功能编号 | 验证描述 | 覆盖方法 | 排列组合示例 |
---|---|---|---|
MISC_001 | Fence 操作验证 | 测试 fence 指令的同步效果 | Fence + 切换Cmode |
验证方法说明
覆盖方法:通过随机测试、定向测试和边界值测试覆盖功能点。
排列组合:优先覆盖高频场景,再逐步覆盖低频组合(如 512G 页)。
备注
-
需根据实际硬件行为调整测试用例的输入和预期输出。
-
动态检查(如 PMP check)需结合具体权限配置。