1. LSU介绍
LSU(Load Store Unit)是一个专门的执行单元,负责执行所有的加载(load)和存储(store)指令等,生成load和store操作的虚拟地址,并从内存中加载数据或将数据从寄存器中存储回内存。LSU里一般包括L1 D-cache、D-TLB、AGU、load queue、store queue等模块。
AGU(Address Generate Unit)用来计算地址,访问内存的指令(load/store等) 通常会在指令中携带内存地址,AGU负责计算出指令中携带的地址,计算得到的地址是虚拟地址,还需要访问D-TLB等获取物理地址。
AGU完成了地址转换的一小部分,真正的重头戏是从虚拟地址转为物理地址,以及从物理地址得到数据的过程。这个过程直接决定了处理器的性能,尤其是load/store指令也采用乱序执行的处理器,需要复杂的硬件逻辑来检测和处理各种违例情况,例如store/load违例或者load/load违例等。
2. 香山处理器的LSU
以开源的香山处理器为例,其访存单元的架构如下图:
香山处理器(南湖架构)核内的访存单元包含两条load流水线,彼此分离的两条sta流水线和两条std流水线。load queue和store queue负责维护访存指令的顺序信息。 load queue会在 load 指令在一级缓存中缺失时负责监听后续的重填结果并执行写回操作。store queue负责在指令提交之前暂存store 的数据,并为store向load的前递提供数据。
在store指令提交之后, store queue 会将其中的数据搬运到committed store buffer. committed store buffer 会以缓存行为单位对 store 的写请求进行合并,在接近满的时候将合并后的多个 store 写请求一并写入到L1 D-cache。
一级数据缓存对核内访存组件暴露两个位宽为64的读端口和一个与一级数据缓存行宽度相同的写端口, 以及一个数据重填端口。数据重填端口的宽度数据缓存和 L2 cache之间的总线宽度决定。
MMU负责将虚拟地址翻译成物理地址,然后用这个物理地址去访存。同时也会进行权限检查,比如是否可写,可执行。香山的MMU包含TLB、L2 TLB、Repeater、PMP & PMA等组件。
2.1 load pipeline
香山处理器(雁栖湖架构)包含两条load流水线,每条load流水线分成3级流水:
Stage 0:
指令和操作数被从保留站读出
加法器将操作数与立即数相加,计算虚拟地址
虚拟地址送入TLB进行TLB查询
虚拟地址送入数据缓存进行Tag索引
Stage 1:
TLB 产生物理地址
完成快速异常检查
虚拟地址进行 Data 索引
物理地址送进数据缓存进行 Tag 比较
虚拟/物理地址送进store queue/ committed store buffer开始进行 store 到 load 的前递操作
根据一级数据缓存返回的命中向量以及初步异常判断的结果, 产生提前唤醒信号送给保留站
如果在这一级就出现了会导致指令从保留站重发的事件, 通知保留站这条指令需要重发
Stage 2:
完成异常检查
根据一级数据缓存及前递返回的结果选择数据
根据 load 指令的要求, 对返回的结果做裁剪操作
更新 load queue 中对应项的状态
结果 (整数) 写回到公共数据总线
结果 (浮点) 送到浮点模块
Stage 3:
根据 dcache 的反馈结果, 更新 load queue 中对应项的状态
根据 dcache 的反馈结果, 反馈到保留站, 通知保留站这条指令是否需要重发
Load Miss
一条 miss 的 load 指令会执行以下操作来取得其所需的数据, 这一节将逐个介绍这些机制:
禁用当前指令的提前唤醒
更新 load queue 的状态
分配 dcache MSHR (MissQueue entry)
侦听 dcache refill
写回 miss 的 load 指令
在load stage 1, 根据 dcache tag 比较结果, load 流水线可以得知当前指令是否 miss. 在发生 miss 时, 这条指令的提前唤醒有效位会被设置成false, 以禁用当前指令的提前唤醒。
在 load stage 2, 如果发现 miss, load 流水线不会写回结果到寄存器堆, 不占用 load 指令写回端口. 同时更新 load queue 的状态, 这条发生 miss 的 load 指令此后会在在 load queue 中等待 dcache refill. 与此同时, dcache 会尝试为这条 miss 的 load 指令分配 dcache MSHR (MissQueue entry). 由于分配逻辑复杂, 分配的结果要在下一拍才能反馈到 load queue。
在 load stage 3, 根据 dcache MSHR 分配的结果再次更新 load queue 的状态. 如果 dcache MSHR 分配失败, 则请求保留站重发这条指令。
若这条指令成功被分配 dcache MSHR,后续其将在 load queue 中侦听 dcache refill 的结果。
Replay From RS
load流水线是非阻塞的, 即无论出现任何异常情况, 都不会影响流水线中有效指令的流动. 而在除 load miss 之外的异常情况发生导致 load 无法正常执行完时, load 流水线会利用从保留站重发(Replay From RS)机制来重新执行这条指令. 下面逐个介绍会触发从保留站重发机制的事件:
TLB miss
TLB miss事件会通过使用feedbackSlow 端口请求从保留站重发. TLB miss 的指令在重发时存在重发延迟, 在指令在保留站中等待到延迟结束后才被重发. 重发延迟的存在是因为 TLB 重填需要时间, 在 TLB 重填完成之前重发指令还会产生 TLB miss, 是没有意义的。
预取指令的处理
目前, 软件预取指令使用与 load 指令类似的处理流程, 软件预取指令会与正常的 load 指令一样进入 load 流水线, 在发现 miss 时向 dcache 的 MissQueue 发出请求, 触发对下层 cache 的访问. 特殊地, 软件预取指令执行期间会屏蔽所有例外, 且不会重发。
Load 的提前唤醒
南湖的保留站支持提前唤醒机制来尽快调度后续的指令. 但是, 南湖架构暂时不支持推测唤醒机制. 被提前唤醒的指令必须要能正常地执行, 否则就需要冲刷整个流水线. 如果一条 load 指令正常执行但没有发出提前唤醒信号, 则会导致依赖这条 load 的后续指令晚一个周期才能被发射, 造成少许的性能损失.
load 流水线会在 load stage 1 向保留站给出快速唤醒信号. 由于 MemBlock 和 IntBlock 之间的线延迟, 这一信号处于关键路径上. 在提前唤醒信号的产生, load 流水线会进行指令能否正常执行的粗略判断. 一旦指令有不能正常执行的迹象, 就不进行提前唤醒.
在极端情况下, load 指令会错误的发出提前唤醒, 此时需要冲刷整个流水线. load 指令错误的发出提前唤醒的条件为:load 指令在 load stage 1 之后发生异常 (来源于数据错误/总线错误).
Forward Failure
这一小节介绍南湖架构在虚地址前递失败时的处理. 当 store queue 或者 store buffer 反馈虚地址前递失败时, load 流水线会在 stage 2 将这条指令附加上 replayInst (需要从取指重发) 的标签. 在这条指令到达 ROB 队尾时触发重定向. 由于虚地址前递失败是很罕见的现象, 这样的重定向不会对性能产生过大影响.
2.2 store pipeline
香山处理器(南湖架构)采用了store 地址与数据分离的执行方式。store的地址与数据在就绪时可以分别从保留站中被发出,进入后续的处理流程。同一条 store 指令对应的数据/地址两个操作靠相同的 robIdx / sqIdx 联系在一起。
香山处理器包含两条store addr (sta) 流水线,每条store addr流水线分成4级。前两个流水级负责将 store的控制信息和地址传递给 store queue,后两个流水级负责等待访存依赖检查完成.
香山处理器(南湖架构)包含两条 store data (std) 流水线,在保留站提供 store data 后, store data 流水线会立刻将 data 写入 store queue 的对应项中。
Sta Pipeline:
Stage 0
计算虚拟地址
虚拟地址送入D-TLB
Stage 1
D-TLB产生物理地址
完成快速异常检查
开始进行访存依赖检查
物理地址送入store queue
Stage 2
访存依赖检查
完成异常检查和PMA查询, 更新store queue
Stage 3
完成访存依赖检查
通知 ROB 可以提交指令
Std Pipeline:
Stage 0
保留站给出 store data
将 store data 写入 store queue
TLB miss 的处理
和load 流水线一样, store addr 流水线也可能经历TLB miss. 两者的处理方式基本一致, 参见 load TLB miss 的处理. store addr 仅使用一个 rsFeedback 端口向保留站反馈 store addr 计算操作是否需要从保留站重发。
2959