ICache verify enviroment and docs added. (#111)
* Add icache compile scripts, waylookup enviroment and revise dir_map.f * Revise icache top compile script and dir_map.f for normal compiling * Revise icache ctrlunit script and add ctrlunit test enviroment * Delete unused script * Add internal signal yaml and revise its responding code in compile script and iprefetchpipe test environment * Revise internal signals yaml and its corresponding content in script * Add mainpipe test environment * Revise internal signals yaml and its corresponding content in script * Add missunit test environment * Add task.get_name * Revise internal signals yaml and its corresponding content in script * Revise dir_map for right mapping * Add icache top test environment * Add icache docs and its pics * Add icache pic and interfaces * Add icache _index.md * Add icache pics * Add icache submodule docs * delete copy pic
|
|
@ -12,4 +12,10 @@ frontend_itlb --> TLB --> ut_frontend/itlb/toffee_version
|
|||
frontend_tlb_fa --> TLBFA --> ut_frontend/itlb/submodules/TLBFA
|
||||
frontend_tlb_nonblock --> TLBNonBlock --> ut_frontend/itlb/submodules/TLBNonBlock
|
||||
frontend_tlb_storage_wrapper --> TlbStorageWrapper --> ut_frontend/itlb/submodules/TlbStorageWrapper
|
||||
frontend_tlbuffer --> TLBuffer --> ut_frontend/itlb/submodules/TLBuffer
|
||||
frontend_tlbuffer --> TLBuffer --> ut_frontend/itlb/submodules/TLBuffer
|
||||
frontend_icache_ctrlunit --> CtrlUnit --> ut_frontend/icache/ctrlunit
|
||||
frontend_icache_iprefetchpipe --> IPrefetchPipe --> ut_frontend/icache/iprefechpipe
|
||||
frontend_icache_icache --> ICache --> ut_frontend/icache/icache
|
||||
frontend_icache_mainpipe --> ICacheMainPipe --> ut_frontend/icache/mainpipe
|
||||
frontend_icache_missunit --> ICacheMissUnit --> ut_frontend/icache/missunit
|
||||
frontend_icache_waylookup --> WayLookup --> ut_frontend/icache/waylookup
|
||||
|
|
@ -0,0 +1,424 @@
|
|||
---
|
||||
title: IPrefetchPipe
|
||||
linkTitle: IPrefetchPipe
|
||||
weight: 12
|
||||
---
|
||||
|
||||
<div class="icache-ctx">
|
||||
|
||||
</div>
|
||||
|
||||
## IPrefetchPipe
|
||||
|
||||
IPrefetchPipe 为预取的流水线,三级流水设计,负责预取请求的过滤。
|
||||
|
||||
<div>
|
||||
<center>
|
||||
<img src="../iprefetchpipe_structure.png"
|
||||
alt="IPrefetchPipe模块结构示意图"
|
||||
style="zoom:100%"/>
|
||||
<br>
|
||||
IPrefetchPipe结构示意图
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
1. 接收预取请求(s0 阶段):
|
||||
|
||||
- 从 FTQ 或后端接收预取请求。
|
||||
- 发送读请求到 ITLB 和 MetaArray 缓存元数据模块。
|
||||
|
||||
2. 地址转换和缓存检查(s1 阶段):
|
||||
|
||||
- 接收 ITLB 的地址转换结果,处理可能的缺失和重发。
|
||||
- 从缓存元数据中读取标签和有效位,检查是否命中。
|
||||
- 进行 PMP 权限检查,合并异常信息。
|
||||
- 根据情况决定是否发送请求到 WayLookup 模块。
|
||||
|
||||
3. 未命中请求处理(s2 阶段):
|
||||
|
||||
- 检查与 missUnit 的交互,更新命中状态。
|
||||
- 对于无异常的未命中请求,向 missUnit 发送请求以获取数据。
|
||||
- 控制流水线的推进和刷新,处理可能的阻塞和异常。
|
||||
|
||||
### S0 流水级
|
||||
|
||||
在 S0 流水级,接收来自 FTQ 的预取请求,向 MetaArray 和 ITLB 发送请求。
|
||||
|
||||
- 接收预取请求:从 FTQ 或后端接收预取请求,提取预取请求的虚拟地址、FTQ 索引、是否为软件预取、是否跨缓存行信、虚拟组索引(s0_req_vSetIdx)和后端的异常信息。
|
||||
- 发送请求到 ITLB:将虚拟地址发送到 ITLB 进行地址转换。
|
||||
- 发送请求到缓存元数据(Meta SRAM):将请求发送到缓存的元数据存储器,以便在后续阶段读取缓存标签和有效位。
|
||||
|
||||
### S1 流水级
|
||||
|
||||
软件预取 enqway 持续一拍...
|
||||
|
||||
- 接收 ITLB 的响应:从 ITLB 接收地址转换的结果,包括物理地址 paddr、异常类型(`af`/`pf`)和特殊情况(`pbmt.nc`/`pbmt.io`)。
|
||||
- 接收缓存元数据的响应并检查缓存命中:从缓存元数据存储器 MetaArray 读取缓存标签 `tag` 和有效位,检查预取地址是否在缓存中已存在,命中结果存入 `waymask` 中。
|
||||
- 权限检查:使用 PMP 对物理地址进行权限检查,确保预取操作的合法性。
|
||||
- 异常处理和合并:合并来自后端、ITLB、PMP 的异常信息,准备在后续阶段处理。
|
||||
- 发送请求到 WayLookup 模块:当条件满足时,将元数据(命中信息 `waymask`、ITLB 信息 `paddr`/`af`/`pf`)发送到 WayLookup 模块,以便进行后续的缓存访问。
|
||||
- 状态机转换:根据当前状态和条件,更新下一个状态。
|
||||
- 状态机初始状态为 `idle`,当 S1 流水级进入新的请求时,首先判断 ITLB 是否缺失,如果缺失,就进入 `itlbResend`;如果 ITLB 命中但命中信息未入队 WayLookup,就进入 `enqWay`;如果 ITLB 命中且 WayLookup 入队但 S2 请求未处理完,就进入 `enterS2`。
|
||||
- 在 `itlbResend` 状态,重发 ITLB 请求,此时占用 ITLB 端口,直至请求回填完成,在回填完成的当拍向 MetaArray 再次发送读请求,回填期间可能发生新的写入,如果 MetaArray 繁忙(正在被写入),就进入`metaResend`,否则进入 `enqWay`。
|
||||
- 在 `metaResend` 状态,重发 MetaArray 读请求,发送成功后进入 `enqWay`。
|
||||
- 在 `enqWay` 状态,尝试将元数据入队 WayLookup,如果 WayLookup 队列已满,就阻塞至 WayLookup 入队成功,另外在 MSHR 发生新的写入时禁止入队,主要是为了防止写入的信息与命中信息所冲突,需要对命中信息进行更新。当成功入队 WayLookup 或者是软件预取时,如果 S2 空闲,就直接进入 `idle`,否则进入 `enterS2`。
|
||||
- 在 `enterS2` 状态,尝试将请求流入下一流水级,流入后进入 `idle`。
|
||||
|
||||
<div> <!--块级封装-->
|
||||
<center> <!--将图片和文字居中-->
|
||||
<img src="../iprefetchpipe_fsm.png"
|
||||
alt="IPrefetchPipe状态机"
|
||||
style="zoom:100%"/>
|
||||
<br> <!--换行-->
|
||||
IPrefetchPipe S1状态机 <!--标题-->
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
### S2 流水级
|
||||
|
||||
- 监控 missUnit 的请求:更新 MSHR 的匹配状态。综合该请求的命中结果、ITLB 异常、PMP 异常、meta 损坏,判断是否需要预取,只有不存在异常时才进行预取。
|
||||
- 发送请求到 missUnit:因为同一个预测块可能对应两个 cacheline,所以通过 Arbiter 依次将请求发送至 MissUnit。
|
||||
|
||||
### 命中信息的更新
|
||||
|
||||
在 S1 流水级中得到命中信息后,距离命中信息真正在 MainPipe 中被使用要经过两个阶段,分别是在 IPrefetchPipe 中等待入队 WayLookup 阶段和在 WayLookup 中等待出队阶段,在等待期间可能会发生 MSHR 对 Meta/DataArray 的更新,因此需要对 MSHR 的响应进行监听,分为两种情况:
|
||||
|
||||
1. 请求在 MetaArray 中未命中,监听到 MSHR 将该请求对应的 cacheline 写入了 SRAM,需要将命中信息更新为命中状态。
|
||||
2. 请求在 MetaArray 中已经命中,监听到同样的位置发生了其它 cacheline 的写入,原有数据被覆盖,需要将命中信息更新为缺失状态。
|
||||
|
||||
为了防止更新逻辑的延迟引入到 DataArray 的访问路径上,在 MSHR 发生新的写入时禁止入队 WayLookup,在下一拍入队。
|
||||
|
||||
### 刷新机制
|
||||
|
||||
在 IPrefetch 中如果收到后端重定向、IFU 预译码、fencei 带来的刷新,就冲刷整个流水线
|
||||
|
||||
IPrefetchPipe 模块中的刷新信号主要来自以下两个方面:
|
||||
|
||||
1. 全局刷新信号:由系统的其他模块发出的全局刷新信号,如怀疑流水线中存在错误数据或需要清除流水线时触发。
|
||||
|
||||
- io.flush:模块输入的全局刷新信号。当系统需要清除所有流水线阶段的数据时,该信号被置为高。
|
||||
|
||||
2. 来自分支预测单元(BPU)的刷新信号:当分支预测错误或需要更新预测信息时,BPU 会发出刷新信号。
|
||||
|
||||
- io.flushFromBpu:包含来自 BPU 的刷新信息,指示哪些指令需要被刷新。
|
||||
|
||||
## IPrefetchPipe 的功能点和测试点
|
||||
|
||||
### 接收预取请求
|
||||
|
||||
从 FTQ 接收预取请求,请求可能有效( io.req.valid 为高),可能无效; IPrefetchPipe 可能处于空闲( io.req.ready 为高),可能处于非空闲状态。
|
||||
只有在请求有效且 IPrefetchPipe 处于空闲状态时,预取请求才会被接收(这里暂不考虑 s0 的刷新信号 s0_flush ,默认其为低)。
|
||||
预取请求分为不同类型,包括硬件预取请求 (isSoftPrefetch = false)和软件预取请求 (isSoftPrefetch = true)。
|
||||
cacheline 也分为单 cacheline 和双 cacheline。
|
||||
|
||||
1. 硬件预取请求:
|
||||
预取请求为硬件 (isSoftPrefetch = false)
|
||||
|
||||
1. 预取请求可以继续:
|
||||
- 当预取请求有效且 IPrefetchPipe 处于空闲状态时,预取请求应该被接收。
|
||||
- s0_fire 信号在没有 s0 的刷新信号( s0_flush 为低)时,应该被置为高。
|
||||
2. 预取请求被拒绝--预取请求无效时:
|
||||
- 当预取请求无效时,预取请求应该被拒绝。
|
||||
- s0_fire 信号应该被置为低。
|
||||
3. 预取请求被拒绝--IPrefetchPipe 非空闲时:
|
||||
- 当 IPrefetchPipe 非空闲时,预取请求应该被拒绝。
|
||||
- s0_fire 信号应该被置为低。
|
||||
4. 预取请求被拒绝--预取请求无效且 IPrefetchPipe 非空闲时:
|
||||
- 当预取请求无效且 IPrefetchPipe 非空闲时,预取请求应该被拒绝。
|
||||
- s0_fire 信号应该被置为低。
|
||||
5. 预取请求有效且为单 cacheline 时:
|
||||
- 当预取请求有效且为单 cacheline 时,预取请求应该被接收。
|
||||
- s0_fire 为高,s0_doubleline 应该被置低(false)。
|
||||
6. 预取请求有效且为双 cacheline 时:
|
||||
- 当预取请求有效且为双 cacheline 时,预取请求应该被接收。
|
||||
- s0_fire 为高,s0_doubleline 应该被置高(true)。
|
||||
|
||||
2. 软件预取请求:
|
||||
预取请求为软件 (isSoftPrefetch = true)
|
||||
|
||||
1. 软件预取请求可以继续:
|
||||
- 当预取请求有效且 IPrefetchPipe 处于空闲状态时,软件预取请求应该被接收。
|
||||
- s0_fire 信号在没有 s0 的刷新信号( s0_flush 为低)时,应该被置为高。
|
||||
2. 软件预取请求被拒绝--预取请求无效时:
|
||||
- 当预取请求无效时,软件预取请求应该被拒绝。
|
||||
- s0_fire 信号应该被置为低。
|
||||
3. 软件预取请求被拒绝--IPrefetchPipe 非空闲时:
|
||||
- 当 IPrefetchPipe 非空闲时,软件预取请求应该被拒绝。
|
||||
- s0_fire 信号应该被置为低。
|
||||
4. 软件预取请求被拒绝--预取请求无效且 IPrefetchPipe 非空闲时:
|
||||
- 当预取请求无效且 IPrefetchPipe 非空闲时,软件预取请求应该被拒绝。
|
||||
- s0_fire 信号应该被置为低。
|
||||
5. 软件预取请求有效且为单 cacheline 时:
|
||||
- 当软件预取请求有效且为单 cacheline 时,软件预取请求应该被接收。
|
||||
- s0_fire 为高,s0_doubleline 应该被置低(false)。
|
||||
6. 软件预取请求有效且为双 cacheline 时:
|
||||
- 当软件预取请求有效且为双 cacheline 时,软件预取请求应该被接收。
|
||||
- s0_fire 为高,s0_doubleline 应该被置高(true)。
|
||||
|
||||
### 接收来自 ITLB 的响应并处理结果
|
||||
|
||||
接收 ITLB 的响应,完成虚拟地址到物理地址的转换。
|
||||
当 ITLB 发生缺失(miss)时,保存请求信息,等待 ITLB 完成后再继续处理。
|
||||
|
||||
1. 地址转换完成:
|
||||
|
||||
- 根据 ITLB 的响应,接收物理地址(paddr),并完成地址转换。
|
||||
- 处理 ITLB 响应可能在不同周期到达的情况,管理有效信号和数据保持机制,确保正确使用物理地址。
|
||||
|
||||
1. 当 ITLB 正常返回物理地址时:
|
||||
|
||||
- ITLB 在一个周期内成功返回物理地址 paddr,s1_valid 为高。
|
||||
- 确认 s1 阶段正确接收到 paddr。
|
||||
|
||||
2. 当 ITLB 发生 TLB 缺失,需要重试时:
|
||||
- fromITLB(PortNumber).bits.miss 为高,表示对应通道的 ITLB 发生了 TLB 缺失,需要重发。
|
||||
- 重发完成后,后续步骤继续进行,fromITLB(PortNumber).bits.miss 为低。
|
||||
|
||||
2. 处理 ITLB 异常:
|
||||
|
||||
- 根据 ITLB 的异常信息,处理可能的异常。pf 缺页、pgf 虚拟机缺页、af 访问错误。
|
||||
|
||||
1. 当 ITLB 发生页错误异常时:
|
||||
- s1_itlb_exception 返回的页错误。
|
||||
- iTLB 返回的物理地址有效(fromITLB(PortNumber).bits.miss 为低),s1_itlb_exception 指示页错误 pf。
|
||||
2. 当 ITLB 发生虚拟机页错误异常时:
|
||||
- s1_itlb_exception 返回的虚拟机页错误。
|
||||
- iTLB 返回的物理地址有效(fromITLB(PortNumber).bits.miss 为低),s1_itlb_exception 指示虚拟机页错误 pgf。
|
||||
3. 当 ITLB 发生访问错误异常时:
|
||||
- s1_itlb_exception 返回的访问错误。
|
||||
- iTLB 返回的物理地址有效(fromITLB(PortNumber).bits.miss 为低),s1_itlb_exception 指示访问错误 af。
|
||||
|
||||
3. 处理虚拟机物理地址(用于虚拟化):
|
||||
|
||||
- 在虚拟化环境下,处理虚拟机物理地址(gpaddr),确定访问是否针对二级虚拟机的非叶子页表项(isForVSnonLeafPTE)。
|
||||
|
||||
1. 发生虚拟机页错误异常返回虚拟机物理地址(gpaddr):
|
||||
- 发生 pgf 后,需要返回对应的 gpaddr。
|
||||
- 只有一个通道发生 pgf 时,返回对应通道的 gpaddr 即可;多个通道发生 pgf 时,返回第一个通道的 gpaddr。
|
||||
2. 当访问二级虚拟机的非叶子页表项时:
|
||||
- 发生 gpf 后,如果是访问二级虚拟机的非叶子页表项时,需要返回对应的 gpaddr。
|
||||
- 只有一个通道发生 pgf 时,返回对应通道的 gpaddr 即可;多个通道发生 pgf 时,返回第一个通道的 gpaddr。
|
||||
|
||||
4. 返回基于页面的内存类型 pbmt 信息:
|
||||
- TLB 有效时,返回 pbmt 信息。
|
||||
|
||||
### 接收来自 IMeta(缓存元数据)的响应并检查缓存命中
|
||||
|
||||
从 Meta SRAM 中读取缓存标签和有效位。
|
||||
将物理地址的标签部分与缓存元数据中的标签比较,确定是否命中。
|
||||
|
||||
1. 缓存标签比较和有效位检查:
|
||||
|
||||
- 从物理地址中提取物理标签(ptag),将其与缓存元数据中的标签进行比较,检查所有缓存路(Way)。检查有效位,确保只考虑有效的缓存行。
|
||||
|
||||
1. 缓存未命中(标签不匹配或有效位为假):
|
||||
- 当标签不匹配或者标签匹配,但是有效位为假时,表示缓存未命中。
|
||||
- s1_meta_ptags(PortNumber)(nWays) 不等于 ptags(PortNumber) 或者它们相等,但是对应的 s1_meta_valids 为低时,总之返回的 waymasks 为全 0。
|
||||
2. 单路缓存命中(标签匹配且有效位为真):
|
||||
- 当标签匹配,且有效位为真时,表示缓存命中。
|
||||
- waymasks 对应的位为 1。
|
||||
|
||||
### PMP(物理内存保护)权限检查
|
||||
|
||||
对物理地址进行 PMP 权限检查,确保预取操作的合法性。
|
||||
处理 PMP 返回的异常和 MMIO 信息
|
||||
|
||||
1. 访问被允许的内存区域
|
||||
- itlb 返回的物理地址在 PMP 允许的范围内。
|
||||
- s1_pmp_exception(i) 为 none。
|
||||
2. 访问被禁止的内存区域
|
||||
- s1_req_paddr(i) 对应的地址在 PMP 禁止的范围内。
|
||||
- s1_pmp_exception(i) 为 af。
|
||||
3. 访问 MMIO 区域
|
||||
- itlb 返回的物理地址在 MMIO 区域。
|
||||
- s1_pmp_mmio 为高。
|
||||
|
||||
### 异常处理和合并
|
||||
|
||||
backend 优先级最高,merge 方法里的异常越靠前优先级越高
|
||||
|
||||
合并来自后端、ITLB、PMP 的异常信息,按照优先级确定最终的异常类型。
|
||||
|
||||
1. 仅 ITLB 产生异常
|
||||
- s1_itlb_exception(i) 为非零,s1_pmp_exception(i) 为零。
|
||||
- s1_exception_out(i) 正确包含 ITLB 异常。
|
||||
2. 仅 PMP 产生异常
|
||||
- s1_itlb_exception(i) 为零,s1_pmp_exception(i) 为非零。
|
||||
- s1_exception_out(i) 正确包含 PMP 异常。
|
||||
3. 仅 后端 产生异常
|
||||
- s1_itlb_exception(i) 为零,s1_pmp_exception(i) 为零。
|
||||
- s1_exception_out(i) 正确包含 后端 异常。
|
||||
4. ITLB 和 PMP 都产生异常
|
||||
- s1_itlb_exception(i) 和 s1_pmp_exception(i) 都为非零。
|
||||
- s1_exception_out(i) 包含 ITLB 异常(优先级更高)。
|
||||
5. ITLB 和 后端 都产生异常
|
||||
- s1_itlb_exception(i) 和 s1_backendException(i) 都为非零。
|
||||
- s1_exception_out(i) 包含 后端 异常(优先级更高)。
|
||||
6. PMP 和 后端 都产生异常
|
||||
- s1_pmp_exception(i) 和 s1_backendException(i) 都为非零。
|
||||
- s1_exception_out(i) 包含 后端 异常(优先级更高)。
|
||||
7. ITLB、PMP 和 后端 都产生异常
|
||||
- s1_itlb_exception(i)、s1_pmp_exception(i) 和 s1_backendException(i) 都为非零。
|
||||
- s1_exception_out(i) 包含 后端 异常(优先级更高)。
|
||||
8. 无任何异常
|
||||
- s1_itlb_exception(i)、s1_pmp_exception(i)、s1_backendException(i) 都为零。
|
||||
- s1_exception_out(i) 指示无异常。
|
||||
|
||||
### 发送请求到 WayLookup 模块
|
||||
|
||||
当条件满足时,将请求发送到 WayLookup 模块,以进行后续的缓存访问。
|
||||
|
||||
1. 正常发送请求到 WayLookup
|
||||
- toWayLookup.valid 为高,toWayLookup.ready 为高,s1_isSoftPrefetch 为假。
|
||||
- 请求成功发送,包含正确的地址、标签、waymask 和异常信息。
|
||||
2. WayLookup 无法接收请求
|
||||
- toWayLookup.valid 为高,toWayLookup.ready 为假。
|
||||
- 状态机等待 WayLookup 准备好,不会错误地推进。
|
||||
3. 软件预取请求不发送到 WayLookup
|
||||
- s1_isSoftPrefetch 为真。
|
||||
- toWayLookup.valid 为假,不会发送预取请求到 WayLookup。
|
||||
|
||||
### 状态机控制和请求处理流程
|
||||
|
||||
使用状态机管理 s1 阶段的请求处理流程。
|
||||
包括处理 ITLB 重发、Meta 重发、进入 WayLookup、等待 s2 准备等状态
|
||||
|
||||
1. 初始为 m_idle 状态:
|
||||
|
||||
1. 正常流程推进,保持 m_idle 状态
|
||||
|
||||
- s1_valid 为高,itlb_finish 为真,toWayLookup.fire 为真,s2_ready 为真。
|
||||
- 状态机保持在 m_idle 状态,s1 阶段顺利推进。
|
||||
|
||||
2. ITLB 未完成,需要重发
|
||||
|
||||
- s1_valid 为高,itlb_finish 为假。
|
||||
- 状态机进入 m_itlbResend 状态,等待 ITLB 完成。
|
||||
|
||||
3. ITLB 完成,WayLookup 未命中
|
||||
|
||||
- s1_valid 为高,itlb_finish 为真,toWayLookup.fire 为假。
|
||||
- 状态机进入 m_enqWay 状态,等待 WayLookup 入队。
|
||||
|
||||
2. 初始为 m_itlbResend 状态:
|
||||
1. ITLB 命中, MetaArray 空闲,需要 WayLookup 入队
|
||||
- itlb_finish 为假,toMeta.ready 为真。
|
||||
- 状态机进入 m_enqWay 状态,等待 WayLookup 入队。
|
||||
2. ITLB 命中, MetaArray 繁忙,等待 MetaArray 读请求
|
||||
- itlb_finish 为假,toMeta.ready 为假。
|
||||
- 状态机进入 m_metaResend 状态,MetaArray 读请求
|
||||
3. 初始为 m_metaResend 状态:
|
||||
|
||||
1. MetaArray 空闲 ,需要 WayLookup 入队
|
||||
- toMeta.ready 为真。
|
||||
- 状态机进入 m_enqWay 状态,等待 WayLookup 入队。
|
||||
|
||||
4. 初始为 m_enqWay 状态:
|
||||
|
||||
1. WayLookup 入队完成或者为软件预取, S2 空闲, 重新进入空闲状态
|
||||
- toWayLookup.fire 或 s1_isSoftPrefetch 为真,s2_ready 为假。
|
||||
- 状态机进入空闲状态 m_idle。
|
||||
2. WayLookup 入队完成或者为软件预取, S2 繁忙,需要 enterS2 状态
|
||||
- toWayLookup.fire 或 s1_isSoftPrefetch 为真,s2_ready 为真。
|
||||
- 状态机进入 m_enterS2 状态,等待 s2 阶段准备好。
|
||||
|
||||
5. 初始为 m_enterS2 状态:
|
||||
1. s2 阶段准备好,请求进入下流水级,流入后进入 m_idle 状态
|
||||
- s2_ready 为真。
|
||||
- 状态机进入空闲状态 m_idle。
|
||||
|
||||
### 监控 missUnit 的请求
|
||||
|
||||
检查 missUnit 的响应,更新缓存的命中状态和 MSHR 的匹配状态。
|
||||
|
||||
1. 请求与 MSHR 匹配且有效:
|
||||
|
||||
- s2_req_vSetIdx 和 s2_req_ptags 与 fromMSHR 中的数据匹配,且 fromMSHR.valid 为高,fromMSHR.bits.corrupt 为假。
|
||||
- s2_MSHR_match(PortNumber) 为真, s2_MSHR_hits(PortNumber) 应保持为真
|
||||
|
||||
2. 请求在 SRAM 中命中:
|
||||
|
||||
- s2_waymasks(PortNumber) 中有一位为高,表示在缓存中命中。
|
||||
- s2_SRAM_hits(PortNumber) 为真,s2_hits(PortNumber) 应为真。
|
||||
|
||||
3. 请求未命中 MSHR 和 SRAM:
|
||||
- 请求未匹配 MSHR,且 s2_waymasks(PortNumber) 为空。
|
||||
- s2_MSHR_hits(PortNumber)、s2_SRAM_hits(PortNumber) 均为假, s2_hits(PortNumber) 为假。
|
||||
|
||||
### 发送请求到 missUnit
|
||||
|
||||
对于未命中的预取请求,向 missUnit 发送请求,以获取缺失的数据。
|
||||
|
||||
1. 确定需要发送给 missUnit 的请求
|
||||
|
||||
- 根据命中状态、异常信息、MMIO 信息等,确定哪些请求需要发送到 missUnit(即 s2_miss)。
|
||||
|
||||
1. 请求未命中且无异常,需要发送到 missUnit:
|
||||
|
||||
- s2_hits(PortNumber) 为假(未命中缓存),s2_exception 无异常,s2_mmio 为假(不是 MMIO 或不可缓存的内存)。
|
||||
- s2_miss(PortNumber) 为真,表示需要发送请求到 missUnit。
|
||||
|
||||
2. 请求命中或有异常,不需要发送到 missUnit:
|
||||
|
||||
- s2_hits(i) 为真(已命中)或者 s2_exception 有异常 或者 s2_mmio 为真(MMIO 访问)。
|
||||
- s2_miss(i) 为假,不会发送请求到 missUnit。
|
||||
|
||||
3. 双行预取时,处理第二个请求的条件:
|
||||
- s2_doubleline 为真,处理第二个请求。
|
||||
- 如果第一个请求有异常或 MMIO,s2_miss(1) 应为假,后续请求被取消或处理。
|
||||
|
||||
2. 避免发送重复请求,发送请求到 missUnit
|
||||
|
||||
- 使用寄存器 has_send 记录每个端口是否已发送请求,避免重复发送。
|
||||
- 将需要发送的请求通过仲裁器 toMSHRArbiter 发送到 missUnit。
|
||||
|
||||
1. 在 s1_real_fire 时,复位 has_send:
|
||||
|
||||
- s1_real_fire 为高。
|
||||
- has_send(PortNumber) 应被复位为假,表示新的请求周期开始。
|
||||
|
||||
2. 当请求成功发送时,更新 has_send:
|
||||
|
||||
- toMSHRArbiter.io.in(PortNumber).fire 为高(请求已发送)。
|
||||
- has_send(PortNumber) 被设置为真,表示该端口已发送请求。
|
||||
|
||||
3. 避免重复发送请求:
|
||||
|
||||
- 同一请求周期内,has_send(PortNumber) 为真,s2_miss(PortNumber) 为真。
|
||||
- toMSHRArbiter.io.in(PortNumber).valid 为假,不会再次发送请求。
|
||||
|
||||
4. 正确发送需要的请求到 missUnit:
|
||||
|
||||
- s2_valid 为高,s2_miss(i) 为真,has_send(i) 为假。
|
||||
- toMSHRArbiter.io.in(i).valid 为高,请求被成功发送。
|
||||
|
||||
5. 仲裁器正确仲裁多个请求:
|
||||
- 多个端口同时需要发送请求。
|
||||
- 仲裁器按照优先级或设计要求选择请求发送到 missUnit,未被选中的请求在下个周期继续尝试发送。
|
||||
|
||||
### 刷新机制
|
||||
|
||||
- io.flush: 全局刷新信号,当该信号为高时,所有请求都需要刷新。
|
||||
- from_bpu_s0_flush:当请求不是软件预取(!s0_isSoftPrefetch, 软件预取请求是由特定的指令触发的,与指令流中的分支预测无关。因此,在处理刷新信号时,对于软件预取请求,通常不受来自 BPU 的刷新信号影响。),且 BPU 指示需要在 Stage 2 或 Stage 3 刷新的请求,由于该请求尚未进入 s1 阶段,因此在 s0 阶段也需要刷新。
|
||||
- s0_flush:综合考虑全局刷新信号、来自 BPU 的刷新信号,以及 s1 阶段的刷新信号
|
||||
- from_bpu_s1_flush:当 s1 阶段的请求有效且不是软件预取,且 BPU 指示在 Stage 3 需要刷新,则在 s1 阶段需要刷新。
|
||||
- io.itlbFlushPipe:当 s1 阶段需要刷新时,该信号用于通知 ITLB 刷新其流水线,以保持一致性。
|
||||
- s1_flush:综合考虑全局刷新信号和来自 BPU 的刷新信号。
|
||||
- s2_flush:用于控制 s2 阶段是否需要刷新。
|
||||
|
||||
1. 发生全局刷新
|
||||
- io.flush 为高。
|
||||
- s0_flush、s1_flush、s2_flush 分别为高,所有阶段的请求被正确清除。
|
||||
2. 来自 BPU 的刷新
|
||||
- io.flushFromBpu.shouldFlushByStageX 为真(X 为 2 或 3),且请求不是软件预取。
|
||||
- 对应阶段的 from_bpu_sX_flush 为高,sX_flush 为高,阶段请求被刷新。
|
||||
3. 刷新时状态机复位
|
||||
- s1_flush 为高。
|
||||
- 状态机 state 被重置为 m_idle 状态。
|
||||
4. ITLB 管道同步刷新
|
||||
- s1_flush 为高。
|
||||
- io.itlbFlushPipe 为高,ITLB 被同步刷新。
|
||||
|
|
@ -0,0 +1,362 @@
|
|||
---
|
||||
title: MainPipe
|
||||
linkTitle: MainPipe
|
||||
weight: 12
|
||||
---
|
||||
|
||||
<div class="icache-ctx">
|
||||
|
||||
</div>
|
||||
|
||||
## MainPipe
|
||||
|
||||
MainPipe 为 ICache 的主流水,为三级流水设计,负责从 DataArray 中读取数据,pmp 检查,缺失处理,并且将结果返回给 IFU。
|
||||
|
||||
<div>
|
||||
<center>
|
||||
<img src="../mainpipe_structure.png"
|
||||
alt="MainPipe结构示意图"
|
||||
style="zoom:100%"/>
|
||||
<br>
|
||||
MainPipe结构示意图
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
1. 从 WayLookup 获取信息,访问 DataArray 单路(S0 阶段)
|
||||
在 S0 流水级,从 WayLookup 获取元数据,包括路命中信息和 ITLB 查询结果,访问 DataArray 的单路,如果 DataArray 正在被写或 WayLookup 中没有有效表项,流水线就会阻塞。每次重定向后,FTQ 中同一个请求被同时发送到 MainPipe 和 IPrefetchPipe 中,MainPipe 始终需要等待 IPrefetchPipe 将请求的查询信息写入 WayLookup 后才能向下走,导致了 1 拍重定向延迟,当预取超过取指时,该延迟就会被覆盖。
|
||||
|
||||
- 接收并解析来自 FTQ 的取指请求,提取必要的请求信息,如虚拟地址、缓存组索引、块内偏移、是否为双行读、后端的异常信息。
|
||||
- 从 WayLookup 模块获取缓存命中信息和 TLB 信息,包括 waymask、物理标签、虚拟机物理地址、是否为叶节点、 ITLB 异常、ITLB 的 PBMT 信息、缓存元数据的校验码。
|
||||
- 访问 DataArray 的单路,如果 DataArray 正在被写或 WayLookup 中没有有效表项,流水线就会阻塞。
|
||||
- 每次重定向后,FTQ 中同一个请求被同时发送到 MainPipe 和 IPrefetchPipe 中,MainPipe 始终需要等待 IPrefetchPipe 将请求的查询信息写入 WayLookup 后才能向下走,导致了 1 拍重定向延迟,当预取超过取指时,该延迟就会被覆盖。
|
||||
|
||||
2. 接收上一个阶段的信息并进行数据暂存、PMP 检查、从 DataArray 获取读响应异常合并、替换策略更新以及监控 MissUnit(S1 阶段)
|
||||
|
||||
- 寄存并延迟 S0 阶段信息
|
||||
- 从 S0 获取的地址、tag、命中方式(waymask)、TLB 异常标志、下一拍要用的数据等,都会在 S1 寄存一拍,保证在流水线停顿时也能维持正确值。
|
||||
- Meta ECC 检查
|
||||
- 对 S0 读出的 meta 和其校验码(ECC/Parity)进行比对,判断是否发生错误。如果关闭 parity 功能,则跳过该检查。
|
||||
- 更新 replacer
|
||||
- 对确定命中的访问请求,进行“touch”更新,标记最近使用过的 way,以便后续替换算法正确运行。
|
||||
- PMP 检查
|
||||
- 根据 S0 得到的物理地址(paddr),在 S1 对其进行 PMP 检查,判断是否拥有执行权限、是否为 MMIO 等。在当拍收到响应,将结果寄存到下一流水级进行处理。
|
||||
- 需要指出,IPrefetchPipe s1 流水级也会进行 PMP 检查,和此处的检查实际上是完全一样的,分别检查只是为了优化时序(避免 `ITLB(reg) -> ITLB.resp -> PMP.req -> PMP.resp -> WayLookup.write -> bypass -> WayLookup.read -> MainPipe s1(reg)` 的超长组合逻辑路径)。
|
||||
- 异常合并
|
||||
- 将 ITLB 与 PMP 异常进行优先级合并,产生最终的异常标记。
|
||||
- 选择数据来源:MSHR 或 SRAM
|
||||
- 接收 DataArray 返回的 data 和 code 并寄存,同时监听 MSHR 的响应,当 DataArray 和 MSHR 的响应同时有效时,后者的优先级更高。当 MSHR 已在填充一些数据,如果当前请求与 MSHR 命中,可以在 S1 阶段直接选用 MSHR 的数据,而不必依赖 SRAM 读出的结果。
|
||||
|
||||
3. 监控 MissUnit,在 ECC 校验、异常处理和缺失处理之后,将最终的数据、异常信息传递给 IFU,完成取指流程(S2 阶段)
|
||||
|
||||
- ECC 校验
|
||||
- DataArray ECC 校验,对 S1 流水级寄存的 code 进行校验,生成 data 是否损坏信号 s2_data_corrupt。如果校验出错,就将错误报告给 BEU。
|
||||
- MetaArray ECC 校验,IPrefetchPipe 读出 MetaArray 的数据后会直接进行校验,并将校验结果随命中信息一起入队 WayLookup 并随 MainPipe 流水到达 S2 级(meta_corrupt 信号),在此处随 DataArray 的 ECC 校验结果一起报告给 BEU。
|
||||
- 监控 MissUnit 响应端口
|
||||
- 检查当前 S2 阶段的请求是否与 MSHR 中的条目匹配,命中时寄存 MSHR 响应的数据,为了时序在下一拍才将数据发送到 IFU。
|
||||
- 更新 Data 和其是否来自 MSHR 的信息。
|
||||
- 更新 s2_hits 和处理异常。
|
||||
- 处理 L2 Cache 的 Corrupt 标志。
|
||||
- 缺失处理,发送 Miss 请求到 MSHR
|
||||
- 计算是否需要重新获取(Refetch)。
|
||||
- 通过是否命中、ECC 错误、正确跨行、是否异常和是否属于 MMIO 区域来发送 Miss 请求。
|
||||
- 设置 Arbiter 合并多个端口的 Miss 请求,确保一次只处理一个请求,同时有避免重复请求的设置。
|
||||
- 判断 Fetch 是否完成。
|
||||
- 生成 L2 Cache 的异常标记,再将当前 S2 阶段的异常(包括 ITLB、PMP)与 L2 Cache 的异常进行合并。
|
||||
- 响应 IFU
|
||||
- 将最终的数据、异常信息传递给 IFU,完成取指流程。
|
||||
- 根据请求是否为跨行,决定如何处理双行数据。
|
||||
- 报告 TileLink 的 Corrupt 错误
|
||||
- 对于每个端口,如果在当前周期 s2_fire 时检测到来自 L2 Cache 的数据 corrupt 错误,就将错误报告给 BEU。
|
||||
|
||||
## MainPipe 的功能点和测试点
|
||||
|
||||
### 访问 DataArray 的单路
|
||||
|
||||
根据从 WayLookup 获取信息,包括路命中信息和 ITLB 查询结果还有 DataArray 当前的情况,决定是否需要从 DataArray 中读取数据。
|
||||
|
||||
1. 访问 DataArray 的单路
|
||||
- 当 WayLookup 中的信息表明路命中时,ITLB 查询成功,并且 DataArray 当前没有写时,MainPipe 会向 DataArray 发送读取请求,以获取数据。
|
||||
- s0_hits 为高(一路命中),s0_itlb_exception 信号为零(ITLB 查询成功),toData.last.ready 为高(DataArray 没有正在进行的写操作)。
|
||||
- toData.valid 信号为高,表示 MainPipe 向 DataArray 发出了读取请求。
|
||||
2. 不访问 DataArray(Way 未命中) ==会访问,但是返回数据无效==
|
||||
- 当 WayLookup 中的信息表明路未命中时,MainPipe 不会向 DataArray 发送读取请求。
|
||||
- s0_hits 为低表示缓存未命中
|
||||
- toData.valid 信号为低,表示 MainPipe 未向 DataArray 发出读取请求。
|
||||
3. 不访问 DataArray(ITLB 查询失败)==会访问,但是返回数据无效==
|
||||
- 当 ITLB 查询失败时,MainPipe 不会向 DataArray 发送读取请求。
|
||||
- s0_itlb_exception 信号不为零(ITLB 查询失败)。
|
||||
- toData.valid 信号为低,表示 MainPipe 未向 DataArray 发出读取请求。
|
||||
4. 不访问 DataArray(DataArray 正在进行写操作)
|
||||
- 当 DataArray 正在进行写操作时,MainPipe 不会向 DataArray 发送读取请求。
|
||||
- toData.last.ready 信号为低,表示 DataArray 正在进行写操作。
|
||||
- toData.valid 信号为低,表示 MainPipe 未向 DataArray 发出读取请求。
|
||||
|
||||
### Meta ECC 校验
|
||||
|
||||
将物理地址的标签部分与对应的 Meta 进行 ECC 校验,以确保 Meta 的完整性。
|
||||
|
||||
1. 无 ECC 错误
|
||||
|
||||
- 当 waymask 全为 0(没有命中),则 hit_num 为 0 或 waymask 有一位为 1(一路命中),hit_num 为 1 且 ECC 对比通过(encodeMetaECC(meta) == code)
|
||||
- s1_meta_corrupt 为假。
|
||||
|
||||
2. 单路命中的 ECC 错误
|
||||
|
||||
- 当 waymask 有一位为 1(一路命中),ECC 对比失败(encodeMetaECC(meta) != code)
|
||||
- s1_data_corrupt(i), io.errors(i).valid, io.errors(i).bits.report_to_beu, io.errors(i).bits.source.data 为 true。
|
||||
|
||||
3. 多路命中
|
||||
> hit multi-way, must be an ECC failure
|
||||
|
||||
- 当 waymask 有两位及以上为 1(多路命中),视为 ECC 错误。
|
||||
- s1_data_corrupt(i), io.errors(i).valid, io.errors(i).bits.report_to_beu, io.errors(i).bits.source.data 为 true。
|
||||
|
||||
4. ECC 功能关闭
|
||||
|
||||
- 当奇偶校验关闭时(ecc_enable 为低),强制清除 s1_meta_corrupt 信号置位。
|
||||
- 不管是否发生 ECC 错误,s1_meta_corrupt 都为假。
|
||||
|
||||
### PMP 检查
|
||||
|
||||
- 将 S1 的物理地址 s1_req_paddr(i) 和指令 TlbCmd.exec 发往 PMP,判断取指是否合法。
|
||||
- 防止非法地址,区分普通内存和 MMIO 内存。
|
||||
|
||||
1. 没有异常
|
||||
- s1_pmp_exception 为全零,表示没有 PMP 异常。
|
||||
2. 通道 0 有 PMP 异常
|
||||
- s1_pmp_exception(0) 为真,表示通道 0 有 PMP 异常。
|
||||
3. 通道 1 有 PMP 异常
|
||||
- s1_pmp_exception(1) 为真,表示通道 1 有 PMP 异常。
|
||||
4. 通道 0 和通道 1 都有 PMP 异常
|
||||
- s1_pmp_exception(0) 和 s1_pmp_exception(1) 都为真,表示通道 0 和通道 1 都有 PMP 异常。
|
||||
5. 没有映射到 MMIO 区域
|
||||
- s1_pmp_mmio(0) 和 s1_pmp_mergemmio(1) 都为假,表示没有映射到 MMIO 区域。
|
||||
6. 通道 0 映射到了 MMIO 区域
|
||||
- s1_pmp_mmio(0) 为真,表示映射到了 MMIO 区域。
|
||||
7. 通道 1 映射到了 MMIO 区域
|
||||
- s1_pmp_mmio(1) 为真,表示映射到了 MMIO 区域。
|
||||
8. 通道 0 和通道 1 都映射到了 MMIO 区域
|
||||
- s1_pmp_mmio(0) 和 s1_pmp_mmio(1) 都为真,表示通道 0 和通道 1 都映射到了 MMIO 区域。
|
||||
|
||||
### 异常合并
|
||||
|
||||
- 将 s1_itlbmergeption 与 s1_pmp_exception 合并生成 s1_exception_out。
|
||||
- ITLB 异常通常优先于 PMP 异常。merge
|
||||
|
||||
1. 没有异常
|
||||
- s1_exception_out 为全零,表示没有异常。
|
||||
2. 只有 ITLB 异常
|
||||
- s1_exception_out 和 s1_itlb_exception 一致
|
||||
3. 只有 PMP 异常
|
||||
- s1_exception_out 和 s1_pmp_exception 一致
|
||||
4. ITLB 与 PMP 异常同时出现
|
||||
> itlb has the highest priority, pmp next
|
||||
- s1_exception_out 和 s1_itlb_exception 一致
|
||||
|
||||
### MSHR 匹配和数据选择
|
||||
|
||||
- 检查当前的请求是否与 MSHR 中正在处理的缺失请求匹配。
|
||||
- 判断 缓存组索引相同(s1_req_vSetIdx(i) == fromMSHR.bits.vSetIdx) ,物理标签相同 (s1_req_ptags(i) == fromMSHR.bits.blkPaddr);若匹配 MSHR 有效且没有错误(fromMSHR.valid && !fromMSHR.bits.corrupt),则优先使用 MSHR 中的数据
|
||||
- 避免重复访问 Data SRAM,提升性能;当 MSHR 中已有重填结果时,可立即命中。
|
||||
|
||||
1. 命中 MSHR
|
||||
|
||||
- MSHR 中已有正确数据时,S1 阶段能直接拿到
|
||||
- s1_MSHR_hits(i) 为 true 时,s1_datas(i) 为 s1_bankMSHRHit(i),s1_data_is_from_MSHR(i) 为 true
|
||||
|
||||
2. 未命中 MSHR
|
||||
|
||||
- MSHR 中存放的地址与当前请求不同,那么应该读取 SRAM 的数据
|
||||
- s1_MSHR_hits(i) 为 true 时,s1_datas(i) 为 fromData.datas(i),s1_data_is_from_MSHR(i) 为 false
|
||||
|
||||
3. MSHR 数据 corrupt
|
||||
|
||||
- fromMSHR.bits.corrupt = true,那么 MSHR 将不匹配,应该读取 SRAM 的数据
|
||||
- s1_datas(i) 为 fromData.datas(i),s1_data_is_from_MSHR(i) 为 false
|
||||
|
||||
### Data ECC 校验
|
||||
|
||||
在 S2 阶段,对从 S1 或 MSHR 获得的数据(如 s2_datas)进行 ECC 校验:
|
||||
|
||||
- 若 ECC 校验失败,则标记 s2_data_corrupt(i) = true。
|
||||
- 若数据来自 MSHR,则不重复进行 ECC 校验(或忽略 corrupt)
|
||||
|
||||
1. 无 ECC 错误
|
||||
|
||||
- s2_bank 全部没有损坏,bank 也选对了对应的端口和 bank,数据不来自 MSHR
|
||||
- s2_data_corrupt(i) 为 false,没有 ECC 错误。
|
||||
|
||||
2. 单 Bank ECC 错误
|
||||
|
||||
- s2_bank_corrupt(bank) 有一个为 true ,即对应的 bank 有损坏;同时 bank 也选对了对应的端口和 bank,数据不来自 MSHR
|
||||
- s2_data_corrupt(i), io.errors(i).valid, io.errors(i).bits.report_to_beu, io.errors(i).bits.source.data 为 true。
|
||||
|
||||
3. 多 Bank ECC 错误
|
||||
|
||||
- s2_bank_corrupt(bank) 有两个或以上为 true,即对应的 bank 有损坏;同时 bank 也选对了对应的端口和 bank,数据不来自 MSHR
|
||||
- s2_data_corrupt(i), io.errors(i).valid, io.errors(i).bits.report_to_beu, io.errors(i).bits.source.data 为 true。
|
||||
|
||||
4. ECC 功能关闭
|
||||
|
||||
- 当奇偶校验关闭时(ecc_enable 为低),强制清除 s2_data_corrupt 信号置位。
|
||||
- 不管是否发生 ECC 错误,s2_data_corrupt 都为假。
|
||||
|
||||
### 冲刷 MetaArray
|
||||
|
||||
Meta 或者 Data ECC 校验错误时,会冲刷 MetaArray,为重取做准备。
|
||||
|
||||
1. 只有 Meta ECC 校验错误
|
||||
> if is meta corrupt, clear all way (since waymask may be unreliable)
|
||||
|
||||
- 当 s1_meta_corrupt 为真时,MetaArray 的所有路都会被冲刷。
|
||||
- toMetaFlush(i).valid 为真,toMetaFlush(i).bits.waymask 对应端口的所有路置位。
|
||||
|
||||
2. 只有 Data ECC 校验错误
|
||||
> if is data corrupt, only clear the way that has error
|
||||
|
||||
- 当 s2_data_corrupt 为真时,只有对应路会被冲刷。
|
||||
- toMetaFlush(i).valid 为真,toMetaFlush(i).bits.waymask 对应端口的对应路置位。
|
||||
|
||||
3. 同时有 Meta ECC 校验错误和 Data ECC 校验错误
|
||||
|
||||
- 处理 Meta ECC 的优先级更高, 将 MetaArray 的所有路冲刷。
|
||||
- toMetaFlush(i).valid 为真,toMetaFlush(i).bits.waymask 对应端口的所有路置位。
|
||||
|
||||
### 监控 MSHR 匹配与数据更新
|
||||
|
||||
- 判断是否命中 MSHR
|
||||
- 根据 MSHR 是否命中和 s1 阶段是否发射来更新 s2 的数据,s2 的命中状态和 l2 是否损坏
|
||||
|
||||
1. MSHR 命中(匹配且本阶段有效)
|
||||
|
||||
- MSHR 的 vSetIdx / blkPaddr 与 S2 请求一致, fromMSHR.valid 有效,s2_valid 也有效
|
||||
- s2_MSHR_match,s2_MSHR_hits 为高,s2_bankMSHRHit 对应 bank 为高
|
||||
- s1_fire 无效时,s2_datas 更新为 MSHR 的数据,将 s2_data_is_from_MSHR 对应位置位,s2_hits 置位,清除 s2_data_corrupt,l2 的 corrupt 更新为 fromMSHR.bits.corrupt
|
||||
- s1_fire 有效时,s2_datas 为 s1_datas 的数据,将 s2_data_is_from_MSHR 对应位置为 s1 的 s1_data_is_from_MSHR,s2_hits 置为 s1_hits,清除 s2_data_corrupt,l2 的 corrupt 为 false
|
||||
|
||||
2. MSHR 未命中
|
||||
|
||||
- MSHR 的 vSetIdx / blkPaddr 与 S2 请求一致, fromMSHR.valid 有效,s2_valid 也有效,至少有一个未达成
|
||||
- s2_MSHR_hits(i) = false,S2 不会更新 s2_datas,继续保持原先 SRAM 数据或进入 Miss 流程。
|
||||
|
||||
### Miss 请求发送逻辑和合并异常
|
||||
|
||||
- 通过计算 s2_should_fetch(i) 判断是否需要向 MSHR 发送 Miss 请求:
|
||||
- 当出现未命中 (!s2_hits(i)) 或 ECC 错误(s2_meta_corrupt(i) || s2_data_corrupt(i)) 时,需要请求重新获取。
|
||||
- 若端口存在异常或处于 MMIO 区域,则不发送 Miss 请求。
|
||||
- 使用 Arbiter 将多个端口的请求合并后发送至 MSHR。
|
||||
- 通过 s2_has_send(i) 避免重复请求。
|
||||
- 将 S2 阶段已有的 ITLB/PMP 异常(s2_exception)与 L2 Cache 报告的 s2_l2_corrupt(i)(封装后为 s2_l2_exception(i))进行合并。
|
||||
|
||||
1. 未发生 Miss
|
||||
|
||||
- 当 s2_hits(i) 为高(s2 已经命中),s2 的 meta 和 data 都没有错误,s2 异常,处于 mmio 区域
|
||||
- 以上条件至少满足一个时,s2_should_fetch(i) 为低,表示不发送 Miss 请求。
|
||||
|
||||
2. 单口 Miss
|
||||
|
||||
- 当出现未命中 (!s2_hits(i)) 或 ECC 错误(s2_meta_corrupt(i) || s2_data_corrupt(i)),端口不存在异常且未处于 MMIO 区域时,会向 MSHR 发送 Miss 请求。
|
||||
- toMSHRArbiter.io.in(i).valid = true ,Arbiter 只发送一条 Miss 请求。
|
||||
|
||||
3. 双口都需要 Miss
|
||||
|
||||
- 同上,但是两个端口都满足 s2_should_fetch 为高的条件。
|
||||
- toMSHRArbiter.io.in(0).valid、toMSHRArbiter.io.in(1).valid 均为 true,Arbiter 根据仲裁顺序依次发出请求。
|
||||
|
||||
4. 重复请求屏蔽
|
||||
|
||||
- 当 s1_fire 为高,表示可以进入 s2 阶段,那么 s2 还没有发送 s2_has_send(i) := false.B
|
||||
- 如果已经有请求发送了,那么对应的 toMSHRArbiter.io.in(i).fire 为高,表示对应的请求可以发送,s2_has_send(i) := true。
|
||||
- 此时再次发送,toMSHRArbiter.io.in(i).valid 为低,表示发送失败。
|
||||
|
||||
5. 仅 ITLB/PMP 异常
|
||||
|
||||
- S1 阶段已记录了 ITLB 或 PMP 异常,L2 corrupt = false。
|
||||
- 2_exception_out 仅保留 ITLB/PMP 异常标记,无新增 AF 异常。
|
||||
|
||||
6. 仅 L2 异常
|
||||
|
||||
- S2 阶段 s2_l2_corrupt(i) = true,且无 ITLB/PMP 异常。
|
||||
- s2_exception_out(i) 表示 L2 访问错误(AF)。
|
||||
|
||||
7. ITLB + L2 同时出现
|
||||
|
||||
- 同时触发 ITLB 异常和 L2 corrupt。
|
||||
- s2_exception_out 优先保留 ITLB 异常类型,不被 L2 覆盖。
|
||||
|
||||
8. s2 阶段取指完成
|
||||
|
||||
- s2_should_fetch 的所有端口都为低,表示需要取指,那么取指完成
|
||||
- s2_fetch_finish 为高
|
||||
|
||||
### 响应 IFU
|
||||
|
||||
- 若当前周期 S2 成功发射(s2_fire = true)且数据获取完毕(s2_fetch_finish),则把数据、异常信息、物理地址等打包到 toIFU.bits 输出。
|
||||
- 若为双行请求(s2_doubleline = true),也会向 IFU 发送第二路的信息(地址、异常)。
|
||||
|
||||
1. 正常命中并返回
|
||||
|
||||
- 不存在任何异常或 Miss,s2 命中,s2 阶段取指完成,外部的 respStall 停止信号也为低 。
|
||||
- toIFU.valid = true,toIFU.bits.data 为正确的 Cacheline 数据,toIFU.bits.exception、pmp_mmio、itlb_pbmt = none。
|
||||
|
||||
2. 异常返回
|
||||
|
||||
- 设置 ITLB、PMP、或 L2 corrupt 异常。
|
||||
- toIFU.bits.exception(i) = 对应异常类型,pmp_mmio、itlb_pbmt 根据是否有对应的异常设置为 true。
|
||||
|
||||
3. 跨行取指
|
||||
|
||||
- s2_doubleline = true,同时检查第一路、第二路返回情况。
|
||||
- toIFU.bits.doubleline = true。
|
||||
- 若第二路正常,toIFU.bits.exception(1) = none;若第二路异常,则 exception(1) 标记相应类型。
|
||||
- pmp_mmio、itlb_pbmt 类似。
|
||||
|
||||
4. RespStall
|
||||
|
||||
- 外部 io.respStall = true,导致 S2 阶段无法发射到 IFU。
|
||||
- s2_fire = false,toIFU.valid 也不拉高,S2 保持原状态等待下一拍(或直到 respStall 解除)。
|
||||
|
||||
### L2 Corrupt 报告
|
||||
|
||||
- 当检测到 L2 Cache 返回的 corrupt 标记时(s2_l2_corrupt(i) = true),在 S2 完成发射后额外向外部错误接口 io.errors(i) 报告。
|
||||
- 与 Data ECC 或 Meta ECC 不同,L2 corrupt 由 L2 自己报告给 BEU,这里不需要再次报告给 beu。
|
||||
|
||||
1. L2 Corrupt 单路
|
||||
|
||||
- s2 阶段准备完成可以发射(s2_fire 为高),s2_MSHR_hits(0)和 fromMSHR.bits.corrupt 为高
|
||||
- s2_l2_corrupt(0) = true,io.errors(0).valid = true,io.errors(0).bits.source.l2 = true。
|
||||
|
||||
2. 双路同时 corrupt
|
||||
|
||||
- 端口 0 和端口 1 都从 L2 corrupt 数据中获取。
|
||||
- s2_l2_corrupt 均为 true,发射后分别报告到 io.errors(0) 和 io.errors(1)。
|
||||
|
||||
### 刷新机制
|
||||
|
||||
- io.flush:外部的全局刷新信号,它用于指示整个流水线需要被冲刷(清空)。
|
||||
- s0_flush: S0 阶段内部的刷新信号,它由 io.flush 传递而来,用于控制 S0 阶段的刷新操作。
|
||||
- s1_flush: S1 阶段内部的刷新信号,它由 io.flush 传递而来,用于控制 S1 阶段的刷新操作。
|
||||
- s2_flush: S2 阶段内部的刷新信号,它由 io.flush 传递而来,用于控制 S2 阶段的刷新操作。
|
||||
|
||||
1. 全局刷新
|
||||
|
||||
- io.flush 被激活时,流水线的各个阶段(S0, S1 和 S2)都能正确响应并执行刷新操作。
|
||||
- io.flush = true。
|
||||
- s0_flush, s1_flush, s2_flush = true。
|
||||
|
||||
2. S0 阶段刷新
|
||||
|
||||
- s0_flush = true。
|
||||
- s0_fire = false。
|
||||
|
||||
3. S1 阶段刷新
|
||||
|
||||
- s1_flush = true。
|
||||
- s1_valid, s1_fire = false。
|
||||
|
||||
4. S2 阶段刷新
|
||||
|
||||
- s2_flush = true。
|
||||
- s2_valid, toMSHRArbiter.io.in(i).valid , s2_fire = false
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
---
|
||||
title: WayLookup
|
||||
linkTitle: WayLookup
|
||||
weight: 12
|
||||
---
|
||||
|
||||
<div class="icache-ctx">
|
||||
|
||||
</div>
|
||||
|
||||
## WayLookup
|
||||
|
||||
<div>
|
||||
<center>
|
||||
<img src="../waylookup_structure_rw.png"
|
||||
alt="WayLookup 读写结构"
|
||||
style="zoom:100%"/>
|
||||
<br>
|
||||
WayLookup 读写结构
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div>
|
||||
<center>
|
||||
<img src="../waylookup_structure_update.png"
|
||||
alt="WayLookup 更新结构"
|
||||
style="zoom:100%"/>
|
||||
<br>
|
||||
WayLookup 更新结构
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
- 内部是 FIFO 环形队列结构。暂存 IPrefetchPipe 查询 MetaArray 和 ITLB 得到的元数据,以备 MainPipe 使用。同时监听 MSHR 写入 SRAM 的 cacheline,对命中信息进行更新。
|
||||
- 通过 readPtr 和 writePtr 来管理读写位置。当有 flush 信号时,读写指针都会被重置。当写入数据时,写指针递增;读取时,读指针递增。需要处理队列的空和满的情况,empty 是读指针等于写指针,而 full 则是两者的值相同且标志位不同。
|
||||
- 处理 GPF 的部分,有一个 gpf_entry 寄存器,存储 GPF 的相关信息。当写入的数据包含 GPF 异常时,需要将信息存入 gpf_entry,并记录当前的写指针位置到 gpfPtr。当读取的时候,如果当前读指针的位置与 gpfPtr 匹配,并且 gpf_entry 有效,那么就将 GPF 信息一并输出。
|
||||
- IPrefetchPipe 向其写入 WayLookupInfo 信息(包括 vSetIdx,waymask,ptag,itlb_exception,itlb_pbmt,meta_codes,gpaddr,isForVSnonLeafPTE)。
|
||||
- 写入前,需要考虑队列是否已满,以及是否有 GPF 阻塞。如果有 GPF 信息待读取且未被处理,则写入需要等待,防止覆盖 GPF 信息。写入时,如果数据中包含 GPF 异常,就将信息存入 gpf_entry,并更新 gpfPtr。
|
||||
- MainPipe 从其读出 WayLookupInfo 信息。
|
||||
- 在读取上,有两种情况:当队列为空但有写请求时,可以直接将写的数据旁路(bypass)给读端口;否则就从 entries 数组中读取对应读指针的数据。同时,如果当前读的位置存在 GPF 信息,就将 GPF 信息一起输出,并在读取后清除有效位。
|
||||
- 允许 bypass(当队列为空但有写请求时,可以直接将写的数据旁路给读端口),为了不将更新逻辑的延迟引入到 DataArray 的访问路径上,在 MSHR 有新的写入时禁止出队,MainPipe 的 S0 流水级也需要访问 DataArray,当 MSHR 有新的写入时无法向下走,所以该措施并不会带来额外影响。
|
||||
- MissUnit 向其写入命中信息。
|
||||
- 若是命中则将 waymask 更新 ICacheMissResp 信息(包括 blkPaddr,vSetIdx,waymask,data,corrupt)且 meta_codes 也更新,否则 waymask 清零。更新逻辑与 IPrefetchPipe 中相同,见 [IPrefetchPipe 子模块文档中的“命中信息的更新”](./01_iprefetchpipe.md#命中信息的更新)一节。
|
||||
|
||||
### GPaddr 省面积机制
|
||||
|
||||
由于 `gpaddr` 仅在 guest page fault 发生时有用,并且每次发生 gpf 后前端实际上工作在错误路径上,后端保证会送一个 redirect(WayLookup flush)到前端(无论是发生 gpf 前就已经预测错误/发生异常中断导致的;还是 gpf 本身导致的),因此在 WayLookup 中只需存储 reset/flush 后第一个 gpf 有效时的 gpaddr。对双行请求,只需存储第一个有 gpf 的行的 `gpaddr。`
|
||||
|
||||
在实现上,把 gpf 相关信号(目前只有 `gpaddr`)与其它信号(`paddr`,etc.)拆成两个 bundle,其它信号实例化 nWayLookupSize 个,gpf 相关只实例化一个寄存器。同时另用一个 `gpfPtr` 指针。总计可以节省$(\text{nWayLookupSize}\times2-1)\times \text{GPAddrBits} - \log_2{(\text{nWayLookupSize})} - 1$bit 的寄存器。
|
||||
当 prefetch 向 WayLookup 写入时,若有 gpf 发生,且 WayLookup 中没有已经存在的 gpf,则将 gpf/gpaddr 写入 `gpf_entry` 寄存器,同时将 `gpfPtr` 设置为此时的 `writePtr。`
|
||||
当 MainPipe 从 WayLookup 读取时,若 bypass,则仍然直接将 prefetch 入队的数据出队;否则,若 `readPtr === gpfPtr`,则读出 gpf_entry;否则读出全 0。
|
||||
需要指出:
|
||||
|
||||
1. 考虑双行请求,`gpaddr` 只需要存一份(若第一行发生 gpf,则第二行肯定也在错误路径上,不必存储),但 gpf 信号本身仍然需要存两份,因为 ifu 需要判断是否是跨行异常。
|
||||
2. `readPtr===gpfPtr` 这一条件可能导致 flush 来的比较慢时 `readPtr` 转了一圈再次与 `gpfPtr` 相等,从而错误地再次读出 gpf,但如前所述,此时工作在错误路径上,因此即使再次读出 gpf 也无所谓。
|
||||
3. 需要注意一个特殊情况:一个跨页的取指块,其 32B 在前一页且无异常,后 2B 在后一页且发生 gpf,若前 32B 正好是 16 条 RVC 压缩指令,则 IFU 会将后 2B 及对应的异常信息丢弃,此时可能导致下一个取指块的 `gpaddr` 丢失。需要在 WayLookup 中已有一个未被 MainPipe 取走的 gpf 及相关信息时阻塞 WayLookup 的入队(即 IPrefetchPipe s1 流水级),见 PR#3719。
|
||||
|
||||
## WayLookup 的功能点和测试点
|
||||
|
||||
### 刷新操作
|
||||
|
||||
- 接收到全局刷新刷新信号 io.flush 后,读、写指针和 GPF 信息都被重置。
|
||||
|
||||
1. 刷新读指针
|
||||
|
||||
- io.flush 为高时,重置读指针。
|
||||
- readPtr.value 为 0, readPtr.flag 为 false。
|
||||
|
||||
2. 刷新写指针
|
||||
|
||||
- io.flush 为高时,重置写指针。
|
||||
- writePtr.value 为 0, writePtr.flag 为 false。
|
||||
|
||||
3. 刷新 GPF 信息
|
||||
|
||||
- io.flush 为高时,重置 GPF 信息。
|
||||
- gpf_entry.valid 为 0, gpf_entry.bits 为 0。
|
||||
|
||||
### 读写指针更新
|
||||
|
||||
- 读写信号握手完毕之后(io.read.fire/io.write.fire 为高),对应指针加一。
|
||||
- 因为是在环形队列上,所以超过队列大小后,指针会回到队列头部。
|
||||
|
||||
1. 读指针更新
|
||||
|
||||
- 当 io.read.fire 为高时,读指针加一。
|
||||
- readPtr.value 加一。
|
||||
- 如果 readPtr.value 超过环形队列的大小,readPtr.flag 会翻转。
|
||||
|
||||
2. 写指针更新
|
||||
|
||||
- 当 io.write.fire 为高时,写指针加一。
|
||||
- writePtr.value 加一。
|
||||
- 如果 writePtr.value 超过环形队列的大小,writePtr.flag 会翻转。
|
||||
|
||||
### 更新操作
|
||||
|
||||
- MissUnit 处理完 Cache miss 后,向 WayLookup 写入命中信息,也就是 update 操作。
|
||||
- 情况分为两种:
|
||||
- 命中:更新 waymask 和 meta_codes。
|
||||
- 未命中:重置 waymask。
|
||||
|
||||
1. 命中更新
|
||||
|
||||
- MissUnit 返回的更新信息和 WayLookup 的信息相同时,更新 waymask 和 meta_codes。
|
||||
- vset_same 和 ptag_same 为真。
|
||||
- waymask 和 meta_codes 更新。
|
||||
- hits 对应位为高。
|
||||
|
||||
2. 未命中更新
|
||||
|
||||
- vset_same 和 way_same 为真。
|
||||
- waymask 清零。
|
||||
- hit 对应位为高。
|
||||
|
||||
3. 不更新
|
||||
|
||||
- 其他情况下不更新。
|
||||
- vset_same 为假或者 ptag_same 和 way_same 都为假。
|
||||
- hits 对应位为低。
|
||||
|
||||
### 读操作
|
||||
|
||||
- 读操作会根据读指针从环形队列中读取信息。
|
||||
- 如果达成了绕过条件,优先绕过。
|
||||
|
||||
1. Bypass 读
|
||||
|
||||
- 队列为空,并且 io.write.valid 写有效时,可以直接读取,而不经过队列。
|
||||
- empty 和 io.write.valid 都为真。
|
||||
- io.read.bits = io.write.bits
|
||||
|
||||
2. 读信号无效
|
||||
|
||||
- 队列为空(readPtr === writePtr)且写信号 io.write.valid 为低。
|
||||
- io.read.valid 为低,读信号无效。
|
||||
|
||||
3. 正常读
|
||||
|
||||
- 未达成绕过条件(empty 和 io.write.valid 至少有一个为假)且 io.read.valid 为高。
|
||||
- 从环形队列中读取信息。
|
||||
- io.read.bits.entry = entries(readPtr.value)
|
||||
|
||||
4. gpf 命中
|
||||
|
||||
- io.read.valid 为高,可以读。
|
||||
- 当 gpf_hits 为高时,从 GPF 队列中读取信息。
|
||||
- io.read.bits.gpf = gpf_entry.bits
|
||||
|
||||
5. gpf 命中且被读取
|
||||
|
||||
- io.read.valid 为高,可以读。
|
||||
|
||||
> also clear gpf_entry.valid when it's read
|
||||
|
||||
- 当 gpf 命中且被读取其时(io.read.fire 为高),gpf_entry.valid 会被置为 0。
|
||||
|
||||
6. gpf 未命中
|
||||
|
||||
- io.read.valid 为高,可以读。
|
||||
- io.read.bits.gpf 清零。
|
||||
|
||||
### 写操作
|
||||
|
||||
- 写操作会根据写指针从环形队列中读取信息。
|
||||
- 如果有 gpf 停止,就会停止写。
|
||||
|
||||
1. gpf 停止
|
||||
> if there is a valid gpf to be read, we should stall write
|
||||
|
||||
- gpf 队列数据有效,并且没有被读取或者没有命中,就会产生 gpf 停止,此时写操作会被停止。
|
||||
- gpf_entry.valid && !(io.read.fire && gpf_hit) 为高时,写操作会被停止(io.write.ready 为低)。
|
||||
|
||||
2. 写就绪无效
|
||||
|
||||
- 当队列为满((readPtr.value === writePtr.value) && (readPtr.flag ^ writePtr.flag))或者 gpf 停止时,写操作会被停止。
|
||||
- (io.write.ready 为低)
|
||||
|
||||
3. 正常写
|
||||
|
||||
- 当 io.write.valid 为高时(没满且没有 gpf 停止),写操作会被执行。
|
||||
- 正常握手完毕 io.write.fire 为高。
|
||||
- 写信息会被写入环形队列。
|
||||
- entries(writePtr.value) = io.write.bits.entry。
|
||||
|
||||
有 ITLB 异常的写
|
||||
|
||||
- 前面与正常写相同,只不过当写信息中存在 ITLB 异常时,会更新 gpf 队列和 gpf 指针。
|
||||
- 此时如果已经被绕过直接读取了,那么就不需要存储它了。
|
||||
- 4. 被绕过直接读取了
|
||||
- can_bypass 和 io.read.fire 都为高。
|
||||
- gpf_entry.valid 为 false。
|
||||
- gpf_entry.bits = io.write.bits.gpf
|
||||
- gpfPtr = writePtr
|
||||
- 5. 没有被绕过直接读取
|
||||
- can_bypass 为低。
|
||||
- gpf_entry.valid 为 true。
|
||||
- gpf_entry.bits = io.write.bits.gpf
|
||||
- gpfPtr = writePtr
|
||||
|
|
@ -0,0 +1,268 @@
|
|||
---
|
||||
title: MissUnit
|
||||
linkTitle: MissUnit
|
||||
weight: 12
|
||||
---
|
||||
|
||||
<div class="icache-ctx">
|
||||
|
||||
</div>
|
||||
|
||||
## 子模块:FIFO
|
||||
|
||||
- 一个先入先出的循环队列,目前仅在 MissUnit 中有使用,作为优先队列 priorityFIFO。
|
||||
- 按照在 MissUnit 中的实例化,pipe 是默认值 false,hasflush 是 true。
|
||||
- 队列的指针都是环形的,分为入队指针(写指针,ent_ptr)和出队指针(读指针,deq_ptr),记录读和写的位置。
|
||||
- 两个指针都有对应的 flag 位,当指针超过队列大小时,flag 位会翻转,用以判断是否已经循环。
|
||||
- 在入队、出队对应的 fire(valid && ready) 信号有效时,移动对应的指针。
|
||||
|
||||
## FIFO 的功能点和测试点
|
||||
|
||||
### 入队操作
|
||||
|
||||
1. 队未满,正常入队
|
||||
|
||||
- 当队列未满,且空位不小于一时,可以正常入队,如果从零号位开始入队到最大容量,入队指针的 flag 不会翻转。
|
||||
- io.enq.fire 为高有效,regFiles(enq_ptr.value) = io.enq.bits,enq_ptr.value+1 入队指针移动,入队指针标记位不翻转。
|
||||
- 重复以上操作至队满。
|
||||
|
||||
2. 队未满,入队后标记位翻转
|
||||
|
||||
- 当队未满,但是空位却是靠近队尾时,入队一位后就到达了队头,入队指针的 flag 会翻转。
|
||||
- 队列的容量为 10,入队指针指向 9,队未满。此时如果 io.enq.fire 为高,则 regFiles(9) = io.enq.bits,enq_ptr.value+1(循环队列,加完后 enq_ptr.value=0)入队指针移动,入队指针标记位翻转。
|
||||
|
||||
3. 队满,入队就绪信号为低,无法入队
|
||||
|
||||
- 当队满时,(enq_ptr.value === deq_ptr.value) && (enq_ptr.flag ^ deq_ptr.flag) 为高,io.enq.ready 为低,io.enq.fire 为低无效。
|
||||
- 此时入队,入队指针的 value 和 flag 不变。
|
||||
|
||||
### 出队操作
|
||||
|
||||
1. 队非空,正常出队
|
||||
|
||||
- 当队列非空时,可以正常出队,如果出队指针不经过最大容量位置,出队指针的 flag 不会翻转。
|
||||
- io.deq.fire 为高有效,io.deq.bits = regFiles(deq_ptr.value),deq_ptr.value+1 出队指针移动,出队指针标记位不翻转。
|
||||
|
||||
2. 队非空,出队后标记位翻转
|
||||
|
||||
- 当队非空,但是出队指针是靠近队尾时,出队一位后就到达了队头,出队指针的 flag 会翻转。
|
||||
- 队列的容量为 10,出队指针指向 9,队非空。此时如果 io.deq.fire 为高,则 io.deq.bits = regFiles(9),deq_ptr.value+1(循环队列,加完后 deq_ptr.value=0)出队指针移动,出队指针标记位翻转。
|
||||
|
||||
3. 队空,出队有效信号为低,无法出队
|
||||
|
||||
- 当队空时,enq_ptr === deq_ptr 为高,io.deq.valid 为低,io.deq.fire 为低无效。
|
||||
- 此时出队,出队指针的 value 和 flag 不变。
|
||||
|
||||
### 刷新清空操作
|
||||
|
||||
1. flush 清空
|
||||
|
||||
- 当刷新信号有效时,重置出队和入队的指针和标记位,清空队列。
|
||||
- 当 flush 为高时,deq_ptr.value=0,enq_ptr.value=0,deq_ptr.flag=false,enq_ptr.flag=false,empty=true,full=false。
|
||||
|
||||
## MissUnit
|
||||
|
||||
<div>
|
||||
<center>
|
||||
<img src="../missunit_structure.png"
|
||||
alt="MissUnit 结构"
|
||||
style="zoom:100%"/>
|
||||
<br>
|
||||
MissUnit 结构
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
- 接收并管理多个 Miss 请求
|
||||
- 处理来自 Fetch 和 Prefetch 的 Miss 请求。
|
||||
- 将这些请求分派给适当数量的 MSHR 进行排队和状态管理。
|
||||
- 管理 MSHR
|
||||
- ICacheMissUnit 使用多个 MSHR 来跟踪和管理未完成的缓存未命中请求。为了防止 flush 时取指 MSHR 不能完全释放,设置取指 MSHR 的数量为 4,预取 MSHR 的数量为 10。采用数据和地址分离的设计方法,所有的 MSHR 共用一组数据寄存器,在 MSHR 只存储请求的地址信息、状态等信息。
|
||||
- 接收来自 MainPipe 的取指请求和来自 IPrfetchPipe 的预取请求,取指请求只能被分配到 fetchMSHR,预取请求只能分配到 prefetchMSHR,入队时采用低 index 优先的分配方式。
|
||||
- 在入队的同时对 MSHR 进行查询,如果请求已经在 MSHR 中存在,就丢弃该请求,对外接口仍表现 fire,只是不入队到 MSHR 中。==在入队时向 Replacer 请求写入 waymask==。当请求完成后,MSHR 会被释放,以便处理新的请求。
|
||||
- 通过 TileLink 协议与 L2 缓存进行通信,发送获取缓存块的请求(mem_acquire),并接收 L2 缓存的响应(mem_grant)。
|
||||
- 当到 L2 的总线空闲时,选择 MSHR 表现进行处理,整体 fetchMSHR 的优先级高于 prefetchMSHR,只有没有需要处理的 fetchMSHR,才会处理 prefetchMSHR。
|
||||
- 对于 fetchMSHR,采用低 index 优先的优先级策略,因为同时最多只有两个请求需要处理,并且只有当两个请求都处理完成时才能向下走,所有 fetchMSHR 之间的优先级并不重要。
|
||||
- 对于 prefetchMSHR,考虑到预取请求之间具有时间顺序,采用先到先得的优先级策略,在入队时通过一个 FIFO 记录入队顺序,处理时按照入队顺序进行处理。
|
||||
- 通过状态机与 Tilelink 的 D 通道进行交互,到 L2 的带宽为 32byte,需要分 2 次传输,并且不同的请求不会发生交织,所以只需要一组寄存器来存储数据。
|
||||
- 当一次传输完成时,根据传输的 id 选出对应的 MSHR,从 MSHR 中读取地址、掩码等信息,将相关信息写入 SRAM,同时将 MSHR 释放。
|
||||
- 向 MetaArray 和 DataArray 发送写请求,向 MainPipe 发送响应
|
||||
- 当数据传回后,MissUnit 根据相应的替换策略信息(victim way),将新数据写回 ICache 的 SRAM(Meta/Data) 。
|
||||
- 同时向取指端(或预取端)返回“Miss 已完成”的响应,包括:写入了哪一路(way)、实际数据以及可能的校验信息(如 corrupt 标记等)。
|
||||
- 处理特殊情况(如 flush、fencei、数据损坏等)
|
||||
- 遇到 Flush 或 fence.i 等指令时,MissUnit 可以终止或跳过某些 Miss 请求的写回,从而保证不在无效或过期的情况下写入缓存。
|
||||
- 数据若出现 corrupt(部分拍损坏),也会在写回或发给前端时进行特殊处理或标记。
|
||||
|
||||
过程:
|
||||
|
||||
1. fetch_req 和 prefetch_req 分别先经过 DeMultiplexer (Demux),把请求分发给对应数量的 MSHR。fetch 的 MSHR 和 prefetch 的 MSHR 分成两组,分别处理取指和预取请求。
|
||||
2. 每个 MSHR 内部会记录当前 Miss 请求的地址、索引、是否已经发出 acquire 等状态。当有其它相同的 miss 请求进来时,可以直接 “ hit MSHR ” 而不用重复创建新的请求。
|
||||
3. 对于 fetchMSHR,采用低 index 优先的优先级策略;对于 prefetchMSHR,采用先到先得的优先级策略,在入队 prefetchMSHR 前通过一个 priorityFIFO.记录入队顺序,处理时按照入队顺序进行处理。
|
||||
4. fetchMSHR 发出的请求与 prefetchArb 选出的 prefetchMSHR 通过 acquireArb 合并后,通过 mem_acquire 发送给下一级或外部存储。
|
||||
5. mem_grant 表示对这一条 Miss 请求的返回数据。需要分多个 beat 收集,直到收满一个 Cacheline。
|
||||
6. 收集完 Cacheline 数据后,会根据对应 MSHR 的信息向 metaArray 和 dataArray 发起写操作 (meta_write, data_write),同时向取指端 (fetch_resp) 发送补全后的数据和标记 (waymask 等)。
|
||||
7. 如果发生 flush 或 fencei,在未发出请求前,请求会被无效化;请求被发出后,会持续阻止新请求进入,已经发出的访问最终会将返回过程走完,但收到的响应并不会回复给 MainPipe 和 IPrefetchPipe,也不会写给 MetaArray 和 DataArray。
|
||||
|
||||
Demultiplexer 类
|
||||
grant:选择第一个 ready(能写)的 mshr,写进去(第 0 到 n 个端口,前面有 ready 的。比如 grant=seq(false,true),grant(1)为 true,表示 1 端口前面有一个 ready 的端口(0 端口))
|
||||
io.out(i).valid:前 i-1 个 mshr 没有 ready 的,输入的写有效。
|
||||
io.in.ready := grant.last || io.out.last.ready,给 MissUnit 的 ready 信号有一个有效,那么 MissUnit 给 MSHR 的 ready 信号就有效。
|
||||
|
||||
## MissUnit 的功能点和测试点
|
||||
|
||||
### 处理取指缺失请求
|
||||
|
||||
处理来自 MainPipe 的取指单元的缓存缺失请求,将缺失请求分发到多个 Fetch MSHR 中的一个,避免重复请求。
|
||||
低索引的请求优先处理。
|
||||
|
||||
1. 接受新的取指请求
|
||||
|
||||
- 当新的 fetch miss 与 MSHR 中的已有请求不重复时(通过 io.fetch_req.bits.blkPaddr / vSetIdx 给出具体地址),MissUnit 会将请求分配到一个空闲的 Fetch MSHR 中。
|
||||
- 当有新的取指缺失请求到达时(io.fetch_req.valid 为高),且没有命中已有的 MSHR(fetchHit 为低),io.fetch_req.ready 应为高,表示可以接受请求。
|
||||
- io.fetch_req.fire 成功握手后,该 MSHR 处于 valid = true 状态,并记录地址。
|
||||
|
||||
2. 处理已有的取指请求
|
||||
|
||||
- 当已有取指缺失请求到达时(io.fetch_req.valid 为高),且命中已有的 MSHR(fetchHit 为高),io.fetch_req.ready 应为高,虽然不接受请求,但是表现出来为已经接收请求。
|
||||
- fetchDemux.io.in.valid 应为低,fetchDemux.io.in.fire 为低,表示没有新的请求被分发到 MSHR。
|
||||
|
||||
3. 低索引的请求优先进入 MSHR
|
||||
- Fetch 的请求会通过 fetchDemux 分配到多个 Fetch MSHR,fetchDemux 的实现中,低索引的 MSHR 会优先被分配请求。
|
||||
- 当取指请求有多个 io.out(i).read 时,选择其中的第一个,也就是低索引的写入 MSHR,io.chose 为对应的索引。
|
||||
|
||||
### 处理预取缺失请求
|
||||
|
||||
与 Fetch Miss 类似,但走另一些 MSHR(Prefetch MSHR)。
|
||||
|
||||
1. 接受新的预取请求
|
||||
|
||||
- 当新的 prefetch miss 与 MSHR 中的已有请求不重复时(通过 io.prefetch_req.bits.blkPaddr / vSetIdx 给出具体地址),MissUnit 会将请求分配到一个空闲的 Prefetch MSHR 中。
|
||||
- 当有新的预取缺失请求到达时(io.prefetch_req.valid 为高),且没有命中已有的 MSHR(prefetchHit 为低),io.prefetch_req.ready 应为高,表示可以接受请求。
|
||||
- io.prefetch_req.fire 成功握手后,该 MSHR 处于 valid = true 状态,并记录地址。
|
||||
|
||||
2. 处理已有的预取请求
|
||||
|
||||
- 当已有预取缺失请求到达时(io.prefetch_req.valid 为高),且命中已有的 MSHR(prefetchHit 为高),io.prefetch_req.ready 应为高,虽然不接受请求,但是表现出来为已经接收请求。
|
||||
- prefetchDemux.io.in.valid 应为低,prefetchDemux.io.in.fire 为低,表示请求被接受但未分发到新的 MSHR。
|
||||
|
||||
3. 低索引的请求优先进入 MSHR
|
||||
|
||||
- Prefetch 的请求会通过 prefetchDemux 分配到多个 Prefetch MSHR,prefetchDemux 的实现中,低索引的 MSHR 会优先被分配请求。
|
||||
- 当取指请求有多个 io.out(i).read 时,选择其中的第一个,也就是低索引的写入 MSHR,io.chose 为对应的索引。
|
||||
|
||||
4. 先进入 MSHR 的优先进入 prefetchArb
|
||||
- 从 prefetchDemux 离开后,请求的编号会进入 priorityFIFO,priorityFIFO 会根据进入队列的顺序排序,先进入队列的请求会先进入 prefetchArb。
|
||||
- prefetchDemux.io.in.fire 为高,并且 prefetchDemux.io.chosen 有数据时,将其编号写入 priorityFIFO。
|
||||
- 在 priorityFIFO 中有多个编号时,出队的顺序和入队顺序一致。
|
||||
- 检查 priorityFIFO.io.deq.bit 中的数据即可。
|
||||
|
||||
### MSHR 管理与查找
|
||||
|
||||
1. MSHR 查找命中逻辑
|
||||
|
||||
- 当新的请求到来时,能够正确查找所有 MSHR,判断请求是否命中已有 MSHR。
|
||||
- 当新的请求(取指或预取)到来时,系统遍历所有 MSHR,根据所有 MSHR 的查找信号 allMSHRs(i).io.lookUps(j).hit,检查请求是否已经存在于某个 MSHR 中。
|
||||
- 如果命中,则对应的 fetchHit 或 prefetchHit 为高。
|
||||
- 对于 prefetchHit 为高,还有一种情况:预取的物理块地址和组索引与取指的相等((io.prefetch_req.bits.blkPaddr === io.fetch_req.bits.blkPaddr) && (io.prefetch_req.bits.vSetIdx === io.fetch_req.bits.vSetIdx))并且有取指请求 io.fetch_req.valid 有效时,也算命中
|
||||
|
||||
2. MSHR 状态的更新与释放
|
||||
|
||||
- 当请求完成后,也就是来自内存总线的响应完成(D 通道接收完所有节拍),MSHR 能够正确地释放(清除其有效位),以便接收新的请求。
|
||||
- TileLink D 通道返回的 source ID ,即 io.mem_grant.bits.source。
|
||||
- 无效化信号 allMSHRs(i).io.invalid 为高,对应的 MSHR 的有效位 allMSHRs(i).valid 变为低
|
||||
|
||||
### acquireArb 仲裁
|
||||
|
||||
预取和取指的 acquire 都会发送给 acquireArb,acquireArb 会选择一个 acquire 发送给 mem_acquire。
|
||||
acquireArb 使用 chisel 自带的 Arbiter 实现,Arbiter 使用固定优先级仲裁,优先级从编号 0 开始,编号越小优先级越高。
|
||||
|
||||
1. acquireArb 仲裁
|
||||
- acquireArb 会选择一个 acquire 发送给 mem_acquire。
|
||||
- 当有多个 MSHR 同时发出请求时,acquireArb 会根据优先级进行仲裁,选择优先级最高的 MSHR 发送请求。
|
||||
- 取指请求总是在 0-3 号,预取请求直接在最后一号,所以取指请求优先级高于预取请求。
|
||||
- 当取指 acquire 和预取 acquire 同时发出时,fetchMSHRs(i).io.acquire 和 prefetchMSHRs(i).io.acquire 都有效,仲裁结果 acquireArb.io.out 应该和 fetchMSHRs(i).io.acquire 一致。
|
||||
|
||||
### Grant 数据接收与 Refill
|
||||
|
||||
在收到 TileLink D 通道数据时收集整行
|
||||
|
||||
- 累计 beat 数(readBeatCnt),直到完成一整行 (last_fire)
|
||||
- 记录 corrupt 标志
|
||||
- 将完成的请求映射回对应的 MSHR (id_r = mem_grant.bits.source)
|
||||
|
||||
1. 正常完整 Grant 流程,readBeatCnt 为 0 时
|
||||
- readBeatCnt 初始为 0,refillCycles - 1 也为 0。
|
||||
- io.mem_grant.valid 为高(因为 io.mem_grant.ready 默认为高,所以 io.mem_grant.fire 为高只需要 io.mem_grant.valid 为高)且 io.mem_grant.bits.opcpde(0)为高。
|
||||
- 此时 respDataReg(0)= io.mem_grant.bits.data
|
||||
- readBeatCnt 加一为 1。
|
||||
2. 正常完整 Grant 流程,readBeatCnt 为 1 时
|
||||
- io.mem_grant.valid 为高且 io.mem_grant.bits.opcpde(0)为高。
|
||||
- 此时 respDataReg(1)= io.mem_grant.bits.data
|
||||
- readBeatCnt 重置回 0。
|
||||
- last_fire 为高。
|
||||
- 下一拍 last_fire_r 为高,id_r=io.mem_grant.bits.source。
|
||||
3. 正常完整 Grant 流程,last_fire_r 为高
|
||||
- last_fire_r 为高,并且 id_r 为 0-13 中的一个。
|
||||
- 对应的 fetchMSHRs 或者 prefetchMSHRs 会被无效,也就是 fetchMSHRs_i 或 prefetchMSHRs_i-4 的 io_invalid 会被置高。
|
||||
4. Grant 带有 corrupt 标志
|
||||
- io.mem_grant.valid 为高且 io.mem_grant.bits.opcpde(0)为高,io.mem_grant.bits.corrupt 为高,则 corrupt_r 应为高。
|
||||
- 如果 io.mem_grant.valid 为高且 io.mem_grant.bits.opcpde(0)为高,io.mem_grant.bits.corrupt 为高中有一个不满足,且此时 last_fire_r 为高,则 corrupt_r 重置为低。
|
||||
|
||||
### 替换策略更新 (Replacer)
|
||||
|
||||
MissUnit 在发出 Acquire 请求时,还会将本次选中的 victim way 对应的索引告诉 io.victim,让替换策略更新其记录(替换策略采用 PLRU)
|
||||
只有当 Acquire 真正“fire”时,才说明成功替换,replacer 需要更新状态
|
||||
|
||||
1. 正常替换更新
|
||||
- 当 io.mem.acquire.ready & acquireArb.io.out.valid 同时为高,也就是 acquireArb.io.out.fir 为高时,io.victim.vSetIdx.valid 也为高。
|
||||
- io.victim.vSetIdx.bits = 当前 MSHR 请求的 acquireArb.io.out.bits.vSetIdx。
|
||||
2. 生成 waymask
|
||||
- 根据从 L2 返回的 mshr_resp 中 mshr_resp.bits.way 生成 waymask 信息。
|
||||
- 返回的 mshr_resp.bits.way 有 16 位,通过独热码生成一位掩码信息,waymask 表示其中哪一路被替换。
|
||||
- 生成的 waymask 应该和 mshr_resp.bits.way 一致。
|
||||
|
||||
### 写回 SRAM (Meta / Data)
|
||||
|
||||
在一条 Miss Request refill 完成时,将新得到的 Cache line 写到 ICache。
|
||||
生成 io.meta_write 和 io.data_write 的请求,带上 waymask, tag, idx, data 。
|
||||
生成 io.meta_write.valid 和 io.data_write.valid 信号。
|
||||
|
||||
1. 生成 io.meta_write.valid 和 io.data_write.valid 信号
|
||||
- 当 grant 传输完成后,经过一拍后,即 last_fire_r 为高,且从 TileLink 返回的 mshr_resp 中的 mshr_resp.valid 为高。
|
||||
- 并且此时没有硬件刷新信号和软件刷新信号,也就是 io.flush 和 io.fencei 为低。 在等待 l2 响应的过程中,没有刷新信号
|
||||
- 也没有数据 corrupt,即 corrupt_r 为低。
|
||||
- 那么 io.meta_write.valid 和 io.data_write.valid 均为高。
|
||||
2. 正常写 SRAM
|
||||
- io.meta_write.bits 的 virIdx、phyTag、waymask、bankIdx、poison 应该正常更新
|
||||
- io.data_write.bits 的 virIdx、data、waymask、bankIdx、poison 应该正常更新
|
||||
|
||||
### 向 mainPipe/prefetchPipe 发出 Miss 完成响应(fetch_resp)
|
||||
|
||||
在完成 refill 后,无论是否要真正写阵列,都会向取指端发送“Miss 请求完成”
|
||||
更新 io.fetch_resp.valid 和 fetch_resp.bits。
|
||||
|
||||
1. 正常 Miss 完成响应
|
||||
- 当 grant 传输完成后,经过一拍后,即 last_fire_r 为高,且从 TileLink 返回的 mshr_resp 中的 mshr_resp.valid 为高。
|
||||
- 无论此时是否有硬件刷新信号和软件刷新信号, io.fetch_resp.valid 都为高,说明可向取指端发送响应。
|
||||
- io.fetch_resp.bits 中的数据更新:
|
||||
- io.fetch_resp.bits.blkPaddr = mshr_resp.bits.blkPaddr
|
||||
- io.fetch_resp.bits.vSetIdx = mshr_resp.bits.vSetIdx
|
||||
- io.fetch_resp.bits.waymask = waymask
|
||||
- io.fetch_resp.bits.data = respDataReg.asUInt
|
||||
- io.fetch_resp.bits.corrupt = corrupt_r
|
||||
|
||||
### 处理 flush / fencei
|
||||
|
||||
一旦收到 io.flush 或 io.fencei 时,对未发射的请求可立即取消,对已经发射的请求在拿到数据后也不写 SRAM。
|
||||
|
||||
1. MSHR 未发射前 fencei
|
||||
- 如果 MSHR 还没有通过 io.acquire.fire 发出请求,就应立即取消该 MSHR(mshr_resp.valid= false),既不发出请求,也不要写 SRAM。
|
||||
- 当 io.fencei 为高时,fetchMSHRs 和 prefetchMSHRs 的 io.req.ready 和 io.acquire.valid 均为低,表示请求不发射。
|
||||
2. MSHR 未发射前 flush
|
||||
- 由于 fetchMSHRs 的 io.flush 被直接设置为 false,所以 io.flush 对 fetchMSHRs 无效,但是对 prefetchMSHRs 有效。
|
||||
- 当 io.flush 为高时,只能发射 fetchMSHRs 的请求。
|
||||
3. MSHR 已发射后 flush/fencei
|
||||
- 已经发射了请求,之后再有刷新信号,那么等数据回来了但不写 SRAM。
|
||||
- 在发射后,io.flush/io.fencei 为高时,等待数据回来,但是写 SRAM 的信号,write_sram_valid、io.meta_write.valid 和 io.data_write.valid 均为低,表示不写 SRAM。
|
||||
- 对于 response fetch 无影响。
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
---
|
||||
title: CtrlUnit
|
||||
linkTitle: CtrlUnit
|
||||
weight: 12
|
||||
---
|
||||
|
||||
<div class="icache-ctx">
|
||||
|
||||
</div>
|
||||
|
||||
## CtrlUnit
|
||||
|
||||
目前 CtrlUnit 主要负责 ECC 校验使能/错误注入等功能。
|
||||
RegField 案例类和伴生对象的作用,RegReadFn 和 RegWriteFn 案例类和伴生对象的作用。
|
||||
|
||||
通过两个控制寄存器 CSR:eccctrl 和 ecciaddr,来实现错误注入。
|
||||
在 eccctrlBundle 中,定义 eccctrl 的 ierror、istatus、itarget、inject、enable 域的初始值。
|
||||
在 ecciaddrBundle 中,定义 ecciaddr 的 paddr 域的初始值。
|
||||
|
||||
### mmio-mapped CSR
|
||||
|
||||
CtrlUnit 内实现了一组 mmio-mapped CSR,连接在 tilelink 总线上,地址可由参数 `cacheCtrlAddressOpt` 配置,默认地址为`0x38022080`。总大小为 128B。
|
||||
|
||||
当参数 `cacheCtrlAddressOpt` 为 `None` 时,CtrlUnit **不会实例化**。此时 ECC 校验使能**默认开启**,软件不可控制关闭;软件不可控制错误注入。
|
||||
|
||||
目前实现的 CSR 如下:
|
||||
|
||||
```plain
|
||||
64 10 7 4 2 1 0
|
||||
0x00 eccctrl | WARL | ierror | istatus | itarget | inject | enable |
|
||||
|
||||
64 PAddrBits-1 0
|
||||
0x08 ecciaddr | WARL | paddr |
|
||||
```
|
||||
|
||||
| CSR | field | desp |
|
||||
| ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| eccctrl | enable | ECC 错误校验使能,原 sfetchctl(0) 。 <br>注:即使不使能,在 icache 重填时仍会计算 parity,可能会有额外功耗;但如果不计算,则在未使能转换成使能时需要冲刷 icache(否则读出的 parity 有 50%概率是错的)。 |
|
||||
| eccctrl | inject | ECC 错误注入使能,写 1 即使能,读恒 0 |
|
||||
| eccctrl | itarget | ECC 错误注入目标 <br>0: metaArray<br>1: rsvd<br>2: dataArray<br>3: rsvd |
|
||||
| eccctrl | istatus | ECC 错误注入状态(read-only)<br>0: idle:注入控制器闲置<br>1: working:收到注入请求,注入中<br>2: injected:注入完成,等待触发<br>3: rsvd<br>4: rsvd<br>5: rsvd<br>6: rsvd<br>7: error:注入出错 |
|
||||
| eccctrl | ierror | ECC 错误原因(read-only)<br>0: ECC 未使能 (i.e. !eccctrl.enable) <br>1: inject 目标 SRAM 无效 (i.e. eccctrl.itarget==rsvd) <br>2: inject 目标地址 (i.e. ecciaddr.paddr) 不在 ICache 中<br>3: rsvd <br>4: rsvd <br>5: rsvd <br>6: rsvd <br>7: rsvd |
|
||||
| ecciaddr | paddr | ECC 错误注入物理地址 |
|
||||
| RERI standard | | RERI 手册还要求了错误计数等寄存器,用于软件获取 RAS controller 状态,参考手册,可能需要与 dcache、L2cache 统一在后端实现,icache 像现在给 BEU 送 error 一样送给后端。<br>即:暂时不需要在 icache 实现,但要把错误计数等机制所需的接口准备出来 |
|
||||
|
||||
### 错误校验使能
|
||||
|
||||
CtrlUnit 的 `eccctrl.enable` 位直接连接到 MainPipe,控制 ECC 校验使能。当该位为 0 时,ICache 不会进行 ECC 校验。但仍会在重填时计算校验码并存储,这可能会有少量的额外功耗;如果不计算,则在未使能转换成使能时需要冲刷 ICache(否则读出的 parity code 可能是错的)。
|
||||
|
||||
### 错误注入使能
|
||||
|
||||
CtrlUnit 内部使用一个状态机控制错误注入过程,其 status (注意:与 `eccctrl.istatus` 不同)有:
|
||||
|
||||
- idle:注入控制器闲置
|
||||
- readMetaReq:发送读取 metaArray 请求
|
||||
- readMetaResp:接收读取 metaArray 响应
|
||||
- writeMeta:写入 metaArray
|
||||
- writeData:写入 dataArray
|
||||
|
||||
当软件向 `eccctrl.inject` 写入 1 时,进行以下简单检查,检查通过时状态机进入 `readMetaReq` 状态:
|
||||
|
||||
- 若 `eccctrl.enable` 为 0,报错 `eccctrl.ierror=0`
|
||||
- 若 `eccctrl.itarget` 为 rsvd(1/3),报错 `eccctrl.ierror=1`
|
||||
|
||||
在 `readMetaReq` 状态下,CtrlUnit 向 MetaArray 发送 `ecciaddr.paddr` 地址对应的 set 读取的请求,等待握手。握手后转移到 `readMetaResp` 状态。
|
||||
|
||||
在 `readMetaResp` 状态下,CtrlUnit 接收到 MetaArray 的响应,检查 `ecciaddr.paddr` 地址对应的 ptag 是否命中,若未命中则报错 `eccctrl.ierror=2`。否则,根据 `eccctrl.itarget` 进入 `writeMeta` 或 `writeData` 状态。
|
||||
|
||||
在 `writeMeta` 或 `writeData` 状态下,CtrlUnit 向 MetaArray/DataArray 写入任意数据,同时拉高 `poison` 位,写入完成后状态机进入 `idle` 状态。
|
||||
|
||||
ICache 顶层中实现了一个 Mux,当 CtrlUnit 的状态机不为 `idle` 时,将 MetaArray/DataArray 的读写口连接到 CtrlUnit,而非 MainPipe/IPrefetchPipe/MissUnit。当状态机 `idle` 时反之。
|
||||
|
||||
状态机和错误注入流程
|
||||
|
||||
- `is_idle`:模块处于空闲状态,等待错误注入的触发。只有当 `eccctrl.istatus` 为 `working` 时,状态机才会转移到 `is_readMetaReq` 状态,准备读取元数据。
|
||||
- `is_readMetaReq`:发送读取元数据请求。通过接口 `metaRead` 向缓存发送读取请求。当握手成功后状态会转移到 `is_readMetaResp`。
|
||||
- `is_readMetaResp`:接收元数据响应并验证。如果未命中,则会设置错误状态。没命中会转移状态到`is_idle`,并设置错误错误注入状态和错误原因;如果找到有效的缓存行并且标签匹配,根据错误注入目标来判断是向元数据还是数据阵列写入错误。
|
||||
- `is_writeMeta`:写入带 poison 标记的数据完成注入。当握手成功后,错误注入状态设置为 injected(注入完成,等待触发),状态转移到`is_idle`。
|
||||
- `is_writeData`:写入带 poison 标记的数据完成注入。当握手成功后,向数据阵列写入错误数据,错误注入状态设置为 injected(注入完成,等待触发),状态转移到`is_idle`。
|
||||
|
||||
寄存器和接口映射
|
||||
|
||||
- eccctrl:控制 ECC 启用、错误注入状态等。寄存器通过 eccctrlRegField 进行映射。
|
||||
- ecciaddr:指定错误注入的物理地址。通过 ecciaddrRegField 映射。
|
||||
- 通过寄存器描述符(RegFieldDesc)和寄存器字段(RegField),定义了寄存器的这些是寄存器的描述信息和读写逻辑。
|
||||
- 通过 node.regmap,这两个寄存器被映射到指定的地址偏移。eccctrl 寄存器被映射到 params.eccctrlOffset 地址,ecciaddr 寄存器被映射到 params.ecciaddrOffset 地址。
|
||||
- `node.regmap` 使得这两个寄存器可以通过外部的 TileLink 接口进行访问,外部模块可以读写这些寄存器以控制 ECC 和错误注入功能。
|
||||
|
||||
## CtrlUnit 的功能点和测试点
|
||||
|
||||
### ECC 启用/禁用
|
||||
|
||||
控制 eccctrl.enable 字段来启用或禁用 ECC 功能。外部系统可以通过写寄存器 eccctrl 来控制 ECC 是否启用。
|
||||
|
||||
- 通过寄存器写入控制信号 enable,当 enable 为 true 时,ECC 功能启用;为 false 时,ECC 功能禁用。
|
||||
|
||||
1. 启用 ECC
|
||||
|
||||
- 向 eccctrl.enable 寄存器写入 true,验证模块内部 eccctrl.enable 设置为 true,并确保后续的错误注入操作能够成功进行。此测试确保 eccctrl.enable 写操作被执行。
|
||||
- 确保 eccctrl.enable 被正确设置为 true,并触发 eccctrlRegWriteFn 中的写操作逻辑。
|
||||
|
||||
2. 禁用 ECC
|
||||
|
||||
- 向 eccctrl.enable 寄存器写入 false,验证模块内部 eccctrl.enable 设置为 false,并确保在后续的错误注入过程中,ECC 功能被禁用,不允许进行错误注入。此测试确保 eccctrl.enable 写操作被正确设置为 false。
|
||||
- 验证禁用 ECC 时 eccctrl.enable 为 false,并触发 eccctrlRegWriteFn 中的错误处理分支。x.istatus = eccctrlInjStatus.error 和 x.ierror = eccctrlInjError.notEnabled
|
||||
|
||||
### 状态机转换
|
||||
|
||||
根据状态机的状态,验证错误注入的流程是否正确。
|
||||
|
||||
1. is_idle 状态
|
||||
|
||||
- 初始为 is_idle 状态。
|
||||
- 当 eccctrl.istatus 为 working 时,验证此时的状态为 is_readMetaReq。
|
||||
|
||||
2. is_readMetaReq 状态
|
||||
|
||||
- 当握手成功后(io.metaRead.ready 和 io.metaRead.valid 都为高),验证此时的状态为 is_readMetaResp。
|
||||
|
||||
3. is_readMetaResp 状态
|
||||
|
||||
- 未命中
|
||||
- 当 waymask 全零的时候,表示没有命中,会进入 is_idle 状态,并且设置错误错误注入状态和错误原因。
|
||||
- 验证此时的状态为 is_idle, eccctrl.istatus = error 和 eccctrl.ierror = notFound。
|
||||
- 命中
|
||||
- 当 waymask 不全零的时候,表示命中,会根据错误注入目标来判断是向元数据还是数据阵列写入错误。
|
||||
- 当 eccctrl.itarget=metaArray 时,验证此时的状态为 is_writeMeta ;当 eccctrl.itarget!=metaArray 时,验证此时的状态为 is_writeData。
|
||||
|
||||
4. is_writeMeta 状态
|
||||
|
||||
- RegWriteFn
|
||||
- 此状态进入后,io.dataWrite.valid 会为高
|
||||
- x.itarget = req.itarget
|
||||
- 当 req.inject 为高并且 x.istatus = idle 时:
|
||||
- 如果 ecc 的 req.enable = false,则验证 x.istatus = error 且 x.ierror = notEnabled
|
||||
- 否则,如果 req.itarget != metaArray 和 dataArray,则验证 x.istatus = error 且 x.ierror = targetInvalid
|
||||
- 如果都不满足,则验证 x.istatus = working
|
||||
- 状态转换
|
||||
- 当 io.metaWrite.fire 为高, 验证下一个状态为 is_idle,并且 eccctrl.istatus = injected。
|
||||
|
||||
5. is_writeData 状态
|
||||
|
||||
- RegWriteFn
|
||||
- 此状态进入后,io.dataWrite.valid 会为高
|
||||
- res.inject = false
|
||||
- 当 ready 为高,且 x.istatus = injected 或 x.istatus = error 时,验证 x.istatus = idle 和 x.ierror = notEnabled
|
||||
- 状态转换
|
||||
- 当 io.dataWrite.fire 为高, 验证下一个状态为 is_idle,并且 eccctrl.istatus = injected。
|
||||
|
||||
### 寄存器映射和外部访问
|
||||
|
||||
通过 TileLink 总线将寄存器映射到特定地址,使外部模块可以读写 ECC 控制寄存器和注入地址寄存器。
|
||||
|
||||
- 使用 TLRegisterNode 实现寄存器的映射,使得外部系统可以通过地址访问寄存器。寄存器的读写操作通过 TileLink 协议进行。
|
||||
|
||||
1. 外部读取和写入 ECC 控制寄存器
|
||||
|
||||
- 验证外部模块可以通过 TileLink 协议正确读取和写入 eccctrl 和 ecciaddr 寄存器,并对模块内部的状态产生影响,确保读写操作完全覆盖。
|
||||
|
||||
2. 外部模块触发错误注入
|
||||
|
||||
- 通过外部模块经 TileLink 总线向 eccctrl.inject 寄存器写入 true,触发错误注入,验证内部状态是否按 RegWriteFn 内部过程执行。
|
||||
|
|
@ -0,0 +1,178 @@
|
|||
---
|
||||
title: ICache
|
||||
linkTitle: ICache
|
||||
weight: 12
|
||||
---
|
||||
|
||||
<div class="icache-ctx">
|
||||
|
||||
</div>
|
||||
|
||||
## ICache
|
||||
|
||||
各种组合数据的宽度以 system verilog/verilog 中的为准。
|
||||
|
||||
|
||||
- IPrefetchPipe 接收来自 FTQ 的预取请求,然后向 MetaArray 和 ITLB 发送请求,再从 ITLB 的响应得到 paddr,之后与 MetaArray 返回的 tag 进行比较得到命中信息,把命中信息、MetaArray ECC 校验信息和 ITLB 信息一并写入 WayLookup,同时进行 PMP 检查。未命中就将信息发送给 MissUnit 处理,MissUnit 通过 TileLink 总线向 L2Cache 发起请求,获取数据后返回给 MetaArray 和 IPrefetchPipe。之后会判断是否 Miss,如果 Miss 则把预取请求发送到 MissUnit,它会通过 TileLink 向 L2 做预取指。
|
||||
- MainPipe 接收来自 FTQ 的取指请求,然后从 WayLookup 获取路命中信息和 ITLB 查询结果,再访问 DataArray。命中后向 replacer 发送 touch 请求,replacer 采用 PLRU 替换策略,接收到 MainPipe 的命中更新,向 MissUnit 提供写入的 waymask。同时进行 PMP 检查,接收 DataArry 返回的数据。对 DataArray 做 ECC 校验,根据 DataArry 和 MetaArry 的校验结果(MetaArray 的校验结果来自 Waylookup)判断是否将错误报告给总线(beu)。之后,如果 DataArry 没有命中,将信息发往 MissUnit 处理。MissUnit 通过 TileLink 总线向 L2Cache 发起请求,获取数据后返回给 DataArray 和 MainPipe。之后就可以将数据返回给 IFU。
|
||||
- MetaArray 存储缓存行的标签(Tag)和 ECC 校验码
|
||||
- 使用双 Bank SRAM 结构,支持双线访问(Double-Line),每个 Bank 存储部分元数据。
|
||||
- 标签包含物理地址的高位,用于地址匹配。
|
||||
- 支持标签 ECC 校验,检测和纠正存储错误。
|
||||
- valid_array 记录每个 Way 的有效状态,Flush 操作会清零
|
||||
- DataArray 存储实际的指令数据块。
|
||||
- 数据按 Bank 划分为八个,每个 Bank 宽度为 64 位,支持多 Bank 并行访问。
|
||||
- 数据 ECC 校验,分段生成校验码,增强错误检测能力。
|
||||
- 支持双线访问,根据地址偏移选择 Bank,单周期可读取 32 字节数据。
|
||||
- 冲刷信号有三种:ftqPrefetch.flushFromBpu,itlbFlushPipe,模块外部的 fencei 和 flush 信号。
|
||||
- ftqPrefetch.flushFromBpu:通过 FTQ 来自的 BPU 刷新信号,用于控制预取请求的冲刷。
|
||||
- itlbFlushPipe:ITLB 的冲刷信号,itlb 在收到该信号时会冲刷 gpf 缓存。
|
||||
- fencei:刷新 MetaArray,清除所有路的 valid_array 清零;missUnit 中所有 MSHR 置无效。
|
||||
- flush:mainPipe 和 prefetchPipe 所有流水级直接置无效,wayLookup 读写指针复位,gpf_entry 直接置无效,missUnit 中所有 MSHR 置无效。
|
||||
|
||||
### Replacer
|
||||
|
||||
采用 PLRU 更新算法,考虑到每次取指可能访问连续的 doubleline,对于奇地址和偶地址设置两个 replacer,在进行 touch 和 victim 时根据地址的奇偶分别更新 replacer。
|
||||
|
||||
<div>
|
||||
<center>
|
||||
<img src="../plru.png"
|
||||
alt="PLRU 算法示意"
|
||||
style="zoom:100%"/>
|
||||
<br>
|
||||
PLRU 算法示意
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
#### touch
|
||||
|
||||
Replacer 具有两个 touch 端口,用以支持双行,根据 touch 的地址奇偶分配到对应的 replacer 进行更新。
|
||||
|
||||
#### victim
|
||||
|
||||
Replacer 只有一个 victim 端口,因为同时只有一个 MSHR 会写入 SRAM,同样根据地址的奇偶从对应的 replacer 获取 waymask。并且在下一拍再进行 touch 操作更新 replacer。
|
||||
|
||||
## ICache 的功能点和测试点
|
||||
|
||||
### FTQ 预取请求处理
|
||||
|
||||
接收来自 FTQ 的预取请求,经 IPrefetchPipe 请求过滤(查询 ITLB 地址,是否命中 MetaArry,PMP 检查),(有异常则由 MissUnit 处理)后进入 WayLookup。
|
||||
|
||||
1. 预取地址命中,无异常
|
||||
|
||||
- io.ftqPrefetch.req.bits 的 startAddr 和 nextlineStart 在正常地址范围内,itlb 命中无异常,itlb 查询到的地址与 MetaArry 的 ptag 匹配,pmp 检查通过。
|
||||
- 如果没有监听到 MSHR 同样的位置发生了其它 cacheline 的写入,那么验证 wayLookup.io.write 的内容应该命中的取指数据。
|
||||
- 如果监听到 MSHR 同样的位置发生了其它 cacheline 的写入,那么验证 wayLookup.io.write 的内容应该是未命中的取指数据。
|
||||
|
||||
2. 预取地址未命中,无异常
|
||||
|
||||
- io.ftqPrefetch.req.bits 的 startAddr 和 nextlineStart 在正常地址范围内,itlb 命中无异常,itlb 查询到的地址与 MetaArry 的 ptag 不匹配,pmp 检查通过。
|
||||
- 如果监听到 MSHR 将该请求对应的 cacheline 写入了 SRAM,那么验证 wayLookup.io.write 的内容应该命中的取指数据。
|
||||
- 如果监听到 MSHR 没有将该请求对应的 cacheline 写入了 SRAM,那么验证 wayLookup.io.write 的内容应该未命中的取指数据。
|
||||
|
||||
3. 预取地址 TLB 异常,无其他异常
|
||||
|
||||
- io.ftqPrefetch.req.bits 的 startAddr 和 nextlineStart 在正常地址范围内,itlb 异常。
|
||||
- 验证 wayLookup.io.write 的 itlb_exception 内容中,其有对应的异常类型编号(pf:01;gpf:10;af:11)。
|
||||
|
||||
4. 预取地址 PMP 异常,无其他异常
|
||||
|
||||
- io.ftqPrefetch.req.bits 的 startAddr 和 nextlineStart 在正常地址范围内,itlb 命中无异常,itlb 查询到的地址与 MetaArry 的 ptag 匹配,pmp 检查未通过。
|
||||
- 验证 wayLookup.io.write 的 tlb_pbmt 内容中,其有对应的异常类型编号(nc:01;io:10)。
|
||||
|
||||
### FTQ 取指请求处理
|
||||
|
||||
io.fetch.resp <> mainPipe.io.fetch.resp 发送回 IFU 的数据是在 io.fetch.resp。
|
||||
|
||||
接收来自 FTQ 的取指请求,从 WayLookup 获取路命中信息和 ITLB 查询结果,再访问 DataArray,监控 MSHR 的响应。更新 replacer,做 pmp 检查。后做 DataArray 和 MetaArray 的 ECC 校验。最后将数据发送给 IFU。
|
||||
|
||||
1. 取指请求命中,无异常
|
||||
|
||||
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息,命中,pmp 检查正常,DataArray 和 MetaArray 的 ECC 校验正常。
|
||||
- 验证 replacer.io.touch 的 vSetIdx 和 way 和 ftq 的 fetch 一致,missUnit.io.victim 的 vSetIdx 和 way 是按照制定的算法生成的。
|
||||
- 验证 io.fetch.resp 的数据应该是取指的数据。
|
||||
|
||||
2. 取指请求未命中,MSHR 返回的响应命中,无异常
|
||||
|
||||
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息,未命中,pmp 检查正常,DataArray 和 MetaArray 的 ECC 校验正常。
|
||||
- 请求在 MSHR 返回的响应命中。
|
||||
- 验证 missUnit.io.victim 的 vSetIdx 和 way 是按照制定的算法生成的。
|
||||
- 验证 io.fetch.resp 的数据应该是取指的数据。
|
||||
|
||||
3. 取指请求命中,ECC 校验错误,无其他异常
|
||||
|
||||
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息,命中,pmp 检查正常,DataArray 或 MetaArray 的 ECC 校验错误。
|
||||
- 验证 io.error.valid 为高,且 io.error.bits 内容为对应的错误源和错误类型。
|
||||
- 先刷 MetaArray 的 ValidArray,给 MissUnit 发请求,由其在 L2 重填,阻塞至数据返回。
|
||||
- 验证 replacer.io.touch 的 vSetIdx 和 way 和 ftq 的 fetch 一致,missUnit.io.victim 的 vSetIdx 和 way 是按照制定的算法生成的。
|
||||
- 验证 io.fetch.resp 的数据应该是取指的数据。
|
||||
|
||||
4. 取指请求未命中,但是 exception 非 0(af、gpf、pf),无其他异常
|
||||
|
||||
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息,命中,pmp 检查未通过,DataArray 和 MetaArray 的 ECC 校验正常。
|
||||
- 验证 io.fetch.resp 为对应的错误源和错误类型。
|
||||
- 验证 io.fetch.resp 的数据无效,里面有异常类型。
|
||||
|
||||
5. 取指请求未命中,通过 WayLookup 中读取到的预取过来的 itlb 中返回 pbmt。
|
||||
|
||||
- 有 itlb_pbmt 和 pmp_mmio 时,他们合成 s1_mmio,传递到 s2_mmio,生成 s2_miss,有特殊情况就不会取指。
|
||||
- io.fetch.req.bits.pcMemRead 的 0-4 的 startAddr 和 nextlineStart 在正常地址范围内,从 WayLookup 获取信息,命中,pmp 检查通过,DataArray 和 MetaArray 的 ECC 校验正常。
|
||||
- 验证 io.fetch.resp 为对应的错误源和错误类型。
|
||||
- 验证 io.fetch.resp 的数据无效,里面有特殊情况类型类型。
|
||||
|
||||
6. 取指请求未命中,pmp 返回 mmio ,处理同 5。
|
||||
|
||||
### MetaArray 功能
|
||||
|
||||
在 IPrefetchPipe 的 S0,接收来自 IPrefetchPipe 的读请求 read,返回对应路和组的响应 readResp。
|
||||
在 miss 的时候,MissUnit 会将会应的数据写入 write 到 MetaArray。
|
||||
MetaArray 主要存储了每个 Cache 行的标签和 ECC 校验码。
|
||||
|
||||
1. 元数据写入操作(对应的 Set 已满): ICacheMetaArray 应当能够正确地将元数据(标签和有效位)写入到指定的 Set 和 Way 。
|
||||
|
||||
- 从 MissUnit 返回的请求都是未命中的请求(已命中不会向 MissUnit 请求,那么 MissUnit 自然也不会向 MetaArray 写入)。
|
||||
- 发送一个写请求 write 到 ICacheMetaArray,ICacheReplacer 根据 PLRU 替换策略指定 way,替换路被写入 waymask,最后指定 virIdx、phyTag、waymask、bankIdx、poison。
|
||||
- 写入操作后,发起一个对相同虚拟索引的读请求。验证 readResp 的 metas 和 codes 分别包含写入的 ptag 和 ecc code,并且对于写入的路,readResp.entryValid 信号被置为有效。
|
||||
|
||||
2. 元数据读取操作 (命中): 当一个读请求在 ICacheMetaArray 中命中时(存在有效的条目),它应该返回正确的元数据(标签和有效位)。
|
||||
|
||||
- 首先,向特定的虚拟索引(组和路)写入元数据(参照上面的写入操作)。然后,向相同的虚拟索引发送一个读请求。
|
||||
- 验证 readResp.metas 包含之前写入的物理标签,并且对于相应的路,readResp.entryValid 信号被置为有效。
|
||||
|
||||
3. 元数据读取操作 (未命中): 当读取一个尚未被写入的地址时,ICacheMetaArray 应当指示未命中(条目无效)。
|
||||
|
||||
- 向 ICacheMetaArray 发送一个读请求,请求的虚拟索引在复位后从未被写入过。
|
||||
- 验证对于任何路,readResp.entryValid 信号都没有被置为有效。 对应的 readResp.metas 和 codes 的内容是 DontCare 也就是 0。
|
||||
|
||||
4. 独立的缓存组刷新:在第 i 个端口是有效的刷新请求,并且该请求的 waymask 指定了当前正在处理的第 w 路时,应该使第 i 个端口的条目无效。
|
||||
|
||||
- 先向 ICacheMetaArray 写入指定一个或多个端口的元数据,然后再给对应的端口的路发送刷新请求 io.flush,其包含虚拟索引 virIdx 和路掩码 waymask。
|
||||
- 验证 valid_array 对应的路中的 virIdx 被置为无效,io.readResp.entryValid 对应路的对应端口为无效。
|
||||
|
||||
5. 全部刷新操作: ICacheMetaArray 应当能够在接收到全部刷新请求时,使所有条目无效。
|
||||
|
||||
- 先向多个不同的虚拟索引写入元数据。然后置位 io.flushAll 信号。
|
||||
- 验证步骤: 在 io.flushAll 信号置位后,发起对所有之前写入过的虚拟索引的读请求。验证在所有的读取响应中,对于任何路,readResp.entryValid 信号都没有被置为有效。
|
||||
|
||||
### DataArray 功能
|
||||
|
||||
与 MetaArray 类似,在 MainPipe 的 S0,接收来自 MainPipe 的读请求 read,返回对应路和组的响应 readResp。
|
||||
在 miss 的时候,MissUnit 会将会应的数据写入 write 到 DataArray。
|
||||
DataArray 主要存储了每个 Cache 行的标签和 ECC 校验码。
|
||||
|
||||
1. 数据写入操作(对应的 Set 已满): ICacheDataArray 应当能够正确地将数据写入到指定的 Set (组)、Way (路) 和数据 Bank (存储体)。
|
||||
|
||||
- 发送一个写请求 write 到 ICacheDataArray,ICacheReplacer 根据 PLRU 替换策略指定 way,替换路被写入 waymask,最终指定虚拟索引、数据、路掩码、存储体索引 bankIdx 和毒化位。写入的数据模式应跨越多个数据存储体。
|
||||
- 写入操作后,发起一个对相同虚拟索引和块偏移量的读请求。验证 readResp.datas 与写入的数据相匹配。
|
||||
|
||||
2. 数据读取操作 (命中): 当一个读请求命中时(相应的元数据有效),它应该从相应的组、路和数据存储体返回正确的数据。
|
||||
|
||||
- 首先,向特定的虚拟索引和块偏移量写入数据。然后,向相同的虚拟索引和块偏移量发送一个读请求。使用不同的块偏移量进行测试,以覆盖存储体的选择逻辑。
|
||||
- 验证 readResp.datas 包含之前写入的数据。
|
||||
|
||||
3. 数据读取操作 (未命中): 当读取一个尚未被写入的地址时,ICacheDataArray 的输出应该是默认值或无关值。
|
||||
|
||||
- 向 ICacheDataArray 发送一个读请求,请求的虚拟索引在复位后从未被写入过。
|
||||
- 验证 readResp.datas 为 0。
|
||||
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 580 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 266 KiB |
|
After Width: | Height: | Size: 512 KiB |
|
After Width: | Height: | Size: 333 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 411 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
|
@ -1,24 +0,0 @@
|
|||
#coding=utf8
|
||||
#***************************************************************************************
|
||||
# This project is licensed under Mulan PSL v2.
|
||||
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
# You may obtain a copy of Mulan PSL v2 at:
|
||||
# http://license.coscl.org.cn/MulanPSL2
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
#
|
||||
# See the Mulan PSL v2 for more details.
|
||||
#**************************************************************************************/
|
||||
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def build(cfg):
|
||||
return False
|
||||
|
||||
|
||||
def line_coverage_files(cfg):
|
||||
return []
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import os
|
||||
from comm import warning, info, get_all_rtl_files
|
||||
|
||||
|
||||
def build(cfg):
|
||||
from tempfile import NamedTemporaryFile
|
||||
from toffee_test.markers import match_version
|
||||
from comm import error, info, get_root_dir, exe_cmd
|
||||
|
||||
# check version
|
||||
if not match_version(cfg.rtl.version, "openxiangshan-kmh-*"):
|
||||
error(f"frontend_icache_ctrlunit: Unsupported RTL version {cfg.rtl.version}")
|
||||
return False
|
||||
|
||||
# find source files for ICacheCtrlUnit
|
||||
rtl_files = get_all_rtl_files("ICacheCtrlUnit", cfg=cfg)
|
||||
info(f"rtl_files: {rtl_files}")
|
||||
assert rtl_files, "Cannot find RTL files of Frontend.ICacheCtrlUnit"
|
||||
|
||||
# additional internal signal files
|
||||
internal_signals_path = os.path.join(
|
||||
get_root_dir("scripts/icache_related/icache_ctrlunit_internals.yaml")
|
||||
)
|
||||
# assert os.path.exists(internal_signals_path), "Cannot find internal signal files"
|
||||
|
||||
# export ICacheCtrlUnit.sv
|
||||
if not os.path.exists(get_root_dir("dut/ICacheCtrlUnit")):
|
||||
info("Exporting ICacheCtrlUnit.sv")
|
||||
with NamedTemporaryFile("w+", encoding="utf-8", suffix=".txt") as filelist:
|
||||
filelist.write("\n".join(rtl_files))
|
||||
filelist.flush()
|
||||
s, _, err = exe_cmd(
|
||||
f"picker export --cp_lib false {rtl_files[0]} --fs {filelist.name} --lang python --tdir "
|
||||
f"{get_root_dir('dut')}/ -w ICacheCtrlUnit.fst -c --internal={internal_signals_path}"
|
||||
)
|
||||
assert s, err
|
||||
return True
|
||||
|
||||
|
||||
def line_coverage_files(cfg):
|
||||
return ["ICacheCtrlUnit.v"]
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import os
|
||||
from comm import warning, info, get_all_rtl_files
|
||||
|
||||
|
||||
def build(cfg):
|
||||
from tempfile import NamedTemporaryFile
|
||||
from toffee_test.markers import match_version
|
||||
from comm import error, info, get_root_dir, exe_cmd
|
||||
|
||||
# check version
|
||||
if not match_version(cfg.rtl.version, "openxiangshan-kmh-*"):
|
||||
error(f"frontend_icache: Unsupported RTL version {cfg.rtl.version}")
|
||||
return False
|
||||
|
||||
# find source files for ICache
|
||||
rtl_files = get_all_rtl_files("ICache", cfg=cfg)
|
||||
info(f"rtl_files: {rtl_files}")
|
||||
assert rtl_files, "Cannot find RTL files of Frontend.ICache"
|
||||
|
||||
# additional internal signal files
|
||||
internal_signals_path = os.path.join(
|
||||
get_root_dir("scripts/icache_related/icache_icache_internals.yaml")
|
||||
)
|
||||
# assert os.path.exists(internal_signals_path), "Cannot find internal signal files"
|
||||
|
||||
# export ICache.sv
|
||||
if not os.path.exists(get_root_dir("dut/ICache")):
|
||||
info("Exporting ICache.sv")
|
||||
with NamedTemporaryFile("w+", encoding="utf-8", suffix=".txt") as filelist:
|
||||
filelist.write("\n".join(rtl_files))
|
||||
filelist.flush()
|
||||
s, _, err = exe_cmd(
|
||||
f"picker export --cp_lib false {rtl_files[0]} --fs {filelist.name} --lang python --tdir "
|
||||
f"{get_root_dir('dut')}/ -w ICache.fst -c --internal={internal_signals_path}"
|
||||
)
|
||||
assert s, err
|
||||
return True
|
||||
|
||||
|
||||
def line_coverage_files(cfg):
|
||||
return ["ICache.v"]
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import os
|
||||
from comm import warning, info, get_all_rtl_files
|
||||
|
||||
|
||||
def build(cfg):
|
||||
from tempfile import NamedTemporaryFile
|
||||
from toffee_test.markers import match_version
|
||||
from comm import error, info, get_root_dir, exe_cmd
|
||||
|
||||
# check version
|
||||
if not match_version(cfg.rtl.version, "openxiangshan-kmh-*"):
|
||||
error(f"frontend_icache_iprefetchpipe: Unsupported RTL version {cfg.rtl.version}")
|
||||
return False
|
||||
|
||||
# find source files for IPrefetchPipe
|
||||
rtl_files = get_all_rtl_files("IPrefetchPipe", cfg=cfg)
|
||||
info(f"rtl_files: {rtl_files}")
|
||||
assert rtl_files, "Cannot find RTL files of Frontend.IPrefetchPipe"
|
||||
|
||||
# additional internal signal files
|
||||
internal_signals_path = os.path.join(get_root_dir("scripts/icache_related/icache_iprefetchpipe_internals.yaml"))
|
||||
# assert os.path.exists(internal_signals_path), "Cannot find internal signal files"
|
||||
|
||||
# export IPrefetchPipe.sv
|
||||
if not os.path.exists(get_root_dir("dut/IPrefetchPipe")):
|
||||
info("Exporting IPrefetchPipe.sv")
|
||||
with NamedTemporaryFile("w+", encoding="utf-8", suffix=".txt") as filelist:
|
||||
filelist.write("\n".join(rtl_files))
|
||||
filelist.flush()
|
||||
s, _, err = exe_cmd(
|
||||
f"picker export --cp_lib false {rtl_files[0]} --fs {filelist.name} --lang python --tdir "
|
||||
f"{get_root_dir('dut')}/ -w IPrefetchPipe.fst -c --internal={internal_signals_path}"
|
||||
)
|
||||
assert s, err
|
||||
return True
|
||||
|
||||
|
||||
def line_coverage_files(cfg):
|
||||
return ["IPrefetchPipe.v"]
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import os
|
||||
from comm import warning, info, get_all_rtl_files
|
||||
|
||||
|
||||
def build(cfg):
|
||||
from tempfile import NamedTemporaryFile
|
||||
from toffee_test.markers import match_version
|
||||
from comm import error, info, get_root_dir, exe_cmd
|
||||
|
||||
# check version
|
||||
if not match_version(cfg.rtl.version, "openxiangshan-kmh-*"):
|
||||
error(f"frontend_icache_mainpipe: Unsupported RTL version {cfg.rtl.version}")
|
||||
return False
|
||||
|
||||
# find source files for ICacheMainPipe
|
||||
rtl_files = get_all_rtl_files("ICacheMainPipe", cfg=cfg)
|
||||
info(f"rtl_files: {rtl_files}")
|
||||
assert rtl_files, "Cannot find RTL files of Frontend.ICacheMainPipe"
|
||||
|
||||
# additional internal signal files
|
||||
internal_signals_path = os.path.join(
|
||||
get_root_dir("scripts/icache_related/icache_mainpipe_internals.yaml")
|
||||
)
|
||||
# assert os.path.exists(internal_signals_path), "Cannot find internal signal files"
|
||||
|
||||
# export ICacheMainPipe.sv
|
||||
if not os.path.exists(get_root_dir("dut/ICacheMainPipe")):
|
||||
info("Exporting ICacheMainPipe.sv")
|
||||
with NamedTemporaryFile("w+", encoding="utf-8", suffix=".txt") as filelist:
|
||||
filelist.write("\n".join(rtl_files))
|
||||
filelist.flush()
|
||||
s, _, err = exe_cmd(
|
||||
f"picker export --cp_lib false {rtl_files[0]} --fs {filelist.name} --lang python --tdir "
|
||||
f"{get_root_dir('dut')}/ -w ICacheMainPipe.fst -c --internal={internal_signals_path}"
|
||||
)
|
||||
|
||||
assert s, err
|
||||
return True
|
||||
|
||||
|
||||
def line_coverage_files(cfg):
|
||||
return ["ICacheMainPipe.v"]
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import os
|
||||
from comm import warning, info, get_all_rtl_files
|
||||
|
||||
|
||||
def build(cfg):
|
||||
from tempfile import NamedTemporaryFile
|
||||
from toffee_test.markers import match_version
|
||||
from comm import error, info, get_root_dir, exe_cmd
|
||||
|
||||
# check version
|
||||
if not match_version(cfg.rtl.version, "openxiangshan-kmh-*"):
|
||||
error(f"frontend_icache_missunit: Unsupported RTL version {cfg.rtl.version}")
|
||||
return False
|
||||
|
||||
# find source files for ICacheMissUnit
|
||||
rtl_files = get_all_rtl_files("ICacheMissUnit", cfg=cfg)
|
||||
info(f"rtl_files: {rtl_files}")
|
||||
assert rtl_files, "Cannot find RTL files of Frontend.ICacheMissUnit"
|
||||
|
||||
# additional internal signal files
|
||||
internal_signals_path = os.path.join(
|
||||
get_root_dir("scripts/icache_related/icache_missunit_internals.yaml")
|
||||
)
|
||||
# assert os.path.exists(internal_signals_path), "Cannot find internal signal files"
|
||||
|
||||
# export ICacheMissUnit.sv
|
||||
if not os.path.exists(get_root_dir("dut/ICacheMissUnit")):
|
||||
info("Exporting ICacheMissUnit.sv")
|
||||
with NamedTemporaryFile("w+", encoding="utf-8", suffix=".txt") as filelist:
|
||||
filelist.write("\n".join(rtl_files))
|
||||
filelist.flush()
|
||||
s, _, err = exe_cmd(
|
||||
f"picker export --cp_lib false {rtl_files[0]} --fs {filelist.name} --lang python --tdir "
|
||||
f"{get_root_dir('dut')}/ -w ICacheMissUnit.fst -c --internal={internal_signals_path}"
|
||||
)
|
||||
assert s, err
|
||||
return True
|
||||
|
||||
|
||||
def line_coverage_files(cfg):
|
||||
return ["ICacheMissUnit.v"]
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import os
|
||||
from comm import warning, info, get_all_rtl_files
|
||||
|
||||
|
||||
def build(cfg):
|
||||
from tempfile import NamedTemporaryFile
|
||||
from toffee_test.markers import match_version
|
||||
from comm import error, info, get_root_dir, exe_cmd
|
||||
|
||||
# check version
|
||||
if not match_version(cfg.rtl.version, "openxiangshan-kmh-*"):
|
||||
error(f"frontend_icache_waylookup: Unsupported RTL version {cfg.rtl.version}")
|
||||
return False
|
||||
|
||||
# find source files for WayLookup
|
||||
rtl_files = get_all_rtl_files("WayLookup", cfg=cfg)
|
||||
info(f"rtl_files: {rtl_files}")
|
||||
assert rtl_files, "Cannot find RTL files of Frontend.WayLookup"
|
||||
|
||||
# additional internal signal files
|
||||
internal_signals_path = os.path.join(
|
||||
get_root_dir("scripts/icache_related/icache_waylookup_internals.yaml")
|
||||
)
|
||||
# assert os.path.exists(internal_signals_path), "Cannot find internal signal files"
|
||||
|
||||
# export WayLookup.sv
|
||||
if not os.path.exists(get_root_dir("dut/WayLookup")):
|
||||
info("Exporting WayLookup.sv")
|
||||
with NamedTemporaryFile("w+", encoding="utf-8", suffix=".txt") as filelist:
|
||||
filelist.write("\n".join(rtl_files))
|
||||
filelist.flush()
|
||||
s, _, err = exe_cmd(
|
||||
f"picker export --cp_lib false {rtl_files[0]} --fs {filelist.name} --lang python --tdir "
|
||||
f"{get_root_dir('dut')}/ -w WayLookup.fst -c --internal={internal_signals_path}"
|
||||
# f"{get_root_dir('dut')}/ -w WayLookup.fst -c "
|
||||
)
|
||||
assert s, err
|
||||
return True
|
||||
|
||||
|
||||
def line_coverage_files(cfg):
|
||||
return ["WayLookup.v"]
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
ICacheCtrlUnit:
|
||||
- "wire io_injecting_0"
|
||||
- "wire eccctrl_istatus"
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
ICache:
|
||||
- "wire _metaArray_io_read_ready"
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
IPrefetchPipe:
|
||||
- "wire s1_flush"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
ICacheMainPipe:
|
||||
- "wire s2_fire"
|
||||
- "wire _toMSHRArbiter_io_in_0_valid_T_4"
|
||||
- "wire _toMSHRArbiter_io_in_1_valid_T_4"
|
||||
- "reg s2_valid"
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
ICacheMissUnit:
|
||||
- "wire _prefetchMSHRs_9_io_req_ready"
|
||||
- "wire _prefetchMSHRs_9_io_acquire_valid"
|
||||
- "wire _prefetchMSHRs_8_io_req_ready"
|
||||
- "wire _prefetchMSHRs_8_io_acquire_valid"
|
||||
- "wire _prefetchMSHRs_7_io_req_ready"
|
||||
- "wire _prefetchMSHRs_7_io_acquire_valid"
|
||||
- "wire _prefetchMSHRs_6_io_req_ready"
|
||||
- "wire _prefetchMSHRs_6_io_acquire_valid"
|
||||
- "wire _prefetchMSHRs_5_io_req_ready"
|
||||
- "wire _prefetchMSHRs_5_io_acquire_valid"
|
||||
- "wire _prefetchMSHRs_4_io_req_ready"
|
||||
- "wire _prefetchMSHRs_4_io_acquire_valid"
|
||||
- "wire _prefetchMSHRs_3_io_req_ready"
|
||||
- "wire _prefetchMSHRs_3_io_acquire_valid"
|
||||
- "wire _prefetchMSHRs_2_io_req_ready"
|
||||
- "wire _prefetchMSHRs_2_io_acquire_valid"
|
||||
- "wire _prefetchMSHRs_1_io_req_ready"
|
||||
- "wire _prefetchMSHRs_1_io_acquire_valid"
|
||||
- "wire _prefetchMSHRs_0_io_req_ready"
|
||||
- "wire _prefetchMSHRs_0_io_acquire_valid"
|
||||
|
||||
- "wire _fetchMSHRs_3_io_req_ready"
|
||||
- "wire _fetchMSHRs_3_io_acquire_valid"
|
||||
- "wire _fetchMSHRs_2_io_req_ready"
|
||||
- "wire _fetchMSHRs_2_io_acquire_valid"
|
||||
- "wire _fetchMSHRs_1_io_req_ready"
|
||||
- "wire _fetchMSHRs_1_io_acquire_valid"
|
||||
- "wire _fetchMSHRs_0_io_req_ready"
|
||||
- "wire _fetchMSHRs_0_io_acquire_valid"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
WayLookup:
|
||||
- "reg readPtr_flag"
|
||||
- "reg readPtr_value"
|
||||
- "reg writePtr_flag"
|
||||
- "reg writePtr_value"
|
||||
- "wire io_write_ready_0"
|
||||
- "reg entries_30_waymask_0"
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .ctrlunit_agent import CtrlUnitAgent
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
from toffee import Agent
|
||||
from..bundle import CtrlUnitBundle
|
||||
|
||||
class CtrlUnitAgent(Agent):
|
||||
def __init__(self, bundle: CtrlUnitBundle):
|
||||
super().__init__(bundle)
|
||||
bundle.set_all(0)
|
||||
self.bundle = bundle
|
||||
|
||||
async def set_opcode(self, set_value):
|
||||
self.bundle.reset.value = 1
|
||||
await self.bundle.step()
|
||||
self.bundle.reset.value = 0
|
||||
await self.bundle.step()
|
||||
self.bundle.auto_in._a._valid.value = 1
|
||||
print("\nBefore setting, auto_in_a_bits_opcode is: ",self.bundle.auto_in._a._bits._opcode.value)
|
||||
self.bundle.auto_in._a._bits._opcode.value = set_value
|
||||
print("After setting, auto_in_a_bits_opcode is :",self.bundle.auto_in._a._bits._opcode.value)
|
||||
await self.bundle.step(10)
|
||||
print("Output auto_in_d_bits_opcode is :",self.bundle.auto_in._d._bits._opcode.value)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .ctrlunit_bundle import CtrlUnitBundle
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
from toffee import Bundle, Signals, Signal
|
||||
|
||||
class _0Bundle(Bundle):
|
||||
_0 = Signal()
|
||||
|
||||
class _1Bundle(Bundle):
|
||||
_io_injecting = _0Bundle.from_prefix("_io_injecting")
|
||||
_eccctrl_istatus = Signal()
|
||||
|
||||
class _2Bundle(Bundle):
|
||||
_address, _opcode, _mask, _data, _size, _source = Signals(6)
|
||||
|
||||
class _3Bundle(Bundle):
|
||||
_bits = _2Bundle.from_prefix("_bits")
|
||||
_ready, _valid = Signals(2)
|
||||
|
||||
class _4Bundle(Bundle):
|
||||
_opcode, _size, _data, _source = Signals(4)
|
||||
|
||||
class _5Bundle(Bundle):
|
||||
_bits = _4Bundle.from_prefix("_bits")
|
||||
_ready, _valid = Signals(2)
|
||||
|
||||
class _6Bundle(Bundle):
|
||||
_a = _3Bundle.from_prefix("_a")
|
||||
_d = _5Bundle.from_prefix("_d")
|
||||
|
||||
class _7Bundle(Bundle):
|
||||
_waymask, _virIdx = Signals(2)
|
||||
|
||||
class _8Bundle(Bundle):
|
||||
_bits = _7Bundle.from_prefix("_bits")
|
||||
_ready, _valid = Signals(2)
|
||||
|
||||
class _9Bundle(Bundle):
|
||||
_1, _0 = Signals(2)
|
||||
|
||||
class _10Bundle(Bundle):
|
||||
_bits_vSetIdx = _9Bundle.from_prefix("_bits_vSetIdx")
|
||||
_ready, _valid = Signals(2)
|
||||
|
||||
class _11Bundle(Bundle):
|
||||
_1, _2, _0, _3 = Signals(4)
|
||||
|
||||
class _12Bundle(Bundle):
|
||||
_0 = _11Bundle.from_prefix("_0")
|
||||
|
||||
class _13Bundle(Bundle):
|
||||
_tag = Signal()
|
||||
|
||||
class _14Bundle(Bundle):
|
||||
_1 = _13Bundle.from_prefix("_1")
|
||||
_2 = _13Bundle.from_prefix("_2")
|
||||
_3 = _13Bundle.from_prefix("_3")
|
||||
_0 = _13Bundle.from_prefix("_0")
|
||||
|
||||
class _15Bundle(Bundle):
|
||||
_0 = _14Bundle.from_prefix("_0")
|
||||
|
||||
class _16Bundle(Bundle):
|
||||
_entryValid = _12Bundle.from_prefix("_entryValid")
|
||||
_metas = _15Bundle.from_prefix("_metas")
|
||||
|
||||
class _17Bundle(Bundle):
|
||||
_phyTag, _waymask, _virIdx, _bankIdx = Signals(4)
|
||||
|
||||
class _18Bundle(Bundle):
|
||||
_bits = _17Bundle.from_prefix("_bits")
|
||||
_ready, _valid = Signals(2)
|
||||
|
||||
class _19Bundle(Bundle):
|
||||
_dataWrite = _8Bundle.from_prefix("_dataWrite")
|
||||
_metaRead = _10Bundle.from_prefix("_metaRead")
|
||||
_metaReadResp = _16Bundle.from_prefix("_metaReadResp")
|
||||
_metaWrite = _18Bundle.from_prefix("_metaWrite")
|
||||
_ecc_enable, _injecting = Signals(2)
|
||||
|
||||
class CtrlUnitBundle(Bundle):
|
||||
io = _19Bundle.from_prefix("io")
|
||||
ICacheCtrlUnit = _1Bundle.from_prefix("ICacheCtrlUnit")
|
||||
auto_in = _6Bundle.from_prefix("auto_in")
|
||||
clock, reset = Signals(2)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .ctrlunit_env import CtrlUnitEnv
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
from toffee import Env
|
||||
from dut.ICacheCtrlUnit import DUTICacheCtrlUnit
|
||||
from ..agent import CtrlUnitAgent
|
||||
from ..bundle import CtrlUnitBundle
|
||||
|
||||
|
||||
class CtrlUnitEnv(Env):
|
||||
def __init__(self, dut: DUTICacheCtrlUnit):
|
||||
super().__init__()
|
||||
self.dut = dut
|
||||
self.bundle = CtrlUnitBundle.from_prefix("").bind(dut)
|
||||
self.agent = CtrlUnitAgent(self.bundle)
|
||||
self.bundle.set_all(0)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import asyncio
|
||||
import toffee
|
||||
import toffee_test
|
||||
from toffee import start_clock
|
||||
from dut.ICacheCtrlUnit import DUTICacheCtrlUnit
|
||||
from ..env import CtrlUnitEnv
|
||||
|
||||
|
||||
@toffee_test.fixture
|
||||
async def ctrlunit_env(toffee_request: toffee_test.ToffeeRequest):
|
||||
toffee.setup_logging(toffee.ERROR)
|
||||
dut = toffee_request.create_dut(DUTICacheCtrlUnit)
|
||||
dut.InitClock("clock")
|
||||
start_clock(dut)
|
||||
ctrlunit_env = CtrlUnitEnv(dut)
|
||||
yield ctrlunit_env
|
||||
|
||||
cur_loop = asyncio.get_event_loop()
|
||||
for task in asyncio.all_tasks(cur_loop):
|
||||
if task.get_name() == "__clock_loop":
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
from .ctrlunit_fixture import ctrlunit_env
|
||||
from ..env import CtrlUnitEnv
|
||||
import toffee_test
|
||||
|
||||
|
||||
@toffee_test.testcase
|
||||
async def test_smoke(ctrlunit_env: CtrlUnitEnv):
|
||||
|
||||
|
||||
await ctrlunit_env.agent.set_opcode(4)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .icache_agent import ICacheAgent
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
from toffee import Agent
|
||||
from ..bundle import ICacheBundle
|
||||
|
||||
|
||||
class ICacheAgent(Agent):
|
||||
def __init__(self, bundle: ICacheBundle):
|
||||
super().__init__(bundle)
|
||||
self.bundle = bundle
|
||||
|
||||
async def fencei_meta_array_func(self, value):
|
||||
|
||||
print(
|
||||
f"\nBefore setting fencei: ICacheMetaArray.io_read_ready.value is:",
|
||||
self.bundle.ICache__metaArray_io_read_ready.value,
|
||||
)
|
||||
|
||||
self.bundle.io._fencei.value = value
|
||||
await self.bundle.step()
|
||||
|
||||
print(
|
||||
f"\nAfter setting fencei = {value}: ICacheMetaArray.io_read_ready.value is:",
|
||||
self.bundle.ICache__metaArray_io_read_ready.value,
|
||||
)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .icache_bundle import ICacheBundle
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
from toffee import Bundle, Signals, Signal
|
||||
|
||||
class _0Bundle(Bundle):
|
||||
_address, _source = Signals(2)
|
||||
|
||||
class _1Bundle(Bundle):
|
||||
_bits = _0Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
class _2Bundle(Bundle):
|
||||
_size, _data, _source, _opcode, _corrupt = Signals(5)
|
||||
|
||||
class _3Bundle(Bundle):
|
||||
_bits = _2Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
class _4Bundle(Bundle):
|
||||
_a = _1Bundle.from_prefix("_a")
|
||||
_d = _3Bundle.from_prefix("_d")
|
||||
|
||||
class _5Bundle(Bundle):
|
||||
_size, _mask, _data, _source, _opcode, _address = Signals(6)
|
||||
|
||||
class _6Bundle(Bundle):
|
||||
_bits = _5Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
class _7Bundle(Bundle):
|
||||
_opcode, _source, _size, _data = Signals(4)
|
||||
|
||||
class _8Bundle(Bundle):
|
||||
_bits = _7Bundle.from_prefix("_bits")
|
||||
_ready, _valid = Signals(2)
|
||||
|
||||
class _9Bundle(Bundle):
|
||||
_d = _8Bundle.from_prefix("_d")
|
||||
_a = _6Bundle.from_prefix("_a")
|
||||
|
||||
class _10Bundle(Bundle):
|
||||
_ctrlUnitOpt_in = _9Bundle.from_prefix("_ctrlUnitOpt_in")
|
||||
_client_out = _4Bundle.from_prefix("_client_out")
|
||||
|
||||
class _11Bundle(Bundle):
|
||||
_addr_rd, _indata, _writeen, _ack, _readen, _be, _all, _outdata, _array, _req = Signals(10)
|
||||
|
||||
class _12Bundle(Bundle):
|
||||
_1 = _11Bundle.from_prefix("_1")
|
||||
_3 = _11Bundle.from_prefix("_3")
|
||||
_2 = _11Bundle.from_prefix("_2")
|
||||
_4 = _11Bundle.from_prefix("_4")
|
||||
_addr_rd, _indata, _writeen, _ack, _readen, _be, _all, _outdata, _array, _req = Signals(10)
|
||||
|
||||
class _13Bundle(Bundle):
|
||||
_report_to_beu, _paddr = Signals(2)
|
||||
|
||||
class _14Bundle(Bundle):
|
||||
_bits = _13Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
class _15Bundle(Bundle):
|
||||
_startAddr, _nextlineStart = Signals(2)
|
||||
|
||||
class _16Bundle(Bundle):
|
||||
_3 = _15Bundle.from_prefix("_3")
|
||||
_2 = _15Bundle.from_prefix("_2")
|
||||
_1 = _15Bundle.from_prefix("_1")
|
||||
_4 = _15Bundle.from_prefix("_4")
|
||||
_0 = _15Bundle.from_prefix("_0")
|
||||
|
||||
class _17Bundle(Bundle):
|
||||
_0, _3, _1, _4, _2 = Signals(5)
|
||||
|
||||
class _18Bundle(Bundle):
|
||||
_pcMemRead = _16Bundle.from_prefix("_pcMemRead")
|
||||
_readValid = _17Bundle.from_prefix("_readValid")
|
||||
_backendException = Signal()
|
||||
|
||||
class _19Bundle(Bundle):
|
||||
_bits = _18Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
class _20Bundle(Bundle):
|
||||
_0, _1 = Signals(2)
|
||||
|
||||
class _21Bundle(Bundle):
|
||||
_0 = Signal()
|
||||
|
||||
class _22Bundle(Bundle):
|
||||
_exception = _20Bundle.from_prefix("_exception")
|
||||
_itlb_pbmt = _20Bundle.from_prefix("_itlb_pbmt")
|
||||
_pmp_mmio = _20Bundle.from_prefix("_pmp_mmio")
|
||||
_vaddr = _20Bundle.from_prefix("_vaddr")
|
||||
_paddr = _21Bundle.from_prefix("_paddr")
|
||||
_data, _gpaddr, _backendException, _isForVSnonLeafPTE, _doubleline = Signals(5)
|
||||
|
||||
class _23Bundle(Bundle):
|
||||
_bits = _22Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
class _24Bundle(Bundle):
|
||||
_req = _19Bundle.from_prefix("_req")
|
||||
_resp = _23Bundle.from_prefix("_resp")
|
||||
_topdownItlbMiss, _topdownIcacheMiss = Signals(2)
|
||||
|
||||
class _25Bundle(Bundle):
|
||||
_value, _flag = Signals(2)
|
||||
|
||||
class _26Bundle(Bundle):
|
||||
_bits = _25Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
class _27Bundle(Bundle):
|
||||
_s3 = _26Bundle.from_prefix("_s3")
|
||||
_s2 = _26Bundle.from_prefix("_s2")
|
||||
|
||||
class _28Bundle(Bundle):
|
||||
_ftqIdx = _25Bundle.from_prefix("_ftqIdx")
|
||||
_startAddr, _nextlineStart = Signals(2)
|
||||
|
||||
class _29Bundle(Bundle):
|
||||
_bits = _28Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
class _30Bundle(Bundle):
|
||||
_req = _29Bundle.from_prefix("_req")
|
||||
_flushFromBpu = _27Bundle.from_prefix("_flushFromBpu")
|
||||
_backendException = Signal()
|
||||
|
||||
class _31Bundle(Bundle):
|
||||
_valid, _bits_vaddr = Signals(2)
|
||||
|
||||
class _32Bundle(Bundle):
|
||||
_gpf_instr, _pf_instr, _af_instr = Signals(3)
|
||||
|
||||
class _33Bundle(Bundle):
|
||||
_0 = _32Bundle.from_prefix("_0")
|
||||
|
||||
class _34Bundle(Bundle):
|
||||
_pbmt = _21Bundle.from_prefix("_pbmt")
|
||||
_paddr = _21Bundle.from_prefix("_paddr")
|
||||
_gpaddr = _21Bundle.from_prefix("_gpaddr")
|
||||
_excp = _33Bundle.from_prefix("_excp")
|
||||
_miss, _isForVSnonLeafPTE = Signals(2)
|
||||
|
||||
class _35Bundle(Bundle):
|
||||
_req = _31Bundle.from_prefix("_req")
|
||||
_resp_bits = _34Bundle.from_prefix("_resp_bits")
|
||||
|
||||
class _36Bundle(Bundle):
|
||||
_1 = _35Bundle.from_prefix("_1")
|
||||
_0 = _35Bundle.from_prefix("_0")
|
||||
|
||||
class _37Bundle(Bundle):
|
||||
_1 = Signal()
|
||||
|
||||
class _38Bundle(Bundle):
|
||||
_miss = _37Bundle.from_prefix("_miss")
|
||||
_except = _37Bundle.from_prefix("_except")
|
||||
_hit = _37Bundle.from_prefix("_hit")
|
||||
|
||||
class _39Bundle(Bundle):
|
||||
_0 = _38Bundle.from_prefix("_0")
|
||||
|
||||
class _40Bundle(Bundle):
|
||||
_hit, _miss = Signals(2)
|
||||
|
||||
class _41Bundle(Bundle):
|
||||
_0 = _40Bundle.from_prefix("_0")
|
||||
|
||||
class _42Bundle(Bundle):
|
||||
_bank_hit = _20Bundle.from_prefix("_bank_hit")
|
||||
_only = _41Bundle.from_prefix("_only")
|
||||
_miss = _39Bundle.from_prefix("_miss")
|
||||
_except = _21Bundle.from_prefix("_except")
|
||||
_hit = Signal()
|
||||
|
||||
class _43Bundle(Bundle):
|
||||
_instr, _mmio = Signals(2)
|
||||
|
||||
class _44Bundle(Bundle):
|
||||
_resp = _43Bundle.from_prefix("_resp")
|
||||
_req_bits_addr = Signal()
|
||||
|
||||
class _45Bundle(Bundle):
|
||||
_1 = _44Bundle.from_prefix("_1")
|
||||
_3 = _44Bundle.from_prefix("_3")
|
||||
_2 = _44Bundle.from_prefix("_2")
|
||||
_0 = _44Bundle.from_prefix("_0")
|
||||
|
||||
class _46Bundle(Bundle):
|
||||
_1 = _31Bundle.from_prefix("_1")
|
||||
_2 = _31Bundle.from_prefix("_2")
|
||||
_0 = _31Bundle.from_prefix("_0")
|
||||
|
||||
class _47Bundle(Bundle):
|
||||
_error = _14Bundle.from_prefix("_error")
|
||||
_pmp = _45Bundle.from_prefix("_pmp")
|
||||
_itlb = _36Bundle.from_prefix("_itlb")
|
||||
_perfInfo = _42Bundle.from_prefix("_perfInfo")
|
||||
_fetch = _24Bundle.from_prefix("_fetch")
|
||||
_softPrefetch = _46Bundle.from_prefix("_softPrefetch")
|
||||
_ftqPrefetch = _30Bundle.from_prefix("_ftqPrefetch")
|
||||
_fencei, _flush, _toIFU, _hartId, _stop, _csr_pf_enable, _itlbFlushPipe = Signals(7)
|
||||
|
||||
class ICacheBundle(Bundle):
|
||||
io = _47Bundle.from_prefix("io")
|
||||
boreChildrenBd_bore = _12Bundle.from_prefix("boreChildrenBd_bore")
|
||||
auto = _10Bundle.from_prefix("auto")
|
||||
reset, ICache__metaArray_io_read_ready, clock = Signals(3)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .icache_env import ICacheEnv
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
from toffee import Env
|
||||
from dut.ICache import DUTICache
|
||||
from ..agent import ICacheAgent
|
||||
from ..bundle import ICacheBundle
|
||||
|
||||
|
||||
class ICacheEnv(Env):
|
||||
def __init__(self, dut: DUTICache):
|
||||
super().__init__()
|
||||
self.dut = dut
|
||||
self.bundle = ICacheBundle.from_prefix("").bind(dut)
|
||||
self.agent = ICacheAgent(self.bundle)
|
||||
self.bundle.set_all(0)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import asyncio
|
||||
import toffee
|
||||
import toffee_test
|
||||
from toffee import start_clock
|
||||
from dut.ICache import DUTICache
|
||||
from ..env import ICacheEnv
|
||||
|
||||
|
||||
@toffee_test.fixture
|
||||
async def icache_env(toffee_request: toffee_test.ToffeeRequest):
|
||||
toffee.setup_logging(toffee.ERROR)
|
||||
dut = toffee_request.create_dut(DUTICache)
|
||||
dut.InitClock("clock")
|
||||
start_clock(dut)
|
||||
icache_env = ICacheEnv(dut)
|
||||
yield icache_env
|
||||
|
||||
cur_loop = asyncio.get_event_loop()
|
||||
for task in asyncio.all_tasks(cur_loop):
|
||||
if task.get_name() == "__clock_loop":
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from .icache_fixture import icache_env
|
||||
from ..env import ICacheEnv
|
||||
import toffee_test
|
||||
|
||||
|
||||
@toffee_test.testcase
|
||||
async def test_smoke(icache_env: ICacheEnv):
|
||||
await icache_env.agent.fencei_meta_array_func(1)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .iprefetchpipe_agent import IPrefetchPipeAgent
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
from toffee import Agent
|
||||
from ..bundle import IPrefetchPipeBundle
|
||||
|
||||
|
||||
class IPrefetchPipeAgent(Agent):
|
||||
def __inti__(self, bundle: IPrefetchPipeBundle):
|
||||
super().__init__(bundle)
|
||||
bundle.set_all(0)
|
||||
self.bundle = bundle
|
||||
|
||||
async def set_s1_flush(self):
|
||||
self.bundle.reset.value = 1
|
||||
await self.bundle.step()
|
||||
self.bundle.reset.value = 0
|
||||
await self.bundle.step()
|
||||
|
||||
print(
|
||||
"\nBefore setting, s1_flush is: ", self.bundle.IPrefetchPipe_s1_flush.value
|
||||
)
|
||||
self.bundle.io._flush.value = 1
|
||||
await self.bundle.step()
|
||||
print("After setting, s1_flush is: ", self.bundle.IPrefetchPipe_s1_flush.value)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .iprefetchpipe_bundle import IPrefetchPipeBundle
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
from toffee import Bundle, Signals, Signal
|
||||
|
||||
|
||||
class _0Bundle(Bundle):
|
||||
_vSetIdx, _blkPaddr = Signals(2)
|
||||
|
||||
|
||||
class _1Bundle(Bundle):
|
||||
_bits = _0Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
|
||||
class _2Bundle(Bundle):
|
||||
_vSetIdx, _corrupt, _blkPaddr, _waymask = Signals(4)
|
||||
|
||||
|
||||
class _3Bundle(Bundle):
|
||||
_bits = _2Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _4Bundle(Bundle):
|
||||
_flag, _value = Signals(2)
|
||||
|
||||
|
||||
class _5Bundle(Bundle):
|
||||
_bits = _4Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _6Bundle(Bundle):
|
||||
_s3 = _5Bundle.from_prefix("_s3")
|
||||
_s2 = _5Bundle.from_prefix("_s2")
|
||||
|
||||
|
||||
class _7Bundle(Bundle):
|
||||
_bits_vaddr, _valid = Signals(2)
|
||||
|
||||
|
||||
class _8Bundle(Bundle):
|
||||
_pf_instr, _af_instr, _gpf_instr = Signals(3)
|
||||
|
||||
|
||||
class _9Bundle(Bundle):
|
||||
_0 = _8Bundle.from_prefix("_0")
|
||||
|
||||
|
||||
class _10Bundle(Bundle):
|
||||
_0 = Signal()
|
||||
|
||||
|
||||
class _11Bundle(Bundle):
|
||||
_pbmt = _10Bundle.from_prefix("_pbmt")
|
||||
_gpaddr = _10Bundle.from_prefix("_gpaddr")
|
||||
_paddr = _10Bundle.from_prefix("_paddr")
|
||||
_excp = _9Bundle.from_prefix("_excp")
|
||||
_isForVSnonLeafPTE, _miss = Signals(2)
|
||||
|
||||
|
||||
class _12Bundle(Bundle):
|
||||
_resp_bits = _11Bundle.from_prefix("_resp_bits")
|
||||
_req = _7Bundle.from_prefix("_req")
|
||||
|
||||
|
||||
class _13Bundle(Bundle):
|
||||
_1 = _12Bundle.from_prefix("_1")
|
||||
_0 = _12Bundle.from_prefix("_0")
|
||||
|
||||
|
||||
class _14Bundle(Bundle):
|
||||
_0, _1, _3, _2 = Signals(4)
|
||||
|
||||
|
||||
class _15Bundle(Bundle):
|
||||
_0 = _14Bundle.from_prefix("_0")
|
||||
_1 = _14Bundle.from_prefix("_1")
|
||||
|
||||
|
||||
class _16Bundle(Bundle):
|
||||
_tag = Signal()
|
||||
|
||||
|
||||
class _17Bundle(Bundle):
|
||||
_1 = _16Bundle.from_prefix("_1")
|
||||
_0 = _16Bundle.from_prefix("_0")
|
||||
_2 = _16Bundle.from_prefix("_2")
|
||||
_3 = _16Bundle.from_prefix("_3")
|
||||
|
||||
|
||||
class _18Bundle(Bundle):
|
||||
_1 = _17Bundle.from_prefix("_1")
|
||||
_0 = _17Bundle.from_prefix("_0")
|
||||
|
||||
|
||||
class _19Bundle(Bundle):
|
||||
_entryValid = _15Bundle.from_prefix("_entryValid")
|
||||
_codes = _15Bundle.from_prefix("_codes")
|
||||
_metas = _18Bundle.from_prefix("_metas")
|
||||
|
||||
|
||||
class _20Bundle(Bundle):
|
||||
_0, _1 = Signals(2)
|
||||
|
||||
|
||||
class _21Bundle(Bundle):
|
||||
_vSetIdx = _20Bundle.from_prefix("_vSetIdx")
|
||||
_isDoubleLine = Signal()
|
||||
|
||||
|
||||
class _22Bundle(Bundle):
|
||||
_bits = _21Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
|
||||
class _23Bundle(Bundle):
|
||||
_fromIMeta = _19Bundle.from_prefix("_fromIMeta")
|
||||
_toIMeta = _22Bundle.from_prefix("_toIMeta")
|
||||
|
||||
|
||||
class _24Bundle(Bundle):
|
||||
_mmio, _instr = Signals(2)
|
||||
|
||||
|
||||
class _25Bundle(Bundle):
|
||||
_resp = _24Bundle.from_prefix("_resp")
|
||||
_req_bits_addr = Signal()
|
||||
|
||||
|
||||
class _26Bundle(Bundle):
|
||||
_1 = _25Bundle.from_prefix("_1")
|
||||
_0 = _25Bundle.from_prefix("_0")
|
||||
|
||||
|
||||
class _27Bundle(Bundle):
|
||||
_ftqIdx = _4Bundle.from_prefix("_ftqIdx")
|
||||
_backendException, _startAddr, _isSoftPrefetch, _nextlineStart = Signals(4)
|
||||
|
||||
|
||||
class _28Bundle(Bundle):
|
||||
_bits = _27Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
|
||||
class _29Bundle(Bundle):
|
||||
_pbmt = _20Bundle.from_prefix("_pbmt")
|
||||
_exception = _20Bundle.from_prefix("_exception")
|
||||
|
||||
|
||||
class _30Bundle(Bundle):
|
||||
_waymask = _20Bundle.from_prefix("_waymask")
|
||||
_meta_codes = _20Bundle.from_prefix("_meta_codes")
|
||||
_vSetIdx = _20Bundle.from_prefix("_vSetIdx")
|
||||
_ptag = _20Bundle.from_prefix("_ptag")
|
||||
_itlb = _29Bundle.from_prefix("_itlb")
|
||||
|
||||
|
||||
class _31Bundle(Bundle):
|
||||
_gpaddr, _isForVSnonLeafPTE = Signals(2)
|
||||
|
||||
|
||||
class _32Bundle(Bundle):
|
||||
_gpf = _31Bundle.from_prefix("_gpf")
|
||||
_entry = _30Bundle.from_prefix("_entry")
|
||||
|
||||
|
||||
class _33Bundle(Bundle):
|
||||
_bits = _32Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
|
||||
class _34Bundle(Bundle):
|
||||
_MSHRReq = _1Bundle.from_prefix("_MSHRReq")
|
||||
_itlb = _13Bundle.from_prefix("_itlb")
|
||||
_flushFromBpu = _6Bundle.from_prefix("_flushFromBpu")
|
||||
_metaRead = _23Bundle.from_prefix("_metaRead")
|
||||
_wayLookupWrite = _33Bundle.from_prefix("_wayLookupWrite")
|
||||
_req = _28Bundle.from_prefix("_req")
|
||||
_pmp = _26Bundle.from_prefix("_pmp")
|
||||
_MSHRResp = _3Bundle.from_prefix("_MSHRResp")
|
||||
_csr_pf_enable, _itlbFlushPipe, _flush = Signals(3)
|
||||
|
||||
|
||||
class IPrefetchPipeBundle(Bundle):
|
||||
io = _34Bundle.from_prefix("io")
|
||||
clock, IPrefetchPipe_s1_flush, reset = Signals(3)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .iprefetchpipe_env import IPrefetchPipeEnv
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
from toffee import Env
|
||||
from dut.IPrefetchPipe import DUTIPrefetchPipe
|
||||
from ..agent import IPrefetchPipeAgent
|
||||
from ..bundle import IPrefetchPipeBundle
|
||||
|
||||
|
||||
class IPrefetchPipeEnv(Env):
|
||||
def __init__(self, dut: DUTIPrefetchPipe):
|
||||
super().__init__()
|
||||
self.dut = dut
|
||||
self.bundle = IPrefetchPipeBundle.from_prefix("").bind(dut)
|
||||
self.agent = IPrefetchPipeAgent(self.bundle)
|
||||
self.bundle.set_all(0)
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import asyncio
|
||||
import toffee
|
||||
import toffee_test
|
||||
from toffee import start_clock
|
||||
from dut.IPrefetchPipe import DUTIPrefetchPipe
|
||||
from ..env import IPrefetchPipeEnv
|
||||
|
||||
@toffee_test.fixture
|
||||
async def iprefetchpipe_env(toffee_request: toffee_test.ToffeeRequest):
|
||||
toffee.setup_logging(toffee.ERROR)
|
||||
dut = toffee_request.create_dut(DUTIPrefetchPipe)
|
||||
dut.InitClock("clock")
|
||||
start_clock(dut)
|
||||
iprefetchpipe_env = IPrefetchPipeEnv(dut)
|
||||
yield iprefetchpipe_env
|
||||
|
||||
cur = asyncio.get_event_loop()
|
||||
for task in asyncio.all_tasks(cur):
|
||||
if task.get_name() == "__clock_loop":
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from .iprefetchpipe_fixture import iprefetchpipe_env
|
||||
from ..env import IPrefetchPipeEnv
|
||||
import toffee_test
|
||||
|
||||
|
||||
@toffee_test.testcase
|
||||
async def test_smoke(iprefetchpipe_env: IPrefetchPipeEnv):
|
||||
await iprefetchpipe_env.agent.set_s1_flush()
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .mainpipe_agent import ICacheMainPipeAgent
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
from toffee import Agent
|
||||
from ..bundle import ICacheMainPipeBundle
|
||||
|
||||
|
||||
class ICacheMainPipeAgent(Agent):
|
||||
def __init__(self, bundle: ICacheMainPipeBundle):
|
||||
super().__init__(bundle)
|
||||
bundle.set_all(0)
|
||||
self.bundle = bundle
|
||||
|
||||
async def set_flush(self):
|
||||
|
||||
print(
|
||||
f"\nBefore setting: \n \
|
||||
s2_fire is: {self.bundle.ICacheMainPipe_s2._fire.value}\n \
|
||||
_toMSHRArbiter_io_in_0_valid is: {self.bundle.ICacheMainPipe__toMSHRArbiter_io_in._0._valid_T._4.value}\n \
|
||||
_toMSHRArbiter_io_in_1_valid is: {self.bundle.ICacheMainPipe__toMSHRArbiter_io_in._1._valid_T._4.value}\n \
|
||||
s2_valid is: {self.bundle.ICacheMainPipe_s2._valid.value}\n \
|
||||
"
|
||||
)
|
||||
|
||||
self.bundle.io._flush.value = 1
|
||||
await self.bundle.step()
|
||||
|
||||
print(
|
||||
f"After setting: \n \
|
||||
s2_fire is: {self.bundle.ICacheMainPipe_s2._fire.value}\n \
|
||||
_toMSHRArbiter_io_in_0_valid is: {self.bundle.ICacheMainPipe__toMSHRArbiter_io_in._0._valid_T._4.value}\n \
|
||||
_toMSHRArbiter_io_in_1_valid is: {self.bundle.ICacheMainPipe__toMSHRArbiter_io_in._1._valid_T._4.value}\n \
|
||||
s2_valid is: {self.bundle.ICacheMainPipe_s2._valid.value}\n \
|
||||
"
|
||||
)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .mainpipe_bundle import ICacheMainPipeBundle
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
from toffee import Bundle, Signals, Signal
|
||||
|
||||
|
||||
class _0Bundle(Bundle):
|
||||
_4 = Signal()
|
||||
|
||||
|
||||
class _1Bundle(Bundle):
|
||||
_valid_T = _0Bundle.from_prefix("_valid_T")
|
||||
|
||||
|
||||
class _2Bundle(Bundle):
|
||||
_0 = _1Bundle.from_prefix("_0")
|
||||
_1 = _1Bundle.from_prefix("_1")
|
||||
|
||||
|
||||
class _3Bundle(Bundle):
|
||||
_fire, _valid = Signals(2)
|
||||
|
||||
|
||||
class _4Bundle(Bundle):
|
||||
_6, _7, _3, _5, _4, _1, _2, _0 = Signals(8)
|
||||
|
||||
|
||||
class _5Bundle(Bundle):
|
||||
_datas = _4Bundle.from_prefix("_datas")
|
||||
_codes = _4Bundle.from_prefix("_codes")
|
||||
|
||||
|
||||
class _6Bundle(Bundle):
|
||||
_1, _0 = Signals(2)
|
||||
|
||||
|
||||
class _7Bundle(Bundle):
|
||||
_1, _0, _2, _3 = Signals(4)
|
||||
|
||||
|
||||
class _8Bundle(Bundle):
|
||||
_1 = _7Bundle.from_prefix("_1")
|
||||
_0 = _7Bundle.from_prefix("_0")
|
||||
|
||||
|
||||
class _9Bundle(Bundle):
|
||||
_waymask = _8Bundle.from_prefix("_waymask")
|
||||
_vSetIdx = _6Bundle.from_prefix("_vSetIdx")
|
||||
_blkOffset = Signal()
|
||||
|
||||
|
||||
class _10Bundle(Bundle):
|
||||
_bits = _9Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _11Bundle(Bundle):
|
||||
_bits_vSetIdx = _6Bundle.from_prefix("_bits_vSetIdx")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _12Bundle(Bundle):
|
||||
_bits_vSetIdx = _6Bundle.from_prefix("_bits_vSetIdx")
|
||||
_ready, _valid = Signals(2)
|
||||
|
||||
|
||||
class _13Bundle(Bundle):
|
||||
_3 = _12Bundle.from_prefix("_3")
|
||||
_1 = _11Bundle.from_prefix("_1")
|
||||
_2 = _11Bundle.from_prefix("_2")
|
||||
_0 = _10Bundle.from_prefix("_0")
|
||||
|
||||
|
||||
class _14Bundle(Bundle):
|
||||
_fromIData = _5Bundle.from_prefix("_fromIData")
|
||||
_toIData = _13Bundle.from_prefix("_toIData")
|
||||
|
||||
|
||||
class _15Bundle(Bundle):
|
||||
_report_to_beu, _paddr = Signals(2)
|
||||
|
||||
|
||||
class _16Bundle(Bundle):
|
||||
_bits = _15Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _17Bundle(Bundle):
|
||||
_0 = _16Bundle.from_prefix("_0")
|
||||
_1 = _16Bundle.from_prefix("_1")
|
||||
|
||||
|
||||
class _18Bundle(Bundle):
|
||||
_startAddr, _nextlineStart = Signals(2)
|
||||
|
||||
|
||||
class _19Bundle(Bundle):
|
||||
_1 = _18Bundle.from_prefix("_1")
|
||||
_3 = _18Bundle.from_prefix("_3")
|
||||
_0 = _18Bundle.from_prefix("_0")
|
||||
_4 = _18Bundle.from_prefix("_4")
|
||||
_2 = _18Bundle.from_prefix("_2")
|
||||
|
||||
|
||||
class _20Bundle(Bundle):
|
||||
_3, _4, _1, _2, _0 = Signals(5)
|
||||
|
||||
|
||||
class _21Bundle(Bundle):
|
||||
_readValid = _20Bundle.from_prefix("_readValid")
|
||||
_pcMemRead = _19Bundle.from_prefix("_pcMemRead")
|
||||
_backendException = Signal()
|
||||
|
||||
|
||||
class _22Bundle(Bundle):
|
||||
_bits = _21Bundle.from_prefix("_bits")
|
||||
_ready, _valid = Signals(2)
|
||||
|
||||
|
||||
class _23Bundle(Bundle):
|
||||
_0 = Signal()
|
||||
|
||||
|
||||
class _24Bundle(Bundle):
|
||||
_exception = _6Bundle.from_prefix("_exception")
|
||||
_itlb_pbmt = _6Bundle.from_prefix("_itlb_pbmt")
|
||||
_vaddr = _6Bundle.from_prefix("_vaddr")
|
||||
_pmp_mmio = _6Bundle.from_prefix("_pmp_mmio")
|
||||
_paddr = _23Bundle.from_prefix("_paddr")
|
||||
_data, _doubleline, _gpaddr, _backendException, _isForVSnonLeafPTE = Signals(5)
|
||||
|
||||
|
||||
class _25Bundle(Bundle):
|
||||
_bits = _24Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _26Bundle(Bundle):
|
||||
_resp = _25Bundle.from_prefix("_resp")
|
||||
_req = _22Bundle.from_prefix("_req")
|
||||
_topdownIcacheMiss, _topdownItlbMiss = Signals(2)
|
||||
|
||||
|
||||
class _27Bundle(Bundle):
|
||||
_virIdx, _waymask = Signals(2)
|
||||
|
||||
|
||||
class _28Bundle(Bundle):
|
||||
_bits = _27Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _29Bundle(Bundle):
|
||||
_0 = _28Bundle.from_prefix("_0")
|
||||
_1 = _28Bundle.from_prefix("_1")
|
||||
|
||||
|
||||
class _30Bundle(Bundle):
|
||||
_blkPaddr, _vSetIdx = Signals(2)
|
||||
|
||||
|
||||
class _31Bundle(Bundle):
|
||||
_bits = _30Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
|
||||
class _32Bundle(Bundle):
|
||||
_corrupt, _blkPaddr, _vSetIdx, _data = Signals(4)
|
||||
|
||||
|
||||
class _33Bundle(Bundle):
|
||||
_bits = _32Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _34Bundle(Bundle):
|
||||
_resp = _33Bundle.from_prefix("_resp")
|
||||
_req = _31Bundle.from_prefix("_req")
|
||||
|
||||
|
||||
class _35Bundle(Bundle):
|
||||
_1 = Signal()
|
||||
|
||||
|
||||
class _36Bundle(Bundle):
|
||||
_miss = _35Bundle.from_prefix("_miss")
|
||||
_hit = _35Bundle.from_prefix("_hit")
|
||||
_except = _35Bundle.from_prefix("_except")
|
||||
|
||||
|
||||
class _37Bundle(Bundle):
|
||||
_0 = _36Bundle.from_prefix("_0")
|
||||
|
||||
|
||||
class _38Bundle(Bundle):
|
||||
_hit, _miss = Signals(2)
|
||||
|
||||
|
||||
class _39Bundle(Bundle):
|
||||
_0 = _38Bundle.from_prefix("_0")
|
||||
|
||||
|
||||
class _40Bundle(Bundle):
|
||||
_except = _23Bundle.from_prefix("_except")
|
||||
_miss = _37Bundle.from_prefix("_miss")
|
||||
_bank_hit = _6Bundle.from_prefix("_bank_hit")
|
||||
_only = _39Bundle.from_prefix("_only")
|
||||
_hit = Signal()
|
||||
|
||||
|
||||
class _41Bundle(Bundle):
|
||||
_mmio, _instr = Signals(2)
|
||||
|
||||
|
||||
class _42Bundle(Bundle):
|
||||
_resp = _41Bundle.from_prefix("_resp")
|
||||
_req_bits_addr = Signal()
|
||||
|
||||
|
||||
class _43Bundle(Bundle):
|
||||
_0 = _42Bundle.from_prefix("_0")
|
||||
_1 = _42Bundle.from_prefix("_1")
|
||||
|
||||
|
||||
class _44Bundle(Bundle):
|
||||
_way, _vSetIdx = Signals(2)
|
||||
|
||||
|
||||
class _45Bundle(Bundle):
|
||||
_bits = _44Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _46Bundle(Bundle):
|
||||
_0 = _45Bundle.from_prefix("_0")
|
||||
_1 = _45Bundle.from_prefix("_1")
|
||||
|
||||
|
||||
class _47Bundle(Bundle):
|
||||
_pbmt = _6Bundle.from_prefix("_pbmt")
|
||||
_exception = _6Bundle.from_prefix("_exception")
|
||||
|
||||
|
||||
class _48Bundle(Bundle):
|
||||
_waymask = _6Bundle.from_prefix("_waymask")
|
||||
_ptag = _6Bundle.from_prefix("_ptag")
|
||||
_vSetIdx = _6Bundle.from_prefix("_vSetIdx")
|
||||
_meta_codes = _6Bundle.from_prefix("_meta_codes")
|
||||
_itlb = _47Bundle.from_prefix("_itlb")
|
||||
|
||||
|
||||
class _49Bundle(Bundle):
|
||||
_gpaddr, _isForVSnonLeafPTE = Signals(2)
|
||||
|
||||
|
||||
class _50Bundle(Bundle):
|
||||
_entry = _48Bundle.from_prefix("_entry")
|
||||
_gpf = _49Bundle.from_prefix("_gpf")
|
||||
|
||||
|
||||
class _51Bundle(Bundle):
|
||||
_bits = _50Bundle.from_prefix("_bits")
|
||||
_ready, _valid = Signals(2)
|
||||
|
||||
|
||||
class _52Bundle(Bundle):
|
||||
_dataArray = _14Bundle.from_prefix("_dataArray")
|
||||
_pmp = _43Bundle.from_prefix("_pmp")
|
||||
_mshr = _34Bundle.from_prefix("_mshr")
|
||||
_wayLookupRead = _51Bundle.from_prefix("_wayLookupRead")
|
||||
_perfInfo = _40Bundle.from_prefix("_perfInfo")
|
||||
_errors = _17Bundle.from_prefix("_errors")
|
||||
_fetch = _26Bundle.from_prefix("_fetch")
|
||||
_metaArrayFlush = _29Bundle.from_prefix("_metaArrayFlush")
|
||||
_touch = _46Bundle.from_prefix("_touch")
|
||||
_flush, _respStall, _ecc_enable, _hartId = Signals(4)
|
||||
|
||||
|
||||
class ICacheMainPipeBundle(Bundle):
|
||||
ICacheMainPipe_s2 = _3Bundle.from_prefix("ICacheMainPipe_s2")
|
||||
io = _52Bundle.from_prefix("io")
|
||||
ICacheMainPipe__toMSHRArbiter_io_in = _2Bundle.from_prefix(
|
||||
"ICacheMainPipe__toMSHRArbiter_io_in"
|
||||
)
|
||||
clock, reset = Signals(2)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .mainpipe_env import ICacheMainPipeEnv
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
from toffee import Env
|
||||
from dut.ICacheMainPipe import DUTICacheMainPipe
|
||||
from..agent import ICacheMainPipeAgent
|
||||
from..bundle import ICacheMainPipeBundle
|
||||
|
||||
class ICacheMainPipeEnv(Env):
|
||||
def __init__(self, dut: DUTICacheMainPipe):
|
||||
super().__init__()
|
||||
self.dut = dut
|
||||
self.bundle = ICacheMainPipeBundle.from_prefix("").bind(dut)
|
||||
self.agent = ICacheMainPipeAgent(self.bundle)
|
||||
self.bundle.set_all(0)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
from .mainpipe_fixture import icachemainpipe_env
|
||||
from ..env import ICacheMainPipeEnv
|
||||
import toffee_test
|
||||
|
||||
@toffee_test.testcase
|
||||
async def test_smoke(icachemainpipe_env: ICacheMainPipeEnv):
|
||||
await icachemainpipe_env.agent.set_flush()
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import asyncio
|
||||
import toffee
|
||||
import toffee_test
|
||||
from toffee import start_clock
|
||||
from dut.ICacheMainPipe import DUTICacheMainPipe
|
||||
from ..env import ICacheMainPipeEnv
|
||||
|
||||
|
||||
@toffee_test.fixture
|
||||
async def icachemainpipe_env(toffee_request: toffee_test.ToffeeRequest):
|
||||
toffee.setup_logging(toffee.ERROR)
|
||||
dut = toffee_request.create_dut(DUTICacheMainPipe)
|
||||
dut.InitClock("clock")
|
||||
start_clock(dut)
|
||||
icachemainpipe_env = ICacheMainPipeEnv(dut)
|
||||
yield icachemainpipe_env
|
||||
|
||||
cur_loop = asyncio.get_event_loop()
|
||||
for task in asyncio.all_tasks(cur_loop):
|
||||
if task.get_name() == "__clock_loop":
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .missunit_agent import ICacheMissUnitAgent
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
from toffee import Agent
|
||||
from ..bundle import ICacheMissUnitBundle
|
||||
|
||||
|
||||
class ICacheMissUnitAgent(Agent):
|
||||
def __init__(self, bundle: ICacheMissUnitBundle):
|
||||
super().__init__(bundle)
|
||||
self.bundle = bundle
|
||||
|
||||
async def fencei_func(self, value):
|
||||
|
||||
print(f"\nBefore setting fencei :")
|
||||
|
||||
for i in range(10):
|
||||
print(
|
||||
f"prefetchMSHRs.{i}.io.req.ready.value:",
|
||||
getattr(
|
||||
self.bundle.ICacheMissUnit_._prefetchMSHRs, f"_{i}"
|
||||
)._io._req_ready.value,
|
||||
)
|
||||
print(
|
||||
f"prefetchMSHRs.{i}.io.acquire.valid.value:",
|
||||
getattr(
|
||||
self.bundle.ICacheMissUnit_._prefetchMSHRs, f"_{i}"
|
||||
)._io._acquire_valid.value,
|
||||
)
|
||||
|
||||
for i in range(4):
|
||||
print(
|
||||
f"fetchMSHRs.{i}.io.req.ready.value:",
|
||||
getattr(
|
||||
self.bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}"
|
||||
)._io._req_ready.value,
|
||||
)
|
||||
print(
|
||||
f"fetchMSHRs.{i}.io.acquire.valid.value:",
|
||||
getattr(
|
||||
self.bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}"
|
||||
)._io._acquire_valid.value,
|
||||
)
|
||||
|
||||
self.bundle.io._fencei.value = value
|
||||
await self.bundle.step()
|
||||
|
||||
print(f"\nAfter setting fencei = {value}")
|
||||
|
||||
for i in range(10):
|
||||
print(
|
||||
f"prefetchMSHRs.{i}.io.req.ready.value:",
|
||||
getattr(
|
||||
self.bundle.ICacheMissUnit_._prefetchMSHRs, f"_{i}"
|
||||
)._io._req_ready.value,
|
||||
)
|
||||
print(
|
||||
f"prefetchMSHRs.{i}.io.acquire.valid.value:",
|
||||
getattr(
|
||||
self.bundle.ICacheMissUnit_._prefetchMSHRs, f"_{i}"
|
||||
)._io._acquire_valid.value,
|
||||
)
|
||||
|
||||
for i in range(4):
|
||||
print(
|
||||
f"fetchMSHRs.{i}.io.req.ready.value:",
|
||||
getattr(
|
||||
self.bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}"
|
||||
)._io._req_ready.value,
|
||||
)
|
||||
print(
|
||||
f"fetchMSHRs.{i}.io.acquire.valid.value:",
|
||||
getattr(
|
||||
self.bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}"
|
||||
)._io._acquire_valid.value,
|
||||
)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .missunit_bundle import ICacheMissUnitBundle
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
from toffee import Bundle, Signals, Signal
|
||||
|
||||
class _0Bundle(Bundle):
|
||||
_acquire_valid, _req_ready = Signals(2)
|
||||
|
||||
class _1Bundle(Bundle):
|
||||
_io = _0Bundle.from_prefix("_io")
|
||||
|
||||
class _2Bundle(Bundle):
|
||||
_2 = _1Bundle.from_prefix("_2")
|
||||
_0 = _1Bundle.from_prefix("_0")
|
||||
_3 = _1Bundle.from_prefix("_3")
|
||||
_1 = _1Bundle.from_prefix("_1")
|
||||
|
||||
class _3Bundle(Bundle):
|
||||
_5 = _1Bundle.from_prefix("_5")
|
||||
_8 = _1Bundle.from_prefix("_8")
|
||||
_9 = _1Bundle.from_prefix("_9")
|
||||
_7 = _1Bundle.from_prefix("_7")
|
||||
_2 = _1Bundle.from_prefix("_2")
|
||||
_6 = _1Bundle.from_prefix("_6")
|
||||
_3 = _1Bundle.from_prefix("_3")
|
||||
_4 = _1Bundle.from_prefix("_4")
|
||||
_0 = _1Bundle.from_prefix("_0")
|
||||
_1 = _1Bundle.from_prefix("_1")
|
||||
|
||||
class _4Bundle(Bundle):
|
||||
_prefetchMSHRs = _3Bundle.from_prefix("_prefetchMSHRs")
|
||||
_fetchMSHRs = _2Bundle.from_prefix("_fetchMSHRs")
|
||||
|
||||
class _5Bundle(Bundle):
|
||||
_data, _virIdx, _waymask = Signals(3)
|
||||
|
||||
class _6Bundle(Bundle):
|
||||
_bits = _5Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
class _7Bundle(Bundle):
|
||||
_vSetIdx, _blkPaddr = Signals(2)
|
||||
|
||||
class _8Bundle(Bundle):
|
||||
_bits = _7Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
class _9Bundle(Bundle):
|
||||
_waymask, _corrupt, _data, _vSetIdx, _blkPaddr = Signals(5)
|
||||
|
||||
class _10Bundle(Bundle):
|
||||
_bits = _9Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
class _11Bundle(Bundle):
|
||||
_resp = _10Bundle.from_prefix("_resp")
|
||||
_req = _8Bundle.from_prefix("_req")
|
||||
|
||||
class _12Bundle(Bundle):
|
||||
_source, _address = Signals(2)
|
||||
|
||||
class _13Bundle(Bundle):
|
||||
_bits = _12Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
class _14Bundle(Bundle):
|
||||
_source, _corrupt, _data, _size, _opcode = Signals(5)
|
||||
|
||||
class _15Bundle(Bundle):
|
||||
_bits = _14Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
class _16Bundle(Bundle):
|
||||
_acquire = _13Bundle.from_prefix("_acquire")
|
||||
_grant = _15Bundle.from_prefix("_grant")
|
||||
|
||||
class _17Bundle(Bundle):
|
||||
_bankIdx, _phyTag, _virIdx, _waymask = Signals(4)
|
||||
|
||||
class _18Bundle(Bundle):
|
||||
_bits = _17Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
class _19Bundle(Bundle):
|
||||
_valid, _bits = Signals(2)
|
||||
|
||||
class _20Bundle(Bundle):
|
||||
_vSetIdx = _19Bundle.from_prefix("_vSetIdx")
|
||||
_way = Signal()
|
||||
|
||||
class _21Bundle(Bundle):
|
||||
_meta_write = _18Bundle.from_prefix("_meta_write")
|
||||
_mem = _16Bundle.from_prefix("_mem")
|
||||
_data_write = _6Bundle.from_prefix("_data_write")
|
||||
_victim = _20Bundle.from_prefix("_victim")
|
||||
_fetch = _11Bundle.from_prefix("_fetch")
|
||||
_prefetch_req = _8Bundle.from_prefix("_prefetch_req")
|
||||
_fencei, _flush, _hartId = Signals(3)
|
||||
|
||||
class ICacheMissUnitBundle(Bundle):
|
||||
io = _21Bundle.from_prefix("io")
|
||||
ICacheMissUnit_ = _4Bundle.from_prefix("ICacheMissUnit_")
|
||||
reset, clock = Signals(2)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .missunit_env import ICacheMissUnitEnv
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
from toffee import Env
|
||||
from dut.ICacheMissUnit import DUTICacheMissUnit
|
||||
from ..agent import ICacheMissUnitAgent
|
||||
from ..bundle import ICacheMissUnitBundle
|
||||
|
||||
|
||||
class ICacheMissUnitEnv(Env):
|
||||
def __init__(self, dut: DUTICacheMissUnit):
|
||||
super().__init__()
|
||||
self.dut = dut
|
||||
self.bundle = ICacheMissUnitBundle.from_prefix("").bind(dut)
|
||||
self.agent = ICacheMissUnitAgent(self.bundle)
|
||||
self.bundle.set_all(0)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import asyncio
|
||||
import toffee
|
||||
import toffee_test
|
||||
from toffee import start_clock
|
||||
from dut.ICacheMissUnit import DUTICacheMissUnit
|
||||
from ..env import ICacheMissUnitEnv
|
||||
|
||||
|
||||
@toffee_test.fixture
|
||||
async def icachemissunit_env(toffee_request: toffee_test.ToffeeRequest):
|
||||
toffee.setup_logging(toffee.ERROR)
|
||||
dut = toffee_request.create_dut(DUTICacheMissUnit)
|
||||
dut.InitClock("clock")
|
||||
start_clock(dut)
|
||||
icachemissunit_env = ICacheMissUnitEnv(dut)
|
||||
yield icachemissunit_env
|
||||
|
||||
cur_loop = asyncio.get_event_loop()
|
||||
for task in asyncio.all_tasks(cur_loop):
|
||||
if task.get_name() == "__clock_loop":
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from .missunit_fixture import icachemissunit_env
|
||||
from ..env import ICacheMissUnitEnv
|
||||
import toffee_test
|
||||
|
||||
|
||||
@toffee_test.testcase
|
||||
async def test_smoke(icachemissunit_env: ICacheMissUnitEnv):
|
||||
await icachemissunit_env.agent.fencei_func(1)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .waylookup_agent import WayLookupAgent
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
from toffee import Agent
|
||||
from ..bundle import WayLookupBundle
|
||||
|
||||
|
||||
class ReadBitsData:
|
||||
|
||||
WayLookup_readPtr_value_before_flush: int
|
||||
WayLookup_readPtr_value_after_flush: int
|
||||
WayLookup_readPtr_flag_before_flush: bool
|
||||
WayLookup_readPtr_flag_after_flush: bool
|
||||
WayLookup_writePtr_value_before_flush: int
|
||||
WayLookup_writePtr_value_after_flush: int
|
||||
WayLookup_writePtr_flag_before_flush: bool
|
||||
WayLookup_writePtr_flag_after_flush: bool
|
||||
flush: int
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
f"\n"
|
||||
f"WayLookup_readPtr_value_before_flush: {self.WayLookup_readPtr_value_before_flush}\n"
|
||||
f"WayLookup_readPtr_value_after_flush: {self.WayLookup_readPtr_value_after_flush}\n"
|
||||
f"WayLookup_readPtr_flag_before_flush: {self.WayLookup_readPtr_flag_before_flush}\n"
|
||||
f"WayLookup_readPtr_flag_after_flush: {self.WayLookup_readPtr_flag_after_flush}\n"
|
||||
f"WayLookup_writePtr_value_before_flush: {self.WayLookup_writePtr_value_before_flush}\n"
|
||||
f"WayLookup_writePtr_value_after_flush: {self.WayLookup_writePtr_value_after_flush}\n"
|
||||
f"WayLookup_writePtr_flag_before_flush: {self.WayLookup_writePtr_flag_before_flush}\n"
|
||||
f"WayLookup_writePtr_flag_after_flush: {self.WayLookup_writePtr_flag_after_flush}\n"
|
||||
f"flush: {self.flush}\n"
|
||||
)
|
||||
|
||||
|
||||
class WayLookupAgent(Agent):
|
||||
|
||||
def __init__(self, bundle: WayLookupBundle):
|
||||
super().__init__(bundle)
|
||||
bundle.set_all(0)
|
||||
self.bundle = bundle
|
||||
|
||||
async def flush(self) -> ReadBitsData:
|
||||
|
||||
ret = ReadBitsData()
|
||||
|
||||
ret.WayLookup_readPtr_value_before_flush = (
|
||||
self.bundle.WayLookup._readPtr._value.value
|
||||
)
|
||||
ret.WayLookup_readPtr_flag_before_flush = (
|
||||
self.bundle.WayLookup._readPtr._flag.value
|
||||
)
|
||||
ret.WayLookup_writePtr_value_before_flush = (
|
||||
self.bundle.WayLookup._writePtr._value.value
|
||||
)
|
||||
ret.WayLookup_writePtr_flag_before_flush = (
|
||||
self.bundle.WayLookup._writePtr._flag.value
|
||||
)
|
||||
|
||||
self.bundle.io._flush.value = 1
|
||||
await self.bundle.step()
|
||||
|
||||
ret.WayLookup_readPtr_value_after_flush = (
|
||||
self.bundle.WayLookup._readPtr._value.value
|
||||
)
|
||||
ret.WayLookup_readPtr_flag_after_flush = (
|
||||
self.bundle.WayLookup._readPtr._flag.value
|
||||
)
|
||||
ret.WayLookup_writePtr_value_after_flush = (
|
||||
self.bundle.WayLookup._writePtr._value.value
|
||||
)
|
||||
ret.WayLookup_writePtr_flag_after_flush = (
|
||||
self.bundle.WayLookup._writePtr._flag.value
|
||||
)
|
||||
|
||||
ret.flush = self.bundle.io._flush.value
|
||||
return ret
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .waylookup_bundle import WayLookupBundle
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
from toffee import Bundle, Signals, Signal
|
||||
|
||||
|
||||
class _00Bundle(Bundle):
|
||||
_0 = Signal()
|
||||
|
||||
|
||||
class _0Bundle(Bundle):
|
||||
_flag, _value = Signals(2)
|
||||
|
||||
|
||||
class _1Bundle(Bundle):
|
||||
_readPtr = _0Bundle.from_prefix("_readPtr")
|
||||
_writePtr = _0Bundle.from_prefix("_writePtr")
|
||||
_io_write_ready = _00Bundle.from_prefix("_io_write_ready")
|
||||
_entries_30_waymask_0 = Signal()
|
||||
|
||||
|
||||
class _2Bundle(Bundle):
|
||||
_0, _1 = Signals(2)
|
||||
|
||||
|
||||
class _3Bundle(Bundle):
|
||||
_exception = _2Bundle.from_prefix("_exception")
|
||||
_pbmt = _2Bundle.from_prefix("_pbmt")
|
||||
|
||||
|
||||
class _4Bundle(Bundle):
|
||||
_waymask = _2Bundle.from_prefix("_waymask")
|
||||
_vSetIdx = _2Bundle.from_prefix("_vSetIdx")
|
||||
_ptag = _2Bundle.from_prefix("_ptag")
|
||||
_meta_codes = _2Bundle.from_prefix("_meta_codes")
|
||||
_itlb = _3Bundle.from_prefix("_itlb")
|
||||
|
||||
|
||||
class _5Bundle(Bundle):
|
||||
_isForVSnonLeafPTE, _gpaddr = Signals(2)
|
||||
|
||||
|
||||
class _6Bundle(Bundle):
|
||||
_gpf = _5Bundle.from_prefix("_gpf")
|
||||
_entry = _4Bundle.from_prefix("_entry")
|
||||
|
||||
|
||||
class _7Bundle(Bundle):
|
||||
_bits = _6Bundle.from_prefix("_bits")
|
||||
_valid, _ready = Signals(2)
|
||||
|
||||
|
||||
class _8Bundle(Bundle):
|
||||
_corrupt, _vSetIdx, _blkPaddr, _waymask = Signals(4)
|
||||
|
||||
|
||||
class _9Bundle(Bundle):
|
||||
_bits = _8Bundle.from_prefix("_bits")
|
||||
_valid = Signal()
|
||||
|
||||
|
||||
class _10Bundle(Bundle):
|
||||
_update = _9Bundle.from_prefix("_update")
|
||||
_write = _7Bundle.from_prefix("_write")
|
||||
_read = _7Bundle.from_prefix("_read")
|
||||
_flush = Signal()
|
||||
|
||||
|
||||
class WayLookupBundle(Bundle):
|
||||
io = _10Bundle.from_prefix("io")
|
||||
WayLookup = _1Bundle.from_prefix("WayLookup")
|
||||
reset, clock = Signals(2)
|
||||
|
|
@ -0,0 +1 @@
|
|||
from .waylookup_env import WayLookupEnv
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
from toffee import Env
|
||||
from dut.WayLookup import DUTWayLookup
|
||||
from ..agent import WayLookupAgent
|
||||
from ..bundle import WayLookupBundle
|
||||
|
||||
|
||||
class WayLookupEnv(Env):
|
||||
|
||||
def __init__(self, dut: DUTWayLookup):
|
||||
super().__init__()
|
||||
self.dut = dut
|
||||
self.bundle = WayLookupBundle.from_prefix("").bind(dut)
|
||||
self.agent = WayLookupAgent(self.bundle)
|
||||
self.bundle.set_all(0)
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import toffee_test
|
||||
import toffee
|
||||
from dut.WayLookup import DUTWayLookup
|
||||
from toffee import start_clock
|
||||
from ..env import WayLookupEnv
|
||||
import asyncio
|
||||
|
||||
|
||||
@toffee_test.fixture
|
||||
async def waylookup_env(toffee_request: toffee_test.ToffeeRequest):
|
||||
toffee.setup_logging(toffee.ERROR)
|
||||
dut = toffee_request.create_dut(DUTWayLookup)
|
||||
dut.InitClock("clock")
|
||||
start_clock(dut)
|
||||
waylookup_env = WayLookupEnv(dut)
|
||||
yield waylookup_env
|
||||
|
||||
cur_loop = asyncio.get_event_loop()
|
||||
for task in asyncio.all_tasks(cur_loop):
|
||||
if task.get_name() == "__clock_loop":
|
||||
task.cancel()
|
||||
try:
|
||||
await task
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
from .waylookup_fixture import waylookup_env
|
||||
from ..env import WayLookupEnv
|
||||
import toffee_test
|
||||
|
||||
|
||||
@toffee_test.testcase
|
||||
async def test_smoke(waylookup_env: WayLookupEnv):
|
||||
|
||||
print(await waylookup_env.agent.flush())
|
||||