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
This commit is contained in:
yeximing 2025-04-03 10:10:56 +08:00 committed by GitHub
parent 73c0bf672f
commit 904f80bc9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
95 changed files with 5526 additions and 25 deletions

View File

@ -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

View File

@ -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 在一个周期内成功返回物理地址 paddrs1_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 为真,处理第二个请求。
- 如果第一个请求有异常或 MMIOs2_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 被同步刷新。

View File

@ -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 获取读响应异常合并、替换策略更新以及监控 MissUnitS1 阶段)
- 寄存并延迟 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. 不访问 DataArrayWay 未命中) ==会访问,但是返回数据无效==
- 当 WayLookup 中的信息表明路未命中时MainPipe 不会向 DataArray 发送读取请求。
- s0_hits 为低表示缓存未命中
- toData.valid 信号为低,表示 MainPipe 未向 DataArray 发出读取请求。
3. 不访问 DataArrayITLB 查询失败)==会访问,但是返回数据无效==
- 当 ITLB 查询失败时MainPipe 不会向 DataArray 发送读取请求。
- s0_itlb_exception 信号不为零ITLB 查询失败)。
- toData.valid 信号为低,表示 MainPipe 未向 DataArray 发出读取请求。
4. 不访问 DataArrayDataArray 正在进行写操作)
- 当 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_mmio0 和 s1_pmp_mergemmio1 都为假,表示没有映射到 MMIO 区域。
6. 通道 0 映射到了 MMIO 区域
- s1_pmp_mmio0 为真,表示映射到了 MMIO 区域。
7. 通道 1 映射到了 MMIO 区域
- s1_pmp_mmio1 为真,表示映射到了 MMIO 区域。
8. 通道 0 和通道 1 都映射到了 MMIO 区域
- s1_pmp_mmio0 和 s1_pmp_mmio1 都为真,表示通道 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_matchs2_MSHR_hits 为高s2_bankMSHRHit 对应 bank 为高
- s1_fire 无效时s2_datas 更新为 MSHR 的数据,将 s2_data_is_from_MSHR 对应位置位s2_hits 置位,清除 s2_data_corruptl2 的 corrupt 更新为 fromMSHR.bits.corrupt
- s1_fire 有效时s2_datas 为 s1_datas 的数据,将 s2_data_is_from_MSHR 对应位置为 s1 的 s1_data_is_from_MSHRs2_hits 置为 s1_hits清除 s2_data_corruptl2 的 corrupt 为 false
2. MSHR 未命中
- MSHR 的 vSetIdx / blkPaddr 与 S2 请求一致, fromMSHR.valid 有效s2_valid 也有效,至少有一个未达成
- s2_MSHR_hits(i) = falseS2 不会更新 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 均为 trueArbiter 根据仲裁顺序依次发出请求。
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. 正常命中并返回
- 不存在任何异常或 Misss2 命中s2 阶段取指完成,外部的 respStall 停止信号也为低 。
- toIFU.valid = truetoIFU.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 = falsetoIFU.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) = trueio.errors(0).valid = trueio.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

View File

@ -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 信息(包括 vSetIdxwaymaskptagitlb_exceptionitlb_pbmtmeta_codesgpaddrisForVSnonLeafPTE
- 写入前,需要考虑队列是否已满,以及是否有 GPF 阻塞。如果有 GPF 信息待读取且未被处理,则写入需要等待,防止覆盖 GPF 信息。写入时,如果数据中包含 GPF 异常,就将信息存入 gpf_entry并更新 gpfPtr。
- MainPipe 从其读出 WayLookupInfo 信息。
- 在读取上有两种情况当队列为空但有写请求时可以直接将写的数据旁路bypass给读端口否则就从 entries 数组中读取对应读指针的数据。同时,如果当前读的位置存在 GPF 信息,就将 GPF 信息一起输出,并在读取后清除有效位。
- 允许 bypass当队列为空但有写请求时可以直接将写的数据旁路给读端口为了不将更新逻辑的延迟引入到 DataArray 的访问路径上,在 MSHR 有新的写入时禁止出队MainPipe 的 S0 流水级也需要访问 DataArray当 MSHR 有新的写入时无法向下走,所以该措施并不会带来额外影响。
- MissUnit 向其写入命中信息。
- 若是命中则将 waymask 更新 ICacheMissResp 信息(包括 blkPaddrvSetIdxwaymaskdatacorrupt且 meta_codes 也更新,否则 waymask 清零。更新逻辑与 IPrefetchPipe 中相同,见 [IPrefetchPipe 子模块文档中的“命中信息的更新”](./01_iprefetchpipe.md#命中信息的更新)一节。
### GPaddr 省面积机制
由于 `gpaddr` 仅在 guest page fault 发生时有用,并且每次发生 gpf 后前端实际上工作在错误路径上,后端保证会送一个 redirectWayLookup 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

View File

@ -0,0 +1,268 @@
---
title: MissUnit
linkTitle: MissUnit
weight: 12
---
<div class="icache-ctx">
</div>
## 子模块FIFO
- 一个先入先出的循环队列,目前仅在 MissUnit 中有使用,作为优先队列 priorityFIFO。
- 按照在 MissUnit 中的实例化pipe 是默认值 falsehasflush 是 true。
- 队列的指针都是环形的分为入队指针写指针ent_ptr和出队指针读指针deq_ptr记录读和写的位置。
- 两个指针都有对应的 flag 位当指针超过队列大小时flag 位会翻转,用以判断是否已经循环。
- 在入队、出队对应的 firevalid && ready 信号有效时,移动对应的指针。
## FIFO 的功能点和测试点
### 入队操作
1. 队未满,正常入队
- 当队列未满,且空位不小于一时,可以正常入队,如果从零号位开始入队到最大容量,入队指针的 flag 不会翻转。
- io.enq.fire 为高有效regFiles(enq_ptr.value) = io.enq.bitsenq_ptr.value+1 入队指针移动,入队指针标记位不翻转。
- 重复以上操作至队满。
2. 队未满,入队后标记位翻转
- 当队未满,但是空位却是靠近队尾时,入队一位后就到达了队头,入队指针的 flag 会翻转。
- 队列的容量为 10入队指针指向 9队未满。此时如果 io.enq.fire 为高,则 regFiles(9) = io.enq.bitsenq_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=0enq_ptr.value=0deq_ptr.flag=falseenq_ptr.flag=falseempty=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 为高),且没有命中已有的 MSHRfetchHit 为低io.fetch_req.ready 应为高,表示可以接受请求。
- io.fetch_req.fire 成功握手后,该 MSHR 处于 valid = true 状态,并记录地址。
2. 处理已有的取指请求
- 当已有取指缺失请求到达时io.fetch_req.valid 为高),且命中已有的 MSHRfetchHit 为高io.fetch_req.ready 应为高,虽然不接受请求,但是表现出来为已经接收请求。
- fetchDemux.io.in.valid 应为低fetchDemux.io.in.fire 为低,表示没有新的请求被分发到 MSHR。
3. 低索引的请求优先进入 MSHR
- Fetch 的请求会通过 fetchDemux 分配到多个 Fetch MSHRfetchDemux 的实现中,低索引的 MSHR 会优先被分配请求。
- 当取指请求有多个 io.out(i).read 时,选择其中的第一个,也就是低索引的写入 MSHRio.chose 为对应的索引。
### 处理预取缺失请求
与 Fetch Miss 类似,但走另一些 MSHRPrefetch MSHR
1. 接受新的预取请求
- 当新的 prefetch miss 与 MSHR 中的已有请求不重复时(通过 io.prefetch_req.bits.blkPaddr / vSetIdx 给出具体地址MissUnit 会将请求分配到一个空闲的 Prefetch MSHR 中。
- 当有新的预取缺失请求到达时io.prefetch_req.valid 为高),且没有命中已有的 MSHRprefetchHit 为低io.prefetch_req.ready 应为高,表示可以接受请求。
- io.prefetch_req.fire 成功握手后,该 MSHR 处于 valid = true 状态,并记录地址。
2. 处理已有的预取请求
- 当已有预取缺失请求到达时io.prefetch_req.valid 为高),且命中已有的 MSHRprefetchHit 为高io.prefetch_req.ready 应为高,虽然不接受请求,但是表现出来为已经接收请求。
- prefetchDemux.io.in.valid 应为低prefetchDemux.io.in.fire 为低,表示请求被接受但未分发到新的 MSHR。
3. 低索引的请求优先进入 MSHR
- Prefetch 的请求会通过 prefetchDemux 分配到多个 Prefetch MSHRprefetchDemux 的实现中,低索引的 MSHR 会优先被分配请求。
- 当取指请求有多个 io.out(i).read 时,选择其中的第一个,也就是低索引的写入 MSHRio.chose 为对应的索引。
4. 先进入 MSHR 的优先进入 prefetchArb
- 从 prefetchDemux 离开后,请求的编号会进入 priorityFIFOpriorityFIFO 会根据进入队列的顺序排序,先进入队列的请求会先进入 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 都会发送给 acquireArbacquireArb 会选择一个 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 初始为 0refillCycles - 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 发出请求,就应立即取消该 MSHRmshr_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 无影响。

View File

@ -0,0 +1,162 @@
---
title: CtrlUnit
linkTitle: CtrlUnit
weight: 12
---
<div class="icache-ctx">
</div>
## CtrlUnit
目前 CtrlUnit 主要负责 ECC 校验使能/错误注入等功能。
RegField 案例类和伴生对象的作用RegReadFn 和 RegWriteFn 案例类和伴生对象的作用。
通过两个控制寄存器 CSReccctrl 和 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 内部过程执行。

View File

@ -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.flushFromBpuitlbFlushPipe模块外部的 fencei 和 flush 信号。
- ftqPrefetch.flushFromBpu通过 FTQ 来自的 BPU 刷新信号,用于控制预取请求的冲刷。
- itlbFlushPipeITLB 的冲刷信号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 地址,是否命中 MetaArryPMP 检查),(有异常则由 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 非 0af、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 到 ICacheMetaArrayICacheReplacer 根据 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 到 ICacheDataArrayICacheReplacer 根据 PLRU 替换策略指定 way替换路被写入 waymask最终指定虚拟索引、数据、路掩码、存储体索引 bankIdx 和毒化位。写入的数据模式应跨越多个数据存储体。
- 写入操作后,发起一个对相同虚拟索引和块偏移量的读请求。验证 readResp.datas 与写入的数据相匹配。
2. 数据读取操作 (命中): 当一个读请求命中时(相应的元数据有效),它应该从相应的组、路和数据存储体返回正确的数据。
- 首先,向特定的虚拟索引和块偏移量写入数据。然后,向相同的虚拟索引和块偏移量发送一个读请求。使用不同的块偏移量进行测试,以覆盖存储体的选择逻辑。
- 验证 readResp.datas 包含之前写入的数据。
3. 数据读取操作 (未命中): 当读取一个尚未被写入的地址时ICacheDataArray 的输出应该是默认值或无关值。
- 向 ICacheDataArray 发送一个读请求,请求的虚拟索引在复位后从未被写入过。
- 验证 readResp.datas 为 0。

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 KiB

View File

@ -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 []

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -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"]

View File

@ -0,0 +1,3 @@
ICacheCtrlUnit:
- "wire io_injecting_0"
- "wire eccctrl_istatus"

View File

@ -0,0 +1,2 @@
ICache:
- "wire _metaArray_io_read_ready"

View File

@ -0,0 +1,2 @@
IPrefetchPipe:
- "wire s1_flush"

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

View File

@ -0,0 +1 @@
from .ctrlunit_agent import CtrlUnitAgent

View File

@ -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)

View File

@ -0,0 +1 @@
from .ctrlunit_bundle import CtrlUnitBundle

View File

@ -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)

View File

@ -0,0 +1 @@
from .ctrlunit_env import CtrlUnitEnv

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

View File

@ -0,0 +1 @@
from .icache_agent import ICacheAgent

View File

@ -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,
)

View File

@ -0,0 +1 @@
from .icache_bundle import ICacheBundle

View File

@ -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)

View File

@ -0,0 +1 @@
from .icache_env import ICacheEnv

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1 @@
from .iprefetchpipe_agent import IPrefetchPipeAgent

View File

@ -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)

View File

@ -0,0 +1 @@
from .iprefetchpipe_bundle import IPrefetchPipeBundle

View File

@ -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)

View File

@ -0,0 +1 @@
from .iprefetchpipe_env import IPrefetchPipeEnv

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

View File

@ -0,0 +1 @@
from .mainpipe_agent import ICacheMainPipeAgent

View File

@ -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 \
"
)

View File

@ -0,0 +1 @@
from .mainpipe_bundle import ICacheMainPipeBundle

View File

@ -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)

View File

@ -0,0 +1 @@
from .mainpipe_env import ICacheMainPipeEnv

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

View File

@ -0,0 +1 @@
from .missunit_agent import ICacheMissUnitAgent

View File

@ -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,
)

View File

@ -0,0 +1 @@
from .missunit_bundle import ICacheMissUnitBundle

View File

@ -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)

View File

@ -0,0 +1 @@
from .missunit_env import ICacheMissUnitEnv

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

View File

@ -0,0 +1 @@
from .waylookup_agent import WayLookupAgent

View File

@ -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

View File

@ -0,0 +1 @@
from .waylookup_bundle import WayLookupBundle

View File

@ -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)

View File

@ -0,0 +1 @@
from .waylookup_env import WayLookupEnv

View File

@ -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)

View File

@ -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

View File

@ -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())