diff --git a/scripts/icache_related/icache_missunit_internals.yaml b/scripts/icache_related/icache_missunit_internals.yaml index d8979306..1ea26227 100644 --- a/scripts/icache_related/icache_missunit_internals.yaml +++ b/scripts/icache_related/icache_missunit_internals.yaml @@ -1,76 +1,31 @@ ICacheMissUnit: - "wire _prefetchMSHRs_9_io_req_ready" - "wire _prefetchMSHRs_9_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_9_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_9_io_resp_bits_vSetIdx" - "wire _prefetchMSHRs_8_io_req_ready" - "wire _prefetchMSHRs_8_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_8_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_8_io_resp_bits_vSetIdx" - "wire _prefetchMSHRs_7_io_req_ready" - "wire _prefetchMSHRs_7_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_7_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_7_io_resp_bits_vSetIdx" - "wire _prefetchMSHRs_6_io_req_ready" - "wire _prefetchMSHRs_6_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_6_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_6_io_resp_bits_vSetIdx" - "wire _prefetchMSHRs_5_io_req_ready" - "wire _prefetchMSHRs_5_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_5_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_5_io_resp_bits_vSetIdx" - "wire _prefetchMSHRs_4_io_req_ready" - "wire _prefetchMSHRs_4_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_4_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_4_io_resp_bits_vSetIdx" - "wire _prefetchMSHRs_3_io_req_ready" - "wire _prefetchMSHRs_3_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_3_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_3_io_resp_bits_vSetIdx" - "wire _prefetchMSHRs_2_io_req_ready" - "wire _prefetchMSHRs_2_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_2_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_2_io_resp_bits_vSetIdx" - "wire _prefetchMSHRs_1_io_req_ready" - "wire _prefetchMSHRs_1_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_1_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_1_io_resp_bits_vSetIdx" - "wire _prefetchMSHRs_0_io_req_ready" - "wire _prefetchMSHRs_0_io_acquire_valid" - - "wire [41:0] _prefetchMSHRs_0_io_resp_bits_blkPaddr" - - "wire [7:0] _prefetchMSHRs_0_io_resp_bits_vSetIdx" - "wire _fetchMSHRs_3_io_req_ready" - "wire _fetchMSHRs_3_io_acquire_valid" - - "wire [41:0] _fetchMSHRs_3_io_resp_bits_blkPaddr" - - "wire [7:0] _fetchMSHRs_3_io_resp_bits_vSetIdx" - "wire _fetchMSHRs_2_io_req_ready" - "wire _fetchMSHRs_2_io_acquire_valid" - - "wire [41:0] _fetchMSHRs_2_io_resp_bits_blkPaddr" - - "wire [7:0] _fetchMSHRs_2_io_resp_bits_vSetIdx" - "wire _fetchMSHRs_1_io_req_ready" - "wire _fetchMSHRs_1_io_acquire_valid" - - "wire [41:0] _fetchMSHRs_1_io_resp_bits_blkPaddr" - - "wire [7:0] _fetchMSHRs_1_io_resp_bits_vSetIdx" - "wire _fetchMSHRs_0_io_req_ready" - "wire _fetchMSHRs_0_io_acquire_valid" - - "wire [41:0] _fetchMSHRs_0_io_resp_bits_blkPaddr" - - "wire [7:0] _fetchMSHRs_0_io_resp_bits_vSetIdx" - - "wire _prefetchDemux_io_chosen" - - "wire _prefetchDemux_io_in_ready" - - "wire _prefetchDemux_io_in_valid_T_1" - - "wire _fetchDemux_io_in_ready" - - "wire _fetchDemux_io_in_valid_T_1" - - - "wire prefetchHit" - - "wire fetchHit" - - - "wire last_fire" - - "reg last_fire_r" - - - "wire _priorityFIFO_io_enq_ready" - - "wire _priorityFIFO_io_enq_valid_T_probe" - - "wire _priorityFIFO_io_deq_valid" - - "wire _priorityFIFO_io_deq_ready_T" - - "wire _priorityFIFO_io_deq_bits" diff --git "a/ut_frontend/icache/missunit/ICacheMissUnit\346\250\241\345\235\227\351\252\214\350\257\201\346\212\245\345\221\212.md" "b/ut_frontend/icache/missunit/ICacheMissUnit\346\250\241\345\235\227\351\252\214\350\257\201\346\212\245\345\221\212.md" deleted file mode 100644 index 3d9f1697..00000000 --- "a/ut_frontend/icache/missunit/ICacheMissUnit\346\250\241\345\235\227\351\252\214\350\257\201\346\212\245\345\221\212.md" +++ /dev/null @@ -1,351 +0,0 @@ -# ICacheMissUnit模块验证报告 - -## 1. 基本信息 - -| 项目 | 内容 | -|------|------| -| 验证对象 | ICacheMissUnit模块 | -| 验证人员 | Gui-Yue | -| 验证时间 | 2025-8 | -| 报告版本 | V0.1 | -| 验证框架 | Toffee测试框架 | - -## 2. 验证对象介绍 - -### 2.1 模块概述 -ICacheMissUnit模块是香山开源处理器前端ICache中的关键组件,主要负责指令缓存缺失处理功能。该模块实现了MSHR管理机制,支持多种操作模式包括取指缺失处理、预取缺失处理、TileLink协议通信等。 - -### 2.2 硬件架构 - -ICacheMissUnit包含以下主要组件: - -#### 2.2.1 MSHR管理单元 -- **14个MSHR**(Miss Status Holding Register) - - 4个fetchMSHR(ID: 0-3):处理取指缺失 - - 10个prefetchMSHR(ID: 4-13):处理预取缺失 - -#### 2.2.2 请求仲裁器 -- **fetchArb**: fetch请求间的仲裁 -- **prefetchArb**: prefetch请求间的仲裁 -- **acquireArb**: acquire请求间的仲裁(fetch优先于prefetch) - -#### 2.2.3 TileLink协议接口 -- **Acquire通道**: 向下级发送缺失请求 -- **Grant通道**: 接收下级返回的数据,支持multi-beat传输 - -#### 2.2.4 数据处理单元 -- Grant数据收集与拼装 -- SRAM写回控制 -- 替换策略更新 - -### 2.3 接口信号 -模块主要接口包括: - -#### 2.3.1 时钟复位信号 -- **clock**:系统时钟信号 -- **reset**:系统复位信号 - -#### 2.3.2 IO接口束(io) -- **io.fetch**:取指操作接口 - - **io.fetch.req**:取指请求接口 - - **io.fetch.req.valid**:取指请求有效信号 - - **io.fetch.req.ready**:取指请求就绪信号 - - **io.fetch.req.bits**:取指请求数据位 - - **io.fetch.req.bits.blkPaddr**:块物理地址(42位) - - **io.fetch.req.bits.vSetIdx**:虚拟集合索引(8位) - - **io.fetch.resp**:取指响应接口 - - **io.fetch.resp.valid**:取指响应有效信号 - - **io.fetch.resp.bits**:取指响应数据位 - - **io.fetch.resp.bits.blkPaddr**:块物理地址 - - **io.fetch.resp.bits.vSetIdx**:虚拟集合索引 - - **io.fetch.resp.bits.waymask**:路掩码(4位) - - **io.fetch.resp.bits.data**:缓存数据(512位) - - **io.fetch.resp.bits.corrupt**:数据损坏标志 - -- **io.prefetch_req**:预取请求接口 - - **io.prefetch_req.valid**:预取请求有效信号 - - **io.prefetch_req.ready**:预取请求就绪信号 - - **io.prefetch_req.bits**:预取请求数据位 - - **io.prefetch_req.bits.blkPaddr**:块物理地址(42位) - - **io.prefetch_req.bits.vSetIdx**:虚拟集合索引(8位) - -- **io.mem**:TileLink内存接口 - - **io.mem.acquire**:内存获取接口 - - **io.mem.acquire.valid**:获取请求有效信号 - - **io.mem.acquire.ready**:获取请求就绪信号 - - **io.mem.acquire.bits**:获取请求数据位 - - **io.mem.acquire.bits.source**:源标识(4位) - - **io.mem.acquire.bits.address**:内存地址(48位) - - **io.mem.grant**:内存授权接口 - - **io.mem.grant.valid**:授权响应有效信号 - - **io.mem.grant.ready**:授权响应就绪信号 - - **io.mem.grant.bits**:授权响应数据位 - - **io.mem.grant.bits.source**:源标识(4位) - - **io.mem.grant.bits.opcode**:操作码(3位) - - **io.mem.grant.bits.data**:数据(256位) - - **io.mem.grant.bits.corrupt**:数据损坏标志 - -- **io.victim**:替换策略接口 - - **io.victim.vSetIdx**:替换集合索引接口 - - **io.victim.vSetIdx.valid**:替换索引有效信号 - - **io.victim.vSetIdx.bits**:替换索引值(8位) - - **io.victim.way**:替换路选择(2位) - -- **io.data_write**:数据写入接口 - - **io.data_write.valid**:数据写入有效信号 - - **io.data_write.bits**:数据写入数据位 - - **io.data_write.bits.data**:写入数据(512位) - - **io.data_write.bits.virIdx**:虚拟索引(8位) - - **io.data_write.bits.waymask**:路掩码(4位) - -- **io.meta_write**:元数据写入接口 - - **io.meta_write.valid**:元数据写入有效信号 - - **io.meta_write.bits**:元数据写入数据位 - - **io.meta_write.bits.waymask**:路掩码(4位) - - **io.meta_write.bits.virIdx**:虚拟索引(8位) - - **io.meta_write.bits.phyTag**:物理标签(42位) - - **io.meta_write.bits.bankIdx**:Bank索引(8位) - -- **io.flush**:刷新信号 -- **io.fencei**:指令缓存刷新信号 -- **io.hartId**:硬件线程标识(8位) - -#### 2.3.3 ICacheMissUnit内部接口束 -- **ICacheMissUnit_.fetchMSHRs**:取指MSHR数组(4个) - - **ICacheMissUnit_.fetchMSHRs.{i}.io.req_ready**:MSHR请求就绪信号 - - **ICacheMissUnit_.fetchMSHRs.{i}.io.acquire_valid**:MSHR获取有效信号 -- **ICacheMissUnit_.prefetchMSHRs**:预取MSHR数组(10个) - - **ICacheMissUnit_.prefetchMSHRs.{i}.io.req_ready**:MSHR请求就绪信号 - - **ICacheMissUnit_.prefetchMSHRs.{i}.io.acquire_valid**:MSHR获取有效信号 -- **ICacheMissUnit_.last_fire**:最后拍触发信号 -- **ICacheMissUnit_.fetchHit**:取指命中信号 -- **ICacheMissUnit_.prefetchHit**:预取命中信号 - ---- - -## 3. 验证功能点 -### 3.1 入队操作 (CP28) -MissUnit 内部的 priorityFIFO 负责缓存预取 MSHR 的编号,本功能点覆盖 FIFO 入队路径的各种状态。 -- 功能点CP28.1 队未满,正常入队:当队列未满、`io.enq.ready=1` 且 `io.enq.valid=1` 时可以顺利入队,指针 `enq_ptr.value` 顺序递增且 `enq_ptr.flag` 保持不翻转。 -- 功能点CP28.2 队未满,入队后标记位翻转:当入队指针位于队尾(索引 9)时再次入队,`enq_ptr.value` 回绕为 0 且 `enq_ptr.flag` 翻转,实现环形 FIFO。 -- 功能点CP28.3 队满,入队就绪信号为低,无法入队:当 `(enq_ptr.value == deq_ptr.value) && (enq_ptr.flag ^ deq_ptr.flag)` 为真时视为队满,`io.enq.ready=0` 阻止新的请求,`enq_ptr` 保持不变。 -- 测试用例:TC13 test_FIFO_moudle - 依次送入 10 条请求确认正常入队及指针累加,并通过第 11 条请求观察就绪信号拉低、指针保持不变。 - -### 3.2 出队操作 (CP29) -该功能点验证 priorityFIFO 的出队语义以及环形指针的正确性。 -- 功能点CP29.1 队非空,正常出队:当 FIFO 有效且 `io.deq.ready=1` 时,`io.deq.fire` 触发数据吐出,`deq_ptr.value` 顺序递增。 -- 功能点CP29.2 队非空,出队后标记位翻转:当 `deq_ptr.value` 为 9 时出队,指针回绕至 0 并翻转 `deq_ptr.flag`。 -- 功能点CP29.3 队空,出队有效信号为低:当 `enq_ptr` 与 `deq_ptr` 完全一致时视为队空,`io.deq.valid=0`,出队握手被禁止。 -- 测试用例:TC13 test_FIFO_moudle - 先填满再逐条出队查看指针递增与回绕;TC18 test_fifo_priority_ordering - 在全部元素出队后确认队空时 valid 维持低电平。 - -### 3.3 刷新清空操作(CP30) -flush 信号应即时清空 priorityFIFO 的读写指针和标志位,保证刷新后队列重新可用。 -- 功能点CP30.1 flush 清空:当 `io.flush=1` 时,将 `enq_ptr.value`、`deq_ptr.value`、`flag` 全部复位为 0,同时 `empty=1`、`full=0`。 -- 测试用例:TC05 test_set_flush - 直接驱动 flush 信号验证指针复位;TC13 test_FIFO_moudle - 在 FIFO 填满后施加 flush,确认队列恢复初始状态并可再次入队。 - -### 3.4 处理取指缺失请求 (CP31) -MissUnit 需要接收来自取指端的缺失请求、区分新旧 miss,并按照低索引优先策略分配 fetch MSHR。 -- 功能点CP31.1 接受新的取指请求:当 `io.fetch.req.valid=1` 且 `fetchHit=0` 时,`io.fetch.req.ready=1` 并将请求送入空闲 fetch MSHR。 -- 功能点CP31.2 处理已有的取指请求:当同地址 miss 再次到来时 `fetchHit=1`,`fetchDemux.io.in.valid` 保持 0,表示命中已有 MSHR 并阻止再次分配。 -- 功能点CP31.3 低索引优先进入 MSHR:fetchDemux 以编号 0→3 的顺序寻找空位,优先分配低索引 fetch MSHR。 -- 测试用例:TC07 test_send_fetch_request - 发送不同地址 miss 验证新请求被接收;TC14 test_mshr_hit_detection - 驱动重复地址确认命中判定;TC16 test_low_index_priority_fetch - 连续 miss 观察低索引 MSHR 依次被占用。 - -### 3.5 处理预取缺失请求 (CP32) -预取命中与分配策略与取指通路类似,同时增加 FIFO 排队行为。 -- 功能点CP32.1 接受新的预取请求:当 `io.prefetch_req.valid=1` 且 `prefetchHit=0` 时,`io.prefetch_req.ready=1` 并写入空闲 prefetch MSHR。 -- 功能点CP32.2 处理已有的预取请求:命中已有 MSHR 时 `prefetchHit=1`,请求被吸收但不再下发新的分配。 -- 功能点CP32.3 低索引优先进入 MSHR:prefetchDemux 按索引 0→9 分配空闲 MSHR,优先占用低编号。 -- 功能点CP32.4 先进入 MSHR 的优先进入 prefetchArb:预取 MSHR 的编号按入队顺序写入 priorityFIFO,出队顺序与入队一致,保证仲裁 FIFO 语义。 -- 测试用例:TC08 test_send_prefetch_request - 检查新 miss 被接收;TC14 test_mshr_hit_detection - 复用 fetch miss 建立的 MSHR 观察命中;TC17 test_low_index_priority_prefetch - 验证低索引优先;TC18 test_fifo_priority_ordering - 验证 FIFO 先入先出。 - -### 3.6 MSHR 管理与查找 (CP33) -该功能点覆盖 MSHR 查找命中、资源释放及取指/预取共享命中等场景。 -- 功能点CP33.1 MSHR 查找命中逻辑:新的 fetch/prefetch 请求能够遍历全部 MSHR,并在命中时拉高 `fetchHit` 或 `prefetchHit`。 -- 功能点CP33.2 MSHR 状态的更新与释放:Grant 流程完成后,对应 MSHR 有效位被清零,`io.req.ready` 恢复为 1,允许接收后续 miss。 -- 功能点CP33.3 Prefetch 与 fetch 地址相同时命中:当预取与取指请求地址一致且 fetch valid 时,`prefetchHit` 仍置 1,避免重复 miss。 -- 功能点CP33.4 新请求未命中任何 MSHR:当查找未命中已有 miss 时,`fetchHit`/`prefetchHit` 均为 0,以便分配新槽。 -- 测试用例:TC14 test_mshr_hit_detection - 覆盖命中与未命中分支;TC15 test_mshr_release_after_grant - 验证 Grant 完成后的 MSHR 释放;TC26 test_prefetch_same_address_as_fetch - 观测预取与取指同地址命中;TC07/TC08 - 验证首次 miss 时命中标志为 0。 - -### 3.7 acquireArb 仲裁 (CP34) -acquireArb 负责在 fetch 与 prefetch MSHR 之间进行固定优先级仲裁。 -- 功能点CP34.1 acquireArb 仲裁:当 fetch 与 prefetch 同时请求时,acquireArb 优先输出源 ID < 4 的 fetch 请求。 -- 功能点CP34.2 只有 prefetch 请求时被选中:当所有 fetch MSHR 空闲或无请求时,pre-fetch MSHR 的 acquire 可以被仲裁并送出。 -- 测试用例:TC19 test_acquire_arbitration_priority - 先触发预取再触发取指,记录仲裁顺序并确认 fetch 优先;同用例中在 fetch 完成后验证预取请求最终被送出。 - -### 3.8 Grant 数据接收与 Refill (CP35) -MissUnit 必须正确收集 TileLink Grant 的多拍数据并根据 last_fire 状态完成 refill。 -- 功能点CP35.1/CP35.2 正常完整 Grant 流程:在连续两个 beat 的 Grant 中,`readBeatCnt` 递增、`respDataReg` 收集数据,最后一个 beat 置 `last_fire=1`,下一拍 `last_fire_r=1`。 -- 功能点CP35.3 Grant 完成后释放 MSHR:`last_fire_r=1` 时,根据 `grant.bits.source` 无效化对应 MSHR。 -- 功能点CP35.4 Grant 带有 corrupt 标志:当 `grant.bits.corrupt=1` 时,模块记录 `corrupt_r`,后续响应携带污染标志。 -- 测试用例:TC20 test_grant_beat_collection - 手动逐拍驱动 Grant 检查 beat 累计与 last_fire;TC10 test_api_full_fetch_flow - 完整 miss 流程中验证 last_fire_r 与 MSHR 释放;TC11 test_api_grant_with_corruption - 检查 corrupt 标志传递。 - -### 3.9 替换策略更新 (CP36) -当 acquire 成功发送时需要通知替换策略更新被淘汰的路,同时在响应阶段生成 waymask。 -- 功能点CP36.1 正常替换更新:当 `io.mem.acquire.fire` 成功时,`io.victim.vSetIdx.valid` 拉高并输出正在替换的集合索引。 -- 功能点CP36.2 生成 waymask:在 Grant 完成后,根据 L2 返回的 `mshr_resp.bits.way` 生成独热 `waymask`,用于 SRAM 写回与响应。 -- 测试用例:TC21 test_victim_way_update - 在 acquire fire 时读取 victim 接口确认 valid 与 bits;TC22 test_waymask_generation - 通过设置不同 victim way 并完成 Grant,检查响应中的 waymask。 - -### 3.10 写回 SRAM (CP37) -当 miss refill 完整返回时,需要根据刷新/腐化状态决定是否写回 meta/data SRAM。 -- 功能点CP37.1 生成写使能:在 `last_fire_r=1` 且无 flush/fencei/corrupt 条件下,`io.meta_write.valid` 与 `io.data_write.valid` 同时拉高。 -- 功能点CP37.2 正常写入内容:写口携带的 virIdx、phyTag、data、waymask 等字段与 MSHR 记录一致。 -- 功能点CP37.3 有 flush/fencei 时不写 SRAM:当 flush 或 fencei 有效时,即使数据返回也保持写 SRAM 使能为 0。 -- 功能点CP37.4 处理 corrupt 数据:当 `corrupt_r=1` 时禁止写 SRAM,但依旧产生 fetch 响应并将 corrupt 标志向外传播。 -- 测试用例:TC23 test_sram_write_conditions - 验证正常写回路径;TC24 test_no_write_with_flush_fencei - 在 flush/fencei 或 corrupt 条件下确认写使能抑制且返回带有 corrupt 标志。 - -### 3.11 向 mainPipe/prefetchPipe 发出 Miss 完成响应(CP38) -MissUnit 在 Grant 完成后一拍需要向取指前端返回 miss 完成信息。 -- 功能点CP38.1 正常 Miss 完成响应:当 `last_fire_r=1` 时,`io.fetch_resp.valid=1`,并输出 blkPaddr、vSetIdx、waymask、data、corrupt 等字段。 -- 测试用例:TC10 test_api_full_fetch_flow - 完整 miss 流程中检查 fetch_resp 的内容;TC11 test_api_grant_with_corruption - 验证响应携带 corrupt 标志并仍然有效;TC23 test_sram_write_conditions - 在正常写回场景中确认 fetch_resp 数据与写回字段一致。 - -### 3.12 处理 flush / fencei (CP39) -Flush/Fencei 需要同时作用于未发射与已发射的 miss,确保刷新语义正确。 -- 功能点CP39.1 MSHR 未发射前 fencei:当 `io.fencei=1` 时,fetch/prefetch MSHR 的 `io.req.ready` 与 `io.acquire.valid` 均被拉低,未发射请求被取消。 -- 功能点CP39.2 MSHR 未发射前 flush:`io.flush` 仅阻塞 prefetch MSHR(fetch MSHR 的 flush 恒为 0),此时预取请求 ready 拉低而取指通路仍可发射。 -- 功能点CP39.3 MSHR 已发射后 flush/fencei:当请求已发射且收到刷新信号时,不再写 SRAM,但仍保持 fetch 响应有效。 -- 测试用例:TC04 test_fencei_work - 在 fencei 拉高时检查所有 MSHR ready/valid 被清空;TC25 test_flush_fencei_mshr_behavior - 对 flush 与 fencei 分别验证取指/预取通路的阻断范围;TC24 test_no_write_with_flush_fencei - 在数据返回时施加刷新,确认写回抑制但响应仍产生。 - ---- - -## 4. 验证方案 - -### 4.1 验证策略 -采用基于Python的Toffee验证框架,通过以下方式进行验证: -- **单元测试**:针对每个功能点设计独立测试用例 -- **随机测试**:使用随机数据验证模块的鲁棒性 -- **边界测试**:验证极限条件下的模块行为 -- **功能覆盖**:确保所有功能点都被充分测试 - -### 4.2 验证环境 -- **测试框架**:Toffee -- **DUT封装**:DUTICacheMissUnit -- **环境类**:ICacheMissUnitEnv -- **代理类**:ICacheMissUnitAgent -- **信号束**:ICacheMissUnitBundle - -### 4.3 覆盖率策略 -- **行覆盖率**:通过LCOV工具统计代码行覆盖情况 -- **功能覆盖率**:定义覆盖组和覆盖点,确保功能完整性 -- **断言覆盖**:在关键路径添加断言检查 - -## 5. 测试用例 - -### 5.1 测试用例列表 - -| 序号 | 测试用例名称 | 测试目标 | -|------|-------------|----------| -| TC01 | test_smoke | 基本功能冒烟测试 | -| TC02 | test_bundle_drive_fetch_req_inputs | 验证Bundle接口fetch请求驱动 | -| TC03 | test_bundle_read_fetch_req_ready | 验证Bundle接口ready信号读取 | -| TC04 | test_fencei_work | 验证Fencei功能 | -| TC05 | test_set_flush | 验证Flush信号控制 | -| TC06 | test_set_victim_way | 验证Victim Way设置 | -| TC07 | test_send_fetch_request | 验证Fetch请求发送 | -| TC08 | test_send_prefetch_request | 验证Prefetch请求发送 | -| TC09 | test_api_fetch_request_generates_acquire | 验证Fetch请求生成Acquire | -| TC10 | test_api_full_fetch_flow | 验证完整Fetch流程 | -| TC11 | test_api_grant_with_corruption | 验证Grant数据损坏处理 | -| TC12 | test_api_full_prefetch_flow | 验证完整Prefetch流程 | -| TC13 | test_FIFO_moudle | 验证FIFO模块综合功能 | -| TC14 | test_mshr_hit_detection | 验证MSHR命中检测 | -| TC15 | test_mshr_release_after_grant | 验证MSHR释放 | -| TC16 | test_low_index_priority_fetch | 验证Fetch MSHR低索引优先级 | -| TC17 | test_low_index_priority_prefetch | 验证Prefetch MSHR低索引优先级 | -| TC18 | test_fifo_priority_ordering | 验证FIFO优先级顺序 | -| TC19 | test_acquire_arbitration_priority | 验证Acquire仲裁优先级 | -| TC20 | test_grant_beat_collection | 验证Grant多Beat数据收集 | -| TC21 | test_victim_way_update | 验证Victim Way更新 | -| TC22 | test_waymask_generation | 验证Waymask生成 | -| TC23 | test_sram_write_conditions | 验证SRAM写回条件 | -| TC24 | test_no_write_with_flush_fencei | 验证Flush/Fencei写回抑制 | -| TC25 | test_flush_fencei_mshr_behavior | 验证Flush/Fencei MSHR行为 | -| TC26 | test_prefetch_same_address_as_fetch | 验证相同地址处理 | -| TC27 | test_demux_chosen_signal | 验证Demux选择信号 | - -### 5.2 测试数据 -- **固定测试向量**:使用预定义的测试地址确保测试的可重复性 -- **边界值测试**:包含满队列、空队列等边界条件测试 -- **异常情况测试**:包含异常情况测试 -- **递增地址模式**:使用地址递增模式生成测试数据 - -## 6. 测试环境 - -### 6.1 硬件环境 -- 仿真器:Verilator -- 操作系统:Linux - -### 6.2 软件环境 -- Python测试框架:Toffee -- 覆盖率工具:LCOV -- 波形查看:FST格式文件 - -### 6.3 文件结构 -``` -missunit/ -├── test/ -│ ├── missunit_test.py # 主测试文件 -│ └── missunit_fixture.py # 测试fixture -├── env/ -│ ├── missunit_env.py # 测试环境 -│ └── missunit_functionalcoverage.py # 功能覆盖率 -├── agent/ -│ └── missunit_agent.py # 测试api -└── bundle/ - └── missunit_bundle.py # bundle定义 -``` - -## 7. 测试结果分析 - -### 7.1 测试通过率 -- **总测试用例数**:29 -- **通过用例数**:29 -- **失败用例数**:0 -- **通过率**:100% - -### 7.2 覆盖率分析 - -#### 7.2.1 行覆盖率 -- **总体覆盖率**:96.5% (1479/1532行) -- **ICacheMissUnit.v**:91.2% (104/114行) -- **ICacheMissUnit_top.sv**:86.8% (264/304行) - -#### 7.2.2 功能覆盖率 -- **总体功能覆盖率**:100% -- **覆盖点总数**:29 -- **已覆盖点数**:29 -- **覆盖组数量**:11 - -### 7.3 覆盖率详细分析 -功能覆盖率达到100%,说明所有定义的功能点都被充分测试。行覆盖率96.5%属于较高水平,未覆盖的3.5%主要集中在: -- 错误处理分支 -- 极端异常情况 -- 部分初始化代码路径 - - ---- - -## 8. 缺陷分析 - -### 8.1 发现缺陷 -测试过程中未发现功能性缺陷,所有测试用例均通过。 - -### 8.2 潜在风险点 -- ICacheMissUnit_top.sv的覆盖率相对较低(86.8%),建议增加针对顶层模块的测试 -- 部分边界条件可能需要更多测试用例 - -## 9. 测试结论 - -### 9.1 验证完成度 -- √ 所有规划的功能点均已验证 -- √ 功能覆盖率达到100% -- √ 行覆盖率达到96.5%,满足验证要求 -- √ 所有测试用例通过 - -### 9.2 模块质量评估 -ICacheMissUnit模块验证充分,功能实现正确,质量良好。模块在各种测试场景下表现稳定,满足设计要求。 - -### 9.3 改进需求 -1. 需要增加对ICacheMissUnit_top.sv顶层模块的测试覆盖 - -### 9.4 验证结论 -**ICacheMissUnit模块验证通过**,可以进入下一阶段的集成验证。 diff --git "a/ut_frontend/icache/missunit/Missunit\346\250\241\345\235\227\351\252\214\350\257\201\346\212\245\345\221\212.md" "b/ut_frontend/icache/missunit/Missunit\346\250\241\345\235\227\351\252\214\350\257\201\346\212\245\345\221\212.md" new file mode 100644 index 00000000..40b33737 --- /dev/null +++ "b/ut_frontend/icache/missunit/Missunit\346\250\241\345\235\227\351\252\214\350\257\201\346\212\245\345\221\212.md" @@ -0,0 +1,269 @@ +# MissUnit模块验证报告 + +## 1. 基本信息 + +| 项目 | 内容 | +|------|------| +| 验证对象 | ICache MissUnit 模块 | +| 验证人员 | Gui-Yue | +| 验证时间 | 2025-11 | +| 报告版本 | V0.2 | +| 验证框架 | Toffee 测试框架 | + +## 2. 验证对象介绍 + +### 2.1 模块概述 +MissUnit 位于香山前端 ICache 的 miss 处理管线,其职责是接收取指和预取 miss,请求下行 TileLink 层获得 cache line,并将回填结果写入 data/meta 阵列。模块内部同时维护 4 个取指 MSHR、10 个预取 MSHR 以及一个带优先级的 FIFO,以保证 fetch/prefetch miss 可以并行处理、避免重复 miss,并与 victim 选择及 cache 写回流程配合。 + +### 2.2 主要功能 +- **Fetch miss 管理**:对 `io.fetch.req` 进行命中判定、队列分发与 MSHR 分配,命中时直接回送 ready,未命中则生成 Acquire。 +- **Prefetch miss 管理**:维护预取专用 MSHR,并通过 priority FIFO 与 Acquire 仲裁器交互,保证预取请求顺序。 +- **MSHR 查找与合并**:14 路 MSHR 均支持根据 `blkPaddr/vSetIdx` 做 hit 检查,避免重复请求并提供 victim way 信息。 +- **Acquire/Grant 协作**:5 路仲裁器将 fetch 与 prefetch 的 Acquire 合并发送,同时在 Grant 返回后将数据拼成 512-bit 响应,回写 meta/data,并驱动 difftest。 +- **替换/写回接口**:对外输出 `io.victim`, `io.meta_write`, `io.data_write`,并与 flush/fencei 控制联动,保证异常处理时能清空状态。 + +### 2.3 接口信号 + +#### 2.3.1 时钟复位 +- `clock`, `reset` + +#### 2.3.2 `io` 顶层接口 + +| 子接口 | 握手/方向 | 字段说明 | +|--------|-----------|----------| +| `io.fencei` | in | FenceI 控制,高电平时阻断新 miss 并禁止写阵列。 | +| `io.flush` | in | Flush 控制,仅作用于预取路径。 | +| `io.hartId` | in | Hart ID,随 difftest 输出。 | +| `io.fetch.req` | ready/valid(输入) | `blkPaddr[41:0]`, `vSetIdx[7:0]` | +| `io.fetch.resp` | ready/valid(输出) | `blkPaddr`, `vSetIdx`, `waymask[3:0]`, `data[511:0]`, `corrupt` | +| `io.prefetch_req` | ready/valid(输入) | 字段同 fetch req | +| `io.victim.vSetIdx` | valid/bits(输出) | MissUnit 对外报告的 victim 虚拟集合 | +| `io.victim.way` | out | Victim way 选择 | +| `io.mem.acquire` | ready/valid(输出) | `source[3:0]`, `address[47:0]` | +| `io.mem.grant` | valid(输入) | `opcode[3:0]`, `size[2:0]`, `source[3:0]`, `data[255:0]`, `corrupt`(两拍合成 512-bit) | +| `io.meta_write` | valid(输出) | `virIdx[7:0]`, `phyTag[35:0]`, `waymask[3:0]`, `bankIdx` | +| `io.data_write` | valid(输出) | `virIdx[7:0]`, `data[511:0]`, `waymask[3:0]` | + +#### 2.3.3 `ICacheMissUnit_` 调试层级 + +`bundle.ICacheMissUnit_` 公开了内部结构,便于白盒验证: + +- `_prefetchMSHRs._0 ... _9._io`:每个预取 MSHR 的端口,包括 `_req_ready`、`_acquire_valid`、`_invalid`、`_lookUps_0._hit`、`_lookUps_1._hit`、`_resp_bits.{_blkPaddr,_vSetIdx,_way}` 等信号。 +- `_fetchMSHRs._0 ... _3._io`:与预取 MSHR 相同的观测点。 +- Prefetch priority FIFO:在同层级下,可读取 `enq_ptr_value/flag`、`deq_ptr_value/flag`、`full`、`io_enq_ready`、`io_deq_ready` 以及 `_prefetchDemux_io_chosen`,用于 FIFO 行为检查。 + +## 3. 验证功能点 + +### 3.1 基础控制与 API(非 CP) + +1. **Bundle / Agent API 连通性** + - 场景:直接操作 `ICacheMissUnitBundle` 的 fetch 请求、`fencei` 信号,确认写入后一个周期即可从 bundle 读回。 + - 检查:`io.fetch.req.valid`, `io.fetch.req.bits.blkPaddr`, `io.fencei` 等寄存器能在 step 后保持设置值。 + - 用例:TC02 `test_bundle_drive_fetch_req_inputs`。 + +2. **Flush / FenceI 快速冒烟测试** + - 场景:通过 Agent 拉高 `fencei`,等待 10 个周期,再次拉低。 + - 检查:冒烟测试期间不存在断言或仿真崩溃;用于快速确认仿真初始化正确。 + - 用例:TC01 `test_smoke`。 + +3. **Victim/Flush API 设置** + - 场景:使用 Agent API 轮流设置 `io.flush`、四种 `victim_way`,并在 bundle 中回读。 + - 检查:Flush 拉高时 `bundle.io._flush` = 1,victim_way 依次为 0~3。 + - 用例:TC04 `test_set_flush`、TC05 `test_set_victim_way`。 + +4. **Fetch / Prefetch API 测试流** + - 场景:`drive_send_fetch_request` / `drive_send_prefetch_req` 在队列空/满、重复 miss 等场合多次调用。 + - 检查:`send_success`、`bundle.io._fetch._req._ready` 的状态切换;prefetch 第 11 次请求被阻塞再放行。 + - 用例:TC06 `test_send_fetch_request`、TC10 `test_send_prefetch_request`。 + +5. **Acquire / Grant 全流程** + - 场景:发送 miss -> 捕获 `io.mem.acquire` -> 拉高 ready 完成握手 -> 模拟 Grant -> 等待 fetch_resp。 + - 检查:Acquire 地址 `blkPaddr << 6`、source ID、Victim 路设置与对应的独热编码 `waymask`;Grant 分两拍写入,`corrupt` 标志在响应链路中保持一致。 + - 用例:TC07 `test_api_fetch_request_generates_acquire`, TC08 `test_api_full_fetch_flow`, TC09 `test_api_grant_with_corruption`, TC11 `test_api_full_prefetch_flow`. + +### 3.2 Priority FIFO 功能(CP28~CP30) + +**CP28 入队行为** +测试用例:TC12 `test_FIFO_moudle_CP28_CP29_enq_and_deq_operation` +- CP28.1 队未满正常入队:一次性注入 9 个预取 miss,`enq_ptr_value` 逐项累加、`enq_ready=1`,说明 FIFO 在有空位时可以连续握手。 +- CP28.2 入队后指针翻转:当写指针指向 9 时再次入队,观测 `enq_ptr_value` 回到 0、`enq_ptr_flag` 翻转、`full` 置 1。 +- CP28.3 队满无法入队:继续发送请求,`io.prefetch_req.ready` 与 `priorityFIFO.io_enq_ready` 均为 0,API 返回 `send_success=False`。 + +**CP29 出队行为** +测试用例:TC12 `test_FIFO_moudle_CP28_CP29_enq_and_deq_operation` +- CP29.1 正常出队:拉高 `io.mem.acquire.ready` 触发 acquireArb,`deq_ptr_value` 依次递增。 +- CP29.2 出队后指针翻转:当 `deq_ptr_value` 为 9 时再出队,指针回到 0 且 `deq_ptr_flag` 翻转。 +- CP29.3 队空阻塞:FIFO 被完全取走后,`deq_valid=0`、`priorityFIFO.io_deq_ready=0`,额外出队不会改变指针。 + +**CP30 Flush 行为** +测试用例:TC13 `test_FIFO_moudle_CP30_flush_operation` +- 拉高 `io.flush` 时,`enq_ptr`、`deq_ptr` 以及 flag 均复位,`full=0`、`enq_ready=1`。Flush 后重新发起预取请求,队列能再次入队,证明状态确实被清空。 + +### 3.3 Miss pipeline 功能(CP31~CP39) + +**CP31 处理取指缺失** +测试用例:TC14 `test_MISSUNIT_CP31_fetch_miss_process` +- CP31.1 新 miss 进入:当 `fetchHit=0`、`io.fetch.req.valid=1` 时,`io.fetch.req.ready=1` 并通过 `fetchDemux` 分发到最低索引的空闲 MSHR。 +- CP31.2 命中已有 miss:再次发送相同地址,`fetchHit=1`,`fetchDemux.io.in.valid=0` 但 `ready` 仍为 1,实现“表面接收、实际不入队”。 +- CP31.3 低索引优先:同一周期多个 miss 时,`fetchDemux.io.chosen` 总是选择编号最小的空闲 MSHR。 + +**CP32 处理预取缺失** +测试用例:TC15 `test_MISSUNIT_CP32_prefetch_miss_process` +- CP32.1 新预取 miss 入队:`prefetchHit=0` 时 `prefetchDemux` 将请求写入第一个空闲的 prefetch MSHR,同时将索引放入 priority FIFO。 +- CP32.2 命中已有预取:再次发送同址请求,`prefetchHit=1`,`prefetchDemux` 不再 握手,但 `io.prefetch.req.ready=1`。 +- CP32.3 低索引优先:多条预取 miss 同到时,`prefetchDemux.io.chosen` 总是最小空闲索引。 +- CP32.4 FIFO 顺序:结合 TC12/TC13 观察 `prefetchArb` 的 出队 顺序与 priority FIFO 入队顺序一致。 + +**CP33 MSHR 管理与查找** +测试用例:TC16 `test_MISSUNIT_CP33_MSHR_manage`、TC23 `test_MISSUNIT_addational_all_mshr_lookup_coverage` +- CP33.1 Fetch 查找命中:通过内部信号确认 fetch lookUp 判定 `fetchHit`。 +- CP33.2 Prefetch 查找命中:同理验证 prefetch lookUp。 +- CP33.3 Prefetch 与 Fetch 同址:`test_MISSUNIT_addational_all_mshr_lookup_coverage` 构造 fetch/prefetch 同地址,`prefetchHit` 仍会被置高。 +- CP33.4 MSHR 释放:Grant 返回后,对应 MSHR 的 `valid` 拉低,腾出入口。 + +**CP34 Acquire 仲裁** +测试用例:TC18 `test_MISSUNIT_CP34_acquireArb_arbitration` +- 构造 fetch 与 prefetch 同时有 acquire,`acquireArb.io.out` 优先选择 fetch (source 0~3),仅在 fetch 空闲时才向 prefetch 的 source(4~13) 放行。 + +**CP35 Grant / Refill** +测试用例:TC19 `test_MISSUNIT_CP35_grant_accept_and_refill`、TC09 `test_api_grant_with_corruption` +- CP35.1 第 1 beat:`io.mem_grant.valid=1` 时,数据写入 `respDataReg_0`,`readBeatCnt` 从 0 -> 1。 +- CP35.2 第 2 beat:继续 握手,数据写入 `respDataReg_1`,`readBeatCnt` 回到 0,`last_fire` 置 1。 +- CP35.3 MSHR 失效:`last_fire_r=1` 后,根据 source ID 拉高对应 MSHR 的 `io.invalid`。 +- CP35.4 带 `corrupt` 的 Grant:在第二个 beat 设置 `corrupt=1`,`corrupt_r` 拉高并反映到 fetch_resp;若下一次 Grant 正常,该标志被清零。 + +**CP36 替换策略更新** +测试用例:TC20 `test_MISSUNIT_CP36_Replacer`、TC08 `test_api_full_fetch_flow` +- CP36.1 Acquire 时更新 vSetIdx:当 `io.mem.acquire.ready` 与仲裁输出握手时,`io.victim.vSetIdx.valid=1` 且值等于该 miss 的虚拟集合号。 +- CP36.2 Waymask 生成:Grant 完成后,`response["waymask"]` 等于 `1 << victim_way`,同时内部 `mshr_resp_way` 与期望一致。 + +**CP37 SRAM 写回** +测试用例:TC21 `test_MISSUNIT_CP37_SRAM_writeback` +- CP37.1 正常写回:没有 flush/fencei 且 `corrupt_r=0` 时,`io.meta_write.valid`、`io.data_write.valid` 均为 1,字段内容与 MSHR 记录匹配。 +- CP37.2 有 flush/fencei 或 corrupt:在 flush/fencei 拉高或 Grant 标记 `corrupt` 的场景中,写回有效信号保持 0。 + +**CP38 Miss Completion** +测试用例:TC22 `test_MISSUNIT_CP38_mainpipe_iprefetchpipe_response` +- Grant 全部返回后,无论 flush/fencei 是否发生,`io.fetch_resp.valid=1`,并将 `blkPaddr/vSetIdx/waymask/data/corrupt` 正确送回 mainpipe/预取管线。 + +**CP39 Flush/FenceI 处理** +测试用例:TC03 `test_fencei_work`、TC04 `test_set_flush`、TC23 `test_MISSUNIT_CP39_flush_fencei_operation` +- CP39.1 fencei 在发射前:拉高 fencei 时,所有 MSHR 的 `io.req.ready` 与 `io.acquire.valid` 变低,新的 miss 不再发射。 +- CP39.2 flush 在发射前:flush 只影响 prefetch MSHR,拉高时仅允许 fetch 请求继续发射。 +- CP39.3 已发射后的 flush/fencei:当请求已经发出,再出现 flush/fencei 时,Grant 数据仍需接收但不写 SRAM;`io.fetch_resp.valid` 仍会如期拉高。 + +综上,所有 CP 功能点均在测试集中得到覆盖,且每个场景明确对应的 Toffee 用例可复现。 + +## 4. 验证方案 + +### 4.1 验证目标 +- 覆盖 fetch/prefetch miss 的全链路控制与数据路径。 +- 证明 priority FIFO、MSHR 查找、Arbiter、Replacer 在所有 CP(31~39)下行为正确。 +- 验证 API 层能够驱动/观测关键信号,便于系统集成。 +- 收集足够的行覆盖率与功能覆盖率,确保各类边界场景(flush/fencei、corrupt、FIFO 指针回绕)被触发。 + +### 4.2 验证环境 +- **DUT**:`DUTICacheMissUnit` +- **环境类**:`ICacheMissUnitEnv` +- **Agent**:`ICacheMissUnitAgent`(提供 fetch/prefetch/acquire/grant API) +- **Bundle**:`ICacheMissUnitBundle` +- **仿真器**:Verilator + Toffee runtime + +### 4.3 覆盖率策略 +- **代码覆盖**:基于 Verilator LCOV,目标文件包括 `ICacheMissUnit.v`、`ICacheMissUnit_top.sv` 以及 10+ MSHR/Mux 组件,统计 line/toggle/branch/expression。 +- **功能覆盖**:在 `missunit_coverage.py` 中定义 4 组覆盖(Basic/FIFO/Main/Timing),共 19 个覆盖点、40 个 bin,覆盖 API 控制、FIFO 事件、MSHR/Arbiter/Grant/Flush 时序。 +- **断言覆盖**:继承 RTL 中 `assert` 语句,对 FIFO handshake、refill `last_fire` 等关键条件进行运行时校验。 + +## 5. 测试用例 + +### 5.1 用例列表 + +| 序号 | 用例名称 | 目标 | +|------|---------|------| +| TC01 | test_smoke | 基础 fencei 触发冒烟测试 | +| TC02 | test_bundle_drive_fetch_req_inputs | 验证 bundle 可写 fetch_req/fencei | +| TC03 | test_fencei_work | 确认 fencei 清空所有 MSHR | +| TC04 | test_set_flush | flush API 高低电平切换 | +| TC05 | test_set_victim_way | victim way 编程覆盖四路 | +| TC06 | test_send_fetch_request | fetch_req 发起与 ready 行为 | +| TC07 | test_api_fetch_request_generates_acquire | fetch miss 触发 Acquire | +| TC08 | test_api_full_fetch_flow | Fetch miss 全流程(victim、Grant、response) | +| TC09 | test_api_grant_with_corruption | Grant 携带 `corrupt` 标志的传递 | +| TC10 | test_send_prefetch_request | 预取 miss 配额与重复请求 | +| TC11 | test_api_full_prefetch_flow | Prefetch miss 全流程 | +| TC12 | test_FIFO_moudle_CP28_CP29_enq_and_deq_operation | FIFO 入/出队及指针翻转 | +| TC13 | test_FIFO_moudle_CP30_flush_operation | FIFO flush 行为 | +| TC14 | test_MISSUNIT_CP31_fetch_miss_process | Fetch miss 分类与命中 | +| TC15 | test_MISSUNIT_CP32_prefetch_miss_process | Prefetch miss 分类与命中 | +| TC16 | test_MISSUNIT_CP33_MSHR_manage | 14 路 MSHR 查找逻辑 | +| TC17 | test_MISSUNIT_addational_all_mshr_lookup_coverage | 极端查找组合补充 | +| TC18 | test_MISSUNIT_CP34_acquireArb_arbitration | Acquire 仲裁及 source ID | +| TC19 | test_MISSUNIT_CP35_grant_accept_and_refill | Grant 数据节拍收集与 last_fire 判定 | +| TC20 | test_MISSUNIT_CP36_Replacer | Victim 路/waymask 更新 | +| TC21 | test_MISSUNIT_CP37_SRAM_writeback | SRAM 写/flush/corrupt 路径 | +| TC22 | test_MISSUNIT_CP38_mainpipe_iprefetchpipe_response | miss completion 响应 | +| TC23 | test_MISSUNIT_CP39_flush_fencei_operation | Flush/FenceI 在不同阶段动作 | + +### 5.2 测试数据 +- **固定向量与序列**:地址、集合索引用固定步长生成,便于复现。 +- **并发交互**:Acquire/Grant/Prefetch 等接口使用 Toffee 的多协程等待,确保握手次序可控。 +- **特殊场景**:Grant corrupt、flush/fencei 插入、MSHR hit/miss 全组合。 + +## 6. 测试环境 + +### 6.1 硬件/软件 +- 仿真:Verilator 5.038 +- 主机:x86_64 Linux +- Python 3.10 + Toffee + pytest-asyncio + +### 6.2 目录结构 +``` +missunit/ +├── agent/ # ICacheMissUnitAgent,封装 API +├── bundle/ # ICacheMissUnitBundle 结构 +├── env/ # Env、功能覆盖定义 +├── test/ # missunit_test.py 及 fixture +└── Missunit模块验证报告.md +``` + +## 7. 测试结果分析 + +### 7.1 通过率 +- **总用例数**:23 +- **通过**:23 +- **失败**:0 +- **通过率**:100% + +### 7.2 覆盖率 + +#### 7.2.1 代码覆盖 +- **行覆盖**:83.86%(1335/1592) +- **Toggle 覆盖**:84.80%(8006/9441) +- **分支覆盖**:89.31%(259/290) +- **表达式覆盖**:97.01%(324/334) +- **关键文件**: + - `ICacheMissUnit.v`:80.34%(94/117),未覆盖的部分集中在assert以及reset状态 + - `ICacheMSHR_*`, `MuxBundle`, `FIFOReg` 等辅助模块都完成覆盖,未覆盖的部分集中在reset状态。 + +#### 7.2.2 功能覆盖 +- **覆盖组**:4 组(Basic / FIFO / Main / Timing) +- **覆盖点**:19 +- **Bins**:40 +- **覆盖率**:100%(所有 once/hinted bin 均触发) +- **亮点**:CP33 的 4 类 MSHR hit/merge、CP37 SRAM 写回、CP39 flush/fencei 前后时序 均有对应例程触发。 + +### 7.3 结果解读 +- 行覆盖低于 90% 的部分集中在顶层 wrapper 与某些异常分支(如 `refill_done` 断言失败路径)。这些路径需要特制用例(例如强制 TL 错误)才能覆盖。 +- 功能覆盖完整说明规划的 MissUnit CP 已全部执行,特别是 priority FIFO 与 flush/fencei 时序,覆盖点统计已确认 40 个 bin 全命中。 + +## 8. 缺陷与风险 +- 本轮测试未发现功能性缺陷。 +- 风险点: + 1. `ICacheMissUnit_top.sv` 覆盖率较低,建议在系统级压测中插入额外的 DPI/monitor 流程以覆盖剩余路径。 + 2. 未针对 TL 错误、Grant 超时等极端情形编写测试,可能遗漏异常恢复路径。 + +## 9. 结论 +- √ 规划的功能点全部验证,功能覆盖率 100%。 +- √ 23 个用例全部通过,API/FIFO/MSHR/时序场景均被覆盖。 + +综合结论:**MissUnit 模块当前验证结果通过**,满足集成验证准入条件,可进入下一阶段测试。 diff --git a/ut_frontend/icache/missunit/agent/missunit_agent.py b/ut_frontend/icache/missunit/agent/missunit_agent.py index 26f9d0b2..4eca656d 100644 --- a/ut_frontend/icache/missunit/agent/missunit_agent.py +++ b/ut_frontend/icache/missunit/agent/missunit_agent.py @@ -160,7 +160,9 @@ async def drive_respond_with_grant(self, data_beats: list, beat_size_code: int = 6, op_code: int = 5, - is_corrupt_list: list = None + is_corrupt_list: list = None, + pre_beat_hook=None, + post_beat_hook=None ): num_beats = len(data_beats) if is_corrupt_list is None: @@ -182,9 +184,14 @@ async def drive_respond_with_grant(self, self.bundle.io._mem._grant._valid.value = 1 toffee.info(f"Sending Grant beat {i+1}/{num_beats}: data={hex(current_beat_data)}, corrupt={current_corrupt}") + if pre_beat_hook is not None: + await pre_beat_hook(i) + await self.bundle.step() + if post_beat_hook is not None: + await post_beat_hook(i) + self.bundle.io._mem._grant._valid.value = 0 await self.bundle.step() - self.bundle.io._mem._grant._valid.value = 0 toffee.info(f"Grant transmission finished for source_id={source_id}.") async def drive_get_fetch_response(self, timeout_cycles: int = 20) -> dict | None: @@ -204,4 +211,4 @@ async def drive_get_fetch_response(self, timeout_cycles: int = 20) -> dict | Non await self.bundle.step() toffee.info(f"Timeout: Did not capture fetch response after {timeout_cycles} cycles.") - return None \ No newline at end of file + return None diff --git a/ut_frontend/icache/missunit/bundle/missunit_bundle.py b/ut_frontend/icache/missunit/bundle/missunit_bundle.py index f1b89438..c8f40a33 100644 --- a/ut_frontend/icache/missunit/bundle/missunit_bundle.py +++ b/ut_frontend/icache/missunit/bundle/missunit_bundle.py @@ -2,7 +2,6 @@ class _0Bundle(Bundle): _acquire_valid, _req_ready = Signals(2) - _resp_bits_blkPaddr, _resp_bits_vSetIdx = Signals(2) class _1Bundle(Bundle): _io = _0Bundle.from_prefix("_io") @@ -28,8 +27,6 @@ class _3Bundle(Bundle): class _4Bundle(Bundle): _prefetchMSHRs = _3Bundle.from_prefix("_prefetchMSHRs") _fetchMSHRs = _2Bundle.from_prefix("_fetchMSHRs") - last_fire_r, last_fire = Signals(2) - fetchHit, prefetchHit = Signals(2) class _5Bundle(Bundle): _data, _virIdx, _waymask = Signals(3) @@ -62,7 +59,6 @@ class _12Bundle(Bundle): class _13Bundle(Bundle): _bits = _12Bundle.from_prefix("_bits") _valid, _ready = Signals(2) - _valid_T_probe,_ready_T = Signals(2) class _14Bundle(Bundle): _source, _corrupt, _data, _size, _opcode = Signals(5) @@ -98,19 +94,7 @@ class _21Bundle(Bundle): _prefetch_req = _8Bundle.from_prefix("_prefetch_req") _fencei, _flush, _hartId = Signals(3) -class _22Bundle(Bundle): - _io_enq = _13Bundle.from_prefix("_io_enq") - _io_deq = _13Bundle.from_prefix("_io_deq") - _io_deq_bits = Signal() - - -class _24Bundle(Bundle): - _io_chosen, _io_in_ready, _io_in_valid_T_1 = Signals(3) - class ICacheMissUnitBundle(Bundle): io = _21Bundle.from_prefix("io") - priorityFIFO = _22Bundle.from_prefix("ICacheMissUnit__priorityFIFO") ICacheMissUnit_ = _4Bundle.from_prefix("ICacheMissUnit_") - prefetchDemux = _24Bundle.from_prefix("ICacheMissUnit__prefetchDemux") - fetchDemux = _24Bundle.from_prefix("ICacheMissUnit__fetchDemux") reset, clock = Signals(2) diff --git a/ut_frontend/icache/missunit/env/missunit_coverage.py b/ut_frontend/icache/missunit/env/missunit_coverage.py index 7f66a594..8bea3586 100644 --- a/ut_frontend/icache/missunit/env/missunit_coverage.py +++ b/ut_frontend/icache/missunit/env/missunit_coverage.py @@ -1,6 +1,120 @@ -import toffee.funcov as fc +from comm import module_name_with, UT_FCOV from toffee.funcov import CovGroup +MISSUNIT_TEST_PREFIX = "../../test/missunit_test" + + +def _mark_tests(names): + """Helper to build fully-qualified test names for reverse mapping.""" + return module_name_with(names, MISSUNIT_TEST_PREFIX) + + +def define_basic_coverage(bundle, dut): + """ + Basic API/control coverage shared by smoke/bundle tests. + """ + g = CovGroup(UT_FCOV("MissUnit_Basic_Coverage")) + + g.add_watch_point( + {"fencei": bundle.io._fencei}, + bins={"fencei_high": lambda d: d["fencei"].value == 1}, + name="basic_fencei_control", + ) + g.mark_function( + "basic_fencei_control", + _mark_tests(["test_smoke", "test_fencei_work"]), + bin_name=["fencei_high"], + ) + + g.add_watch_point( + {"flush": bundle.io._flush}, + bins={"flush_high": lambda d: d["flush"].value == 1}, + name="basic_flush_control", + ) + g.mark_function( + "basic_flush_control", + _mark_tests(["test_set_flush"]), + bin_name=["flush_high"], + ) + + g.add_watch_point( + {"victim_way": bundle.io._victim._way}, + bins={ + "victim_way_0": lambda d: d["victim_way"].value == 0, + "victim_way_1": lambda d: d["victim_way"].value == 1, + "victim_way_2": lambda d: d["victim_way"].value == 2, + "victim_way_3": lambda d: d["victim_way"].value == 3, + }, + name="basic_victim_way_program", + ) + g.mark_function( + "basic_victim_way_program", + _mark_tests(["test_set_victim_way"]), + bin_name=["victim_way_0", "victim_way_1", "victim_way_2", "victim_way_3"], + ) + + g.add_watch_point( + { + "fetch_ready": bundle.io._fetch._req._ready, + "fetch_valid": bundle.io._fetch._req._valid, + }, + bins={ + "fetch_handshake": lambda d: d["fetch_ready"].value == 1 and d["fetch_valid"].value == 1, + }, + name="basic_fetch_handshake", + ) + g.mark_function( + "basic_fetch_handshake", + _mark_tests( + [ + "test_bundle_drive_fetch_req_inputs", + "test_send_fetch_request", + "test_api_fetch_request_generates_acquire", + "test_api_full_fetch_flow", + ] + ), + bin_name=["fetch_handshake"], + ) + + g.add_watch_point( + { + "prefetch_ready": bundle.io._prefetch_req._ready, + "prefetch_valid": bundle.io._prefetch_req._valid, + }, + bins={ + "prefetch_handshake": lambda d: d["prefetch_ready"].value == 1 and d["prefetch_valid"].value == 1, + }, + name="basic_prefetch_handshake", + ) + g.mark_function( + "basic_prefetch_handshake", + _mark_tests( + [ + "test_send_prefetch_request", + "test_api_full_prefetch_flow", + ] + ), + bin_name=["prefetch_handshake"], + ) + + g.add_watch_point( + { + "grant_valid": bundle.io._mem._grant._valid, + "grant_corrupt": bundle.io._mem._grant._bits._corrupt, + }, + bins={ + "grant_corrupt_seen": lambda d: d["grant_valid"].value == 1 and d["grant_corrupt"].value == 1, + }, + name="basic_grant_corrupt_monitor", + ) + g.mark_function( + "basic_grant_corrupt_monitor", + _mark_tests(["test_api_grant_with_corruption"]), + bin_name=["grant_corrupt_seen"], + ) + + return g + def define_fifo_coverage(bundle,dut): """ @@ -10,16 +124,25 @@ def define_fifo_coverage(bundle,dut): bundle: The top-level ICacheMissUnitBundle object. dut: The DUT object for accessing internal signals. """ - g = CovGroup("MissUnit_FIFO") + g = CovGroup(UT_FCOV("MissUnit_FIFO")) # create FIFO_internalsignals for FIFO functional coverage - FIFO_dict = {"enq_ptr_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_value",\ - "enq_ptr_flag":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_flag",\ - "enq_ptr_new_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_new_value",\ - "deq_ptr_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.deq_ptr_value",\ - "deq_ptr_flag":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.deq_ptr_flag",\ - "deq_ptr_new_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.deq_ptr_new_value",\ - "full":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.full" - } + FIFO_dict = { + # FIFO enq/deq internal signals + "enq_ptr_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_value",\ + "enq_ptr_flag":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_flag",\ + "enq_ptr_new_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_new_value",\ + "enq_ready":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.__Vtogcov__io_enq_ready",\ + "enq_valid":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.__Vtogcov__io_enq_valid",\ + "deq_ptr_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.deq_ptr_value",\ + "deq_ptr_flag":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.deq_ptr_flag",\ + "deq_ptr_new_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.deq_ptr_new_value",\ + "deq_ready":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.__Vtogcov__io_deq_ready",\ + "deq_valid":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.__Vtogcov__io_deq_valid",\ + "dep_bits":"ICacheMissUnit_top.ICacheMissUnit._priorityFIFO_io_deq_bits",\ + "full":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.full",\ + + "enq_bits":"ICacheMissUnit_top.ICacheMissUnit._prefetchDemux_io_chosen",\ + } # ================================================================= # CP 28.1 & 28.2 & 28.3: 正常入队 vs 入队翻转 vs 队满阻塞 # 监控目标:prefetch请求接口和其内部状态 @@ -27,12 +150,12 @@ def define_fifo_coverage(bundle,dut): g.add_watch_point( # 使用字典作为target,让lambda函数更易读 { - "enq_ready": bundle.priorityFIFO._io_enq._ready, - "enq_valid": bundle.priorityFIFO._io_enq._valid_T_probe, + "enq_ready": dut.GetInternalSignal(FIFO_dict["enq_ready"], use_vpi=False), + "enq_valid": dut.GetInternalSignal(FIFO_dict["enq_valid"], use_vpi=False), "enq_ptr_value": dut.GetInternalSignal(FIFO_dict["enq_ptr_value"], use_vpi=False), "enq_ptr_new_value": dut.GetInternalSignal(FIFO_dict["enq_ptr_new_value"], use_vpi=False), "enq_ptr_flag": dut.GetInternalSignal(FIFO_dict["enq_ptr_flag"], use_vpi=False), - "enq_bits": bundle.prefetchDemux._io_chosen, + "enq_bits": dut.GetInternalSignal(FIFO_dict["enq_bits"], use_vpi=False), "deq_ptr_value": dut.GetInternalSignal(FIFO_dict["deq_ptr_value"], use_vpi=False), "deq_ptr_flag": dut.GetInternalSignal(FIFO_dict["deq_ptr_flag"], use_vpi=False), "full": dut.GetInternalSignal(FIFO_dict["full"], use_vpi=False) @@ -60,6 +183,11 @@ def define_fifo_coverage(bundle,dut): }, name="CP_Enqueue_Normal_vs_Full" ) + g.mark_function( + "CP_Enqueue_Normal_vs_Full", + _mark_tests("test_FIFO_moudle_CP28_CP29_enq_and_deq_operation"), + bin_name=["enq_when_not_full", "enq_when_will_full", "enq_blocked_when_full"], + ) # ================================================================= # CP 29.1 & 29.2 & 29.3: 正常出队 vs 出队翻转 vs 队空阻塞 # 监控目标:prefetch请求接口和其内部状态 @@ -67,11 +195,11 @@ def define_fifo_coverage(bundle,dut): g.add_watch_point( # 使用字典作为target,让lambda函数更易读 { - "deq_ready": bundle.priorityFIFO._io_deq._ready_T, - "deq_valid": bundle.priorityFIFO._io_deq._valid, + "deq_ready": dut.GetInternalSignal(FIFO_dict["deq_ready"], use_vpi=False), + "deq_valid": dut.GetInternalSignal(FIFO_dict["deq_valid"], use_vpi=False), "enq_ptr_value": dut.GetInternalSignal(FIFO_dict["enq_ptr_value"], use_vpi=False), "enq_ptr_flag": dut.GetInternalSignal(FIFO_dict["enq_ptr_flag"], use_vpi=False), - "deq_bits": bundle.priorityFIFO._io_deq_bits, + "deq_bits": dut.GetInternalSignal(FIFO_dict["dep_bits"], use_vpi=False), "deq_ptr_value": dut.GetInternalSignal(FIFO_dict["deq_ptr_value"], use_vpi=False), "deq_ptr_new_value":dut.GetInternalSignal(FIFO_dict["deq_ptr_new_value"], use_vpi=False), "deq_ptr_flag": dut.GetInternalSignal(FIFO_dict["deq_ptr_flag"], use_vpi=False), @@ -98,6 +226,11 @@ def define_fifo_coverage(bundle,dut): }, name="CP_Dequeue_Normal_vs_null" ) + g.mark_function( + "CP_Dequeue_Normal_vs_null", + _mark_tests("test_FIFO_moudle_CP28_CP29_enq_and_deq_operation"), + bin_name=["deq_when_not_null", "deq_when_will_wrap", "deq_blocked_when_null"], + ) # ================================================================= # CP 30: flush @@ -123,6 +256,11 @@ def define_fifo_coverage(bundle,dut): }, name="CP_flush" ) + g.mark_function( + "CP_flush", + _mark_tests("test_FIFO_moudle_CP30_flush_operation"), + bin_name=["after_flush"], + ) return g @@ -130,7 +268,16 @@ def define_missunit_coverage_groups(bundle, dut): """ define functional coverage groups of ICacheMissUnit. """ - g = CovGroup("MissUnit_Main_Coverage") + main_cov = CovGroup(UT_FCOV("MissUnit_Main_Coverage")) + timing_cov = CovGroup(UT_FCOV("MissUnit_Timing_Coverage")) + MISSUNIT_dict ={ + "fetch_demux_valid":"ICacheMissUnit_top.ICacheMissUnit.fetchDemux.__Vtogcov__io_in_valid",\ + "fetch_hit":"ICacheMissUnit_top.ICacheMissUnit.fetchHit",\ + "prefetch_demux_valid":"ICacheMissUnit_top.ICacheMissUnit.prefetchDemux.__Vtogcov__io_in_valid",\ + "prefetch_hit":"ICacheMissUnit_top.ICacheMissUnit.prefetchHit",\ + "last_fire":"ICacheMissUnit_top.ICacheMissUnit.last_fire",\ + "last_fire_r":"ICacheMissUnit_top.ICacheMissUnit.last_fire_r",\ + } # ================================================================= # CP 31.1: 接受新的fetch 31.2:处理已经存在的 fetch @@ -141,20 +288,29 @@ def low_index_priority(dic) -> bool: if dic["MSHR_1_acquire_valid"].value == 1: if dic["MSHR_0_acquire_valid"].value == 1: return True + else: + return False elif dic["MSHR_2_acquire_valid"].value == 1: if dic["MSHR_0_acquire_valid"].value == 1 and dic["MSHR_1_acquire_valid"].value == 1: return True + else: + return False elif dic["MSHR_3_acquire_valid"].value == 1: if dic["MSHR_0_acquire_valid"].value == 1 and dic["MSHR_1_acquire_valid"].value == 1 and dic["MSHR_2_acquire_valid"].value == 1: return True + else: + return False else: - return False - g.add_watch_point( + if dic["MSHR_0_acquire_valid"].value == 0 and dic["MSHR_1_acquire_valid"].value == 0 and dic["MSHR_2_acquire_valid"].value == 0 and dic["MSHR_3_acquire_valid"].value == 0: + return True + else: + return False + main_cov.add_watch_point( { "fetch_req_ready": bundle.io._fetch._req._ready, "fetch_req_valid": bundle.io._fetch._req._valid, - "fetch_demux_valid": bundle.fetchDemux._io_in_valid_T_1, - "fetch_hit":bundle.ICacheMissUnit_.fetchHit, + "fetch_demux_valid": dut.GetInternalSignal(MISSUNIT_dict["fetch_demux_valid"], use_vpi=False), + "fetch_hit":dut.GetInternalSignal(MISSUNIT_dict["fetch_hit"], use_vpi=False), "MSHR_0_acquire_valid":bundle.ICacheMissUnit_._fetchMSHRs._0._io._acquire_valid, "MSHR_1_acquire_valid":bundle.ICacheMissUnit_._fetchMSHRs._1._io._acquire_valid, "MSHR_2_acquire_valid":bundle.ICacheMissUnit_._fetchMSHRs._2._io._acquire_valid, @@ -175,6 +331,11 @@ def low_index_priority(dic) -> bool: }, name="fetch_req_new_vs_hit" ) + main_cov.mark_function( + "fetch_req_new_vs_hit", + _mark_tests("test_MISSUNIT_CP31_fetch_miss_process"), + bin_name=["CP31.1", "CP31.2", "CP31.3"], + ) # ================================================================= # CP 32.1: 接受新的prefetch 32.2:处理已经存在的 prefetch @@ -182,12 +343,12 @@ def low_index_priority(dic) -> bool: # 监控目标:prefetch request接口和其内部状态,MSHR接口和其内部状态 # ================================================================= - g.add_watch_point( + main_cov.add_watch_point( { "prefetch_req_ready": bundle.io._prefetch_req._ready, "prefetch_req_valid": bundle.io._prefetch_req._valid, - "prefetch_demux_valid": bundle.prefetchDemux._io_in_valid_T_1, - "prefetch_hit":bundle.ICacheMissUnit_.prefetchHit, + "prefetch_demux_valid": dut.GetInternalSignal(MISSUNIT_dict["prefetch_demux_valid"], use_vpi=False), + "prefetch_hit":dut.GetInternalSignal(MISSUNIT_dict["prefetch_hit"], use_vpi=False), }, bins={ # 功能点 32.1: 接受新的预取请求 @@ -203,12 +364,17 @@ def low_index_priority(dic) -> bool: # 这里不需要覆盖点 name="prefetch_req_new_vs_hit" ) + main_cov.mark_function( + "prefetch_req_new_vs_hit", + _mark_tests("test_MISSUNIT_CP32_prefetch_miss_process"), + bin_name=["CP32.1", "CP32.2"], + ) # ================================================================= # CP 33: MSHR查找命中逻辑 # 监控目标:MSHR查找接口和命中状态 # ================================================================= - g.add_watch_point( + main_cov.add_watch_point( { "fetch_req_valid": bundle.io._fetch._req._valid, "fetch_req_blkPaddr": bundle.io._fetch._req._bits._blkPaddr, @@ -216,8 +382,8 @@ def low_index_priority(dic) -> bool: "prefetch_req_valid": bundle.io._prefetch_req._valid, "prefetch_req_blkPaddr": bundle.io._prefetch_req._bits._blkPaddr, "prefetch_req_vSetIdx": bundle.io._prefetch_req._bits._vSetIdx, - "fetch_hit": bundle.ICacheMissUnit_.fetchHit, - "prefetch_hit": bundle.ICacheMissUnit_.prefetchHit, + "fetch_hit": dut.GetInternalSignal(MISSUNIT_dict["fetch_hit"], use_vpi=False), + "prefetch_hit":dut.GetInternalSignal(MISSUNIT_dict["prefetch_hit"], use_vpi=False), }, bins={ # 33.1: Fetch请求命中现有MSHR @@ -239,12 +405,27 @@ def low_index_priority(dic) -> bool: }, name="MSHR_lookup_hit_logic" ) + main_cov.mark_function( + "MSHR_lookup_hit_logic", + _mark_tests( + [ + "test_MISSUNIT_CP33_MSHR_manage", + "test_MISSUNIT_addational_all_mshr_lookup_coverage", + ] + ), + bin_name=[ + "CP33.1_fetch_hit_existing", + "CP33.2_prefetch_hit_existing", + "CP33.3_prefetch_hit_fetch_same", + "CP33.4_no_hit", + ], + ) # ================================================================= # CP 34: acquireArb仲裁逻辑 # 监控目标:仲裁器的选择逻辑和优先级 # ================================================================= - g.add_watch_point( + main_cov.add_watch_point( { "acquire_valid": bundle.io._mem._acquire._valid, "acquire_source": bundle.io._mem._acquire._bits._source, @@ -273,19 +454,24 @@ def low_index_priority(dic) -> bool: }, name="acquire_arbitration_logic" ) + main_cov.mark_function( + "acquire_arbitration_logic", + _mark_tests("test_MISSUNIT_CP34_acquireArb_arbitration"), + bin_name=["CP34.1_fetch_priority", "CP34.2_prefetch_selected"], + ) # ================================================================= # CP 35: Grant数据接收与处理 # 监控目标:Grant数据收集和状态更新 # ================================================================= - g.add_watch_point( + main_cov.add_watch_point( { "grant_valid": bundle.io._mem._grant._valid, "grant_opcode": bundle.io._mem._grant._bits._opcode, "grant_source": bundle.io._mem._grant._bits._source, "grant_corrupt": bundle.io._mem._grant._bits._corrupt, - "last_fire": bundle.ICacheMissUnit_.last_fire, - "last_fire_r": bundle.ICacheMissUnit_.last_fire_r, + "last_fire": dut.GetInternalSignal(MISSUNIT_dict["last_fire"], use_vpi=False), + "last_fire_r": dut.GetInternalSignal(MISSUNIT_dict["last_fire_r"], use_vpi=False), }, bins={ # 35.1: 第一个beat数据接收 @@ -308,12 +494,22 @@ def low_index_priority(dic) -> bool: }, name="grant_data_collection" ) + main_cov.mark_function( + "grant_data_collection", + _mark_tests("test_MISSUNIT_CP35_grant_accept_and_refill"), + bin_name=[ + "CP35.1_first_beat", + "CP35.2_last_beat", + "CP35.3_grant_corrupt", + "CP35.4_grant_completion", + ], + ) # ================================================================= # CP 36: 替换策略更新 # 监控目标:victim更新信号 # ================================================================= - g.add_watch_point( + main_cov.add_watch_point( { "victim_valid": bundle.io._victim._vSetIdx._valid, "victim_bits": bundle.io._victim._vSetIdx._bits, @@ -328,12 +524,17 @@ def low_index_priority(dic) -> bool: }, name="victim_replacement_update" ) + main_cov.mark_function( + "victim_replacement_update", + _mark_tests("test_MISSUNIT_CP36_Replacer"), + bin_name=["CP36.1_victim_update"], + ) # ================================================================= # CP 37: SRAM写回操作 # 监控目标:Meta/Data写操作信号 # ================================================================= - g.add_watch_point( + timing_cov.add_watch_point( { "meta_write_valid": bundle.io._meta_write._valid, "data_write_valid": bundle.io._data_write._valid, @@ -341,7 +542,7 @@ def low_index_priority(dic) -> bool: "fetch_resp_corrupt": bundle.io._fetch._resp._bits._corrupt, "flush": bundle.io._flush, "fencei": bundle.io._fencei, - "last_fire_r": bundle.ICacheMissUnit_.last_fire_r, + "last_fire_r": dut.GetInternalSignal(MISSUNIT_dict["last_fire_r"], use_vpi=False), }, bins={ # 37.1: 正常写SRAM(无flush/fencei/corrupt) @@ -369,15 +570,25 @@ def low_index_priority(dic) -> bool: }, name="sram_write_operations" ) + timing_cov.mark_function( + "sram_write_operations", + _mark_tests("test_MISSUNIT_CP37_SRAM_writeback"), + bin_name=[ + "CP37.1_normal_sram_write", + "CP37.2_no_write_with_flush", + "CP37.3_fetch_resp_always", + "CP37.4_corrupt_response", + ], + ) # ================================================================= # CP 38: Miss 完成响应 # 监控目标:向 mainPipe/prefetchPipe 发出 Miss 完成响应 # ================================================================= - g.add_watch_point( + timing_cov.add_watch_point( { "fetch_resp_valid": bundle.io._fetch._resp._valid, - "last_fire_r": bundle.ICacheMissUnit_.last_fire_r, + "last_fire_r": dut.GetInternalSignal(MISSUNIT_dict["last_fire_r"], use_vpi=False), "mshr_resp_blkPaddr": dut.GetInternalSignal("ICacheMissUnit_top.ICacheMissUnit.mshr_resp_blkPaddr", use_vpi=False), "mshr_resp_vSetIdx": dut.GetInternalSignal("ICacheMissUnit_top.ICacheMissUnit.mshr_resp_vSetIdx", use_vpi=False), "fetch_resp_blkPaddr": bundle.io._fetch._resp._bits._blkPaddr, @@ -397,12 +608,17 @@ def low_index_priority(dic) -> bool: }, name="miss_completion_response" ) + timing_cov.mark_function( + "miss_completion_response", + _mark_tests("test_MISSUNIT_CP38_mainpipe_iprefetchpipe_response"), + bin_name=["CP38.1_normal_miss_completion"], + ) # ================================================================= # CP 39: 处理 flush/fencei # 监控目标:flush/fencei对MSHR状态和写回操作的影响 # ================================================================= - g.add_watch_point( + main_cov.add_watch_point( { "fencei": bundle.io._fencei, "flush": bundle.io._flush, @@ -430,16 +646,21 @@ def low_index_priority(dic) -> bool: }, name="flush_fencei_mshr_handling" ) + main_cov.mark_function( + "flush_fencei_mshr_handling", + _mark_tests("test_MISSUNIT_CP39_flush_fencei_operation"), + bin_name=["CP39.1_fencei_before_fire", "CP39.2_flush_before_fire"], + ) # ================================================================= # CP 39.3: MSHR 已发射后 flush/fencei 的处理 # 监控目标:发射后的写回抑制 # ================================================================= - g.add_watch_point( + timing_cov.add_watch_point( { "flush": bundle.io._flush, "fencei": bundle.io._fencei, - "last_fire_r": bundle.ICacheMissUnit_.last_fire_r, + "last_fire_r": dut.GetInternalSignal(MISSUNIT_dict["last_fire_r"], use_vpi=False), "meta_write_valid": bundle.io._meta_write._valid, "data_write_valid": bundle.io._data_write._valid, "fetch_resp_valid": bundle.io._fetch._resp._valid, @@ -456,14 +677,22 @@ def low_index_priority(dic) -> bool: }, name="flush_fencei_after_fire" ) + timing_cov.mark_function( + "flush_fencei_after_fire", + _mark_tests("test_MISSUNIT_CP39_flush_fencei_operation"), + bin_name=["CP39.3_flush_fencei_after_fire"], + ) - return g + return main_cov, timing_cov def create_all_coverage_groups(bundle, dut): """ 创建所有覆盖点组合,包括FIFO和主要功能覆盖点 """ + basic_coverage = define_basic_coverage(bundle, dut) fifo_coverage = define_fifo_coverage(bundle, dut) - main_coverage = define_missunit_coverage_groups(bundle, dut) - - return [fifo_coverage, main_coverage] \ No newline at end of file + main_coverage, timing_coverage = define_missunit_coverage_groups(bundle, dut) + return { + "regular": [basic_coverage, fifo_coverage, main_coverage], + "timing": [timing_coverage], + } diff --git a/ut_frontend/icache/missunit/test/missunit_fixture.py b/ut_frontend/icache/missunit/test/missunit_fixture.py index 6d81735f..5dd25c81 100644 --- a/ut_frontend/icache/missunit/test/missunit_fixture.py +++ b/ut_frontend/icache/missunit/test/missunit_fixture.py @@ -1,7 +1,7 @@ import asyncio import toffee import toffee_test -from toffee import start_clock +from toffee import start_clock, create_task from dut.ICacheMissUnit import DUTICacheMissUnit from ..env import ICacheMissUnitEnv from ..env.missunit_coverage import create_all_coverage_groups @@ -15,21 +15,77 @@ async def icachemissunit_env(toffee_request: toffee_test.ToffeeRequest): icachemissunit_env.dut.Step(10) icachemissunit_env.dut.reset.value = 0 icachemissunit_env.dut.Step(10) + # all_signal_list = icachemissunit_env.dut.GetInternalSignalList(use_vpi=False) + # for i in all_signal_list: + # print(f"Signal: {i}") # toffee.info(f"all signals: {icachemissunit_env.dut.GetInternalSignalList(use_vpi=False)}") dut.InitClock("clock") toffee.info("--- [FIXTURE SETUP] Defining all functional coverage groups... ---") - coverage_groups = create_all_coverage_groups(icachemissunit_env.bundle, dut) - - # Add all coverage groups to the test request - for coverage_group in coverage_groups: + coverage_info = create_all_coverage_groups(icachemissunit_env.bundle, dut) + regular_cov_groups = coverage_info["regular"] + timing_cov_groups = coverage_info["timing"] + # Add regular coverage groups to the test request + for coverage_group in regular_cov_groups: toffee_request.add_cov_groups(coverage_group) toffee.info(f"Added coverage group: {coverage_group.name}") + timing_task = None + if timing_cov_groups: + last_fire_r_sig = dut.GetInternalSignal( + "ICacheMissUnit_top.ICacheMissUnit.last_fire_r", use_vpi=False + ) + + async def timing_monitor(): + prev_last_fire = 0 + try: + while True: + grant_valid = icachemissunit_env.bundle.io._mem._grant._valid.value + meta_valid = icachemissunit_env.bundle.io._meta_write._valid.value + data_valid = icachemissunit_env.bundle.io._data_write._valid.value + fetch_resp_valid = icachemissunit_env.bundle.io._fetch._resp._valid.value + flush_val = icachemissunit_env.bundle.io._flush.value + fencei_val = icachemissunit_env.bundle.io._fencei.value + curr_last_fire = last_fire_r_sig.value + should_sample = any( + [ + grant_valid == 1, + meta_valid == 1, + data_valid == 1, + fetch_resp_valid == 1, + flush_val == 1, + fencei_val == 1, + curr_last_fire == 1, + prev_last_fire == 1, + ] + ) + if should_sample: + for cov_group in timing_cov_groups: + cov_group.sample() + prev_last_fire = curr_last_fire + await icachemissunit_env.bundle.step() + except asyncio.CancelledError: + pass + except Exception as exc: + toffee.info(f"[TimingCov] monitor exception: {exc}") + + timing_task = create_task(timing_monitor()) + timing_task.set_name("missunit_timing_cov") + yield icachemissunit_env + if timing_task: + timing_task.cancel() + try: + await timing_task + except asyncio.CancelledError: + pass + + # Include timing coverage groups in the final report + toffee_request.cov_groups.extend(timing_cov_groups) + # Sample all coverage groups - for coverage_group in coverage_groups: + for coverage_group in regular_cov_groups + timing_cov_groups: dut.StepRis(coverage_group.sample) cur_loop = asyncio.get_event_loop() diff --git a/ut_frontend/icache/missunit/test/missunit_test.py b/ut_frontend/icache/missunit/test/missunit_test.py index 9018021c..e00f260c 100644 --- a/ut_frontend/icache/missunit/test/missunit_test.py +++ b/ut_frontend/icache/missunit/test/missunit_test.py @@ -3,6 +3,44 @@ import toffee_test import toffee +def get_internal_signal(dut, signal_name:str): + signal_dict = { + "enq_ptr_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_value",\ + "enq_ptr_flag":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_flag",\ + "enq_ptr_new_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_new_value",\ + "enq_ready":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.__Vtogcov__io_enq_ready",\ + "enq_valid":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.__Vtogcov__io_enq_valid",\ + "deq_ptr_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.deq_ptr_value",\ + "deq_ptr_flag":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.deq_ptr_flag",\ + "deq_ptr_new_value":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.deq_ptr_new_value",\ + "deq_ready":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.__Vtogcov__io_deq_ready",\ + "deq_valid":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.__Vtogcov__io_deq_valid",\ + "fifo_deq_bits":"ICacheMissUnit_top.ICacheMissUnit._priorityFIFO_io_deq_bits",\ + "full":"ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.full",\ + + "enq_bits":"ICacheMissUnit_top.ICacheMissUnit._prefetchDemux_io_chosen",\ + + "fetch_demux_valid":"ICacheMissUnit_top.ICacheMissUnit.fetchDemux.__Vtogcov__io_in_valid",\ + "fetch_hit":"ICacheMissUnit_top.ICacheMissUnit.fetchHit",\ + "prefetch_demux_valid":"ICacheMissUnit_top.ICacheMissUnit.prefetchDemux.__Vtogcov__io_in_valid",\ + "prefetch_hit":"ICacheMissUnit_top.ICacheMissUnit.prefetchHit",\ + "last_fire":"ICacheMissUnit_top.ICacheMissUnit.last_fire",\ + "last_fire_r":"ICacheMissUnit_top.ICacheMissUnit.last_fire_r",\ + "io_mem_acquire_bits_source":"ICacheMissUnit_top.io_mem_acquire_bits_source",\ + "readBeatCnt":"ICacheMissUnit_top.ICacheMissUnit.readBeatCnt",\ + "respDataReg_0":"ICacheMissUnit_top.ICacheMissUnit.respDataReg_0",\ + "respDataReg_1":"ICacheMissUnit_top.ICacheMissUnit.respDataReg_1",\ + "id_r":"ICacheMissUnit_top.ICacheMissUnit.id_r",\ + "corrupt_r":"ICacheMissUnit_top.ICacheMissUnit.corrupt_r",\ + "victim_valid":"ICacheMissUnit_top.io_victim_vSetIdx_valid",\ + "mshr_resp_way":"ICacheMissUnit_top.ICacheMissUnit.mshr_resp_way",\ + "mshr_resp_blkPaddr":"ICacheMissUnit_top.ICacheMissUnit.mshr_resp_blkPaddr",\ + "mshr_resp_vSetIdx":"ICacheMissUnit_top.ICacheMissUnit.mshr_resp_vSetIdx",\ + "refill_done_r_counter":"ICacheMissUnit_top.ICacheMissUnit.refill_done_r_counter",\ + } + return dut.GetInternalSignal(signal_dict[signal_name], use_vpi=False).value + + @toffee_test.testcase async def test_smoke(icachemissunit_env: ICacheMissUnitEnv): @@ -42,15 +80,6 @@ async def test_bundle_drive_fetch_req_inputs(icachemissunit_env: ICacheMissUnitE toffee.info(f"Python side: dut_bundle.io._fencei.value = {dut_bundle.io._fencei.value}") toffee.info("Bundle drive tests completed.") -@toffee_test.testcase -async def test_bundle_read_fetch_req_ready(icachemissunit_env: ICacheMissUnitEnv): - dut_bundle = icachemissunit_env.bundle - toffee.info("\n--- Testing Bundle: Reading fetch_req_ready ---") - ready_val = dut_bundle.io._fetch._req._ready.value - toffee.info(f"Read dut_bundle.io._fetch._req._ready.value = {ready_val} (depends on DUT state)") - assert ready_val in [0, 1], "fetch_req_ready should be 0 or 1" - toffee.info("Bundle read test for fetch_req_ready (Python side) completed.") - @toffee_test.testcase async def test_fencei_work(icachemissunit_env: ICacheMissUnitEnv): """ @@ -390,829 +419,858 @@ async def test_api_full_prefetch_flow(icachemissunit_env: ICacheMissUnitEnv): toffee.info("API Full End-to-End PREFETCH Flow test passed.") @toffee_test.testcase -async def test_FIFO_moudle(icachemissunit_env: ICacheMissUnitEnv): +async def test_FIFO_moudle_CP28_CP29_enq_and_deq_operation(icachemissunit_env: ICacheMissUnitEnv): """ - Goal: Correctly test FIFO by considering the high priority of fetch requests. + Covers FIFO CP28.x (enqueue behaviors) and CP29.x (dequeue behaviors) by + directly driving prefetch requests and observing FIFO internals. """ - toffee.info("\n--- Testing FIFO (Considering Fetch Priority) ---") dut = icachemissunit_env.dut agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - acquire_info_list = [] + toffee.info("\n--- [FIFO Test] CP28/CP29 enqueue & dequeue operations ---") - # 2. 填满 FIFO - toffee.info("Step 2: Enqueue 10 prefetch requests to fill the FIFO.") - for i in range(10): - await agent.drive_send_prefetch_req(0x1000 + i * 0x1000, 0x1A + i) - acquire_info = await agent.drive_get_acquire_request(timeout_cycles=10) - assert acquire_info is not None, f"Failed to get acquire for request{i}" - await agent.drive_acknowledge_acquire(cycles=1) - acquire_info_list.append(acquire_info) - toffee.info(f"the prefetch chosen is {bundle.prefetchDemux._io_chosen.value} now") + def fifo_state(): + names = [ + "enq_ptr_value", "enq_ptr_flag", + "deq_ptr_value", "deq_ptr_flag", + "full", "enq_ready", "deq_ready", "deq_valid" + ] + return {name: int(get_internal_signal(dut, name)) for name in names} - await agent.bundle.step(5) - # CP28.2 队满,入队翻转 - assert dut.GetInternalSignal("ICacheMissUnit_top.ICacheMissUnit.priorityFIFO.enq_ptr_flag", use_vpi=False).value == 1 + async def step_and_sample(cycles: int = 1): + await bundle.step(cycles) + return fifo_state() + async def send_prefetch(slot_idx: int): + blk = 0x8000 + slot_idx * 0x1000 + vset = (0x10 + slot_idx) & 0xFF + info = await agent.drive_send_prefetch_req(blkPaddr=blk, vSetIdx=vset) + assert info["send_success"], f"Prefetch {slot_idx} should enter FIFO" + return info - # 3. 测试队满阻塞 - toffee.info("Step 3: Test blocking when FIFO is full.") - send_result = await agent.drive_send_prefetch_req(blkPaddr=0xDEAD, vSetIdx=0, timeout_cycles=3) - assert send_result["send_success"] is False and bundle.io._prefetch_req._ready.value == 0, "FIFO should be full and starting block the request." + # Ensure control signals are deasserted and FIFO starts empty + await agent.fencei_func(0) + await agent.drive_set_flush(False) + bundle.io._mem._acquire._ready.value = 0 + await bundle.step() + state = fifo_state() + assert state["enq_ptr_value"] == 0 and state["deq_ptr_value"] == 0 + assert state["enq_ptr_flag"] == 0 and state["deq_ptr_flag"] == 0 + assert state["full"] == 0 and state["enq_ready"] == 1 - # 4. 出队一个元素 - toffee.info(f"acquire_info_list is {acquire_info_list}") - toffee.info("Step 4: Dequeue one item from the FIFO.") - await agent.drive_respond_with_grant(source_id=acquire_info_list[0]["source"], data_beats=[0x1, 0x2]) - response_info = await agent.drive_get_fetch_response(timeout_cycles=10) - assert response_info is not None, "Failed to get fetch response after dequeueing an item." - toffee.info(f"Dequeued item and received fetch response: {response_info}") - del acquire_info_list[0] + # --------------------------- + # CP28.1/CP28.2/CP28.3 checks + # --------------------------- + toffee.info("CP28.1: Enqueuing 9 entries without flag flip.") + for i in range(9): + await send_prefetch(i) + state = await step_and_sample() + assert state["enq_ptr_value"] == i + 1, f"Enqueue pointer should advance to {i+1}" + assert state["enq_ptr_flag"] == 0, "Flag must not flip before wrap" + assert state["full"] == 0, "FIFO must not report full before wrap" + assert state["enq_ready"] == 1, "Internal enqueue ready remains high while not full" - # 5. 测试指针回环入队 - toffee.info("Step 5: Test enqueue after making space in the FIFO.") - send_result = await agent.drive_send_prefetch_req(blkPaddr=0xCAFE, vSetIdx=10) - acquire_info = await agent.drive_get_acquire_request(timeout_cycles=10) - assert acquire_info is not None, f"Failed to get acquire for request{i}" - await agent.drive_acknowledge_acquire(cycles=1) - acquire_info_list.append(acquire_info) - assert send_result["send_success"] is True, "Failed to enqueue into the freed FIFO slot." + toffee.info("CP28.2: Enqueue hitting tail causes wrap & flag flip.") + await send_prefetch(9) + state = await step_and_sample() + assert state["enq_ptr_value"] == 0, "Pointer should wrap to 0 after slot 9" + assert state["enq_ptr_flag"] == 1, "Flag should flip when pointer wraps" + assert state["full"] == 1, "FIFO should report full after 10th enqueue" + assert state["enq_ptr_value"] == state["deq_ptr_value"], "Full condition requires equal pointers" + assert state["enq_ptr_flag"] ^ state["deq_ptr_flag"] == 1, "Full condition requires flag mismatch" + assert state["enq_ready"] == 0, "Internal enqueue ready must drop when FIFO full" - # 6.顺序出队所有元素,并驱动drive_acknowledge_acquire以触发空队出列 - await agent.bundle.step(5) - for i in acquire_info_list: - toffee.info(f"this acquire_info is {i}") - await agent.drive_respond_with_grant(source_id=i["source"],data_beats=[0x1,0x2]) - response_info = await agent.drive_get_fetch_response(timeout_cycles=10) - toffee.info(f"#{i} response_info is {response_info}") - assert response_info is not None, "Failed to get prefetch response after dequeueing an item" + toffee.info("CP28.3: Further enqueue blocked while FIFO is full.") + reject_info = await agent.drive_send_prefetch_req( + blkPaddr=0xDEAD_BEEF, vSetIdx=0x55, timeout_cycles=5 + ) + assert reject_info["send_success"] is False, "Request must be rejected when FIFO is full" + state = fifo_state() + assert state["full"] == 1 and state["enq_ready"] == 0 + # --------------------------- + # CP29.1/CP29.2/CP29.3 checks + # --------------------------- + toffee.info("CP29.x: Start draining FIFO via memory acquires.") - await agent.drive_respond_with_grant(source_id=acquire_info_list[0]["source"], data_beats=[0x1, 0x2]) - response_info = await agent.drive_get_fetch_response(timeout_cycles=10) - assert response_info is None, "Expected no fetch response after flushing the FIFO." - await agent.bundle.step(5) + async def acknowledge_next_acquire(): + acquire = await agent.drive_get_and_acknowledge_acquire(timeout_cycles=80, ack_cycles=1) + assert acquire is not None, "Expected acquire request while FIFO is non-empty" + return await step_and_sample() - - # 8. 测试Flush操作 - toffee.info("Step 8: Test flush operation.") - # Flush 应该能清空 prefetch MSHR 和 FIFO,以及 fetch MSHR - await agent.drive_set_flush(True) - await agent.bundle.step(1) - await agent.drive_set_flush(False) - await agent.bundle.step(5) - - # 验证:flush后,所有资源都被释放,可以发送一个新的 prefetch 请求 - toffee.info(" - Verifying a new prefetch can be sent after flush.") - send_result = await agent.drive_send_prefetch_req(blkPaddr=0xB000, vSetIdx=1) - assert send_result["send_success"] is True, "Failed to send a request after a flush operation." - - toffee.info("--- FIFO Test (Considering Fetch Priority) Completed ---") + # Allow pending MSHR scheduling to run before draining + await bundle.step(5) + + toffee.info("CP29.1: Normal dequeue without flag flip for first nine entries.") + for expected_value in range(1, 10): + state = await acknowledge_next_acquire() + assert state["deq_ptr_value"] == expected_value, f"Dequeue pointer should advance to {expected_value}" + assert state["deq_ptr_flag"] == 0, "Flag must remain 0 before wrap" + assert state["deq_valid"] == 1, "FIFO should still report valid data" + + toffee.info("CP29.2: Dequeue at tail wraps pointer and flips flag.") + wrap_state = await acknowledge_next_acquire() + assert wrap_state["deq_ptr_value"] == 0, "Pointer should wrap to 0 on final dequeue" + assert wrap_state["deq_ptr_flag"] == 1, "Flag should flip after wrap on dequeue" + + toffee.info("CP29.3: FIFO empty -> io.deq.valid must be low, enqueues allowed again.") + await bundle.step() + empty_state = fifo_state() + assert empty_state["deq_valid"] == 0, "Dequeue valid should drop when FIFO empty" + assert empty_state["enq_ptr_value"] == empty_state["deq_ptr_value"] + assert empty_state["enq_ptr_flag"] == empty_state["deq_ptr_flag"] + assert empty_state["enq_ready"] == 1, "Internal ready must reassert once FIFO empties" + + toffee.info("FIFO CP28/CP29 behavioral checks completed successfully.") @toffee_test.testcase -async def test_mshr_hit_detection(icachemissunit_env: ICacheMissUnitEnv): +async def test_FIFO_moudle_CP30_flush_operation(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点 CP33: MSHR查找命中逻辑 - - 测试fetch/prefetch请求命中现有MSHR - - 测试prefetch与fetch相同地址时的命中检测 + Covers FIFO CP30: flush clears pointers/flags and reopens enqueue interface. """ - toffee.info("\n--- Testing MSHR Hit Detection Logic ---") + dut = icachemissunit_env.dut agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - test_addr = 0x2000 - test_idx = 0x2B - - # Step 1: 发送一个fetch请求,建立MSHR - toffee.info("Step 1: Send initial fetch request to establish MSHR") - send_result = await agent.drive_send_fetch_request(blkPaddr=test_addr, vSetIdx=test_idx) - assert send_result["send_success"] is True, "Initial fetch request should succeed" - - await bundle.step(2) - - # Step 2: 发送相同地址的fetch请求,应该命中 - toffee.info("Step 2: Send same fetch request, should hit existing MSHR") - bundle.io._fetch._req._valid.value = 1 - bundle.io._fetch._req._bits._blkPaddr.value = test_addr - bundle.io._fetch._req._bits._vSetIdx.value = test_idx - await bundle.step() - - # 验证fetch hit - assert bundle.ICacheMissUnit_.fetchHit.value == 1, "Fetch request should hit existing MSHR" - assert bundle.io._fetch._req._ready.value == 1, "Ready should be high for hit request" - - bundle.io._fetch._req._valid.value = 0 + + def fifo_state(): + names = [ + "enq_ptr_value", "enq_ptr_flag", + "deq_ptr_value", "deq_ptr_flag", + "full", "enq_ready", "deq_valid" + ] + return {name: int(get_internal_signal(dut, name)) for name in names} + + async def send_prefetch(slot_idx: int): + blk = 0xA000 + slot_idx * 0x1000 + vset = (0x20 + slot_idx) & 0xFF + info = await agent.drive_send_prefetch_req(blkPaddr=blk, vSetIdx=vset) + assert info["send_success"], f"Prefetch {slot_idx} should enqueue before flush" + + toffee.info("\n--- [FIFO Test] CP30 flush operation ---") + + # Ensure flush/fencei are low and FIFO empty + await agent.fencei_func(0) + await agent.drive_set_flush(False) await bundle.step() - - # Step 3: 发送相同地址的prefetch请求,应该命中 - toffee.info("Step 3: Send prefetch request with same address, should hit") - bundle.io._prefetch_req._valid.value = 1 - bundle.io._prefetch_req._bits._blkPaddr.value = test_addr - bundle.io._prefetch_req._bits._vSetIdx.value = test_idx + state = fifo_state() + assert state["enq_ptr_value"] == state["deq_ptr_value"] == 0 + assert state["enq_ptr_flag"] == state["deq_ptr_flag"] == 0 + assert state["full"] == 0 and state["enq_ready"] == 1 and state["deq_valid"] == 0 + + # Fill a few entries to put FIFO into non-empty state + for i in range(5): + await send_prefetch(i) + await bundle.step() + filled_state = fifo_state() + assert filled_state["enq_ptr_value"] == 5 + assert filled_state["deq_valid"] == 1 + + # Drive flush high and check pointers/flags reset immediately + await agent.drive_set_flush(True) await bundle.step() - - # 验证prefetch hit - assert bundle.ICacheMissUnit_.prefetchHit.value == 1, "Prefetch request should hit existing MSHR" - assert bundle.io._prefetch_req._ready.value == 1, "Ready should be high for hit request" - - bundle.io._prefetch_req._valid.value = 0 + flushed_state = fifo_state() + assert flushed_state["enq_ptr_value"] == 0 + assert flushed_state["deq_ptr_value"] == 0 + assert flushed_state["enq_ptr_flag"] == 0 + assert flushed_state["deq_ptr_flag"] == 0 + assert flushed_state["full"] == 0 + assert flushed_state["enq_ready"] == 1 + assert flushed_state["deq_valid"] == 0 + + # Clear flush and ensure FIFO remains empty/ready + await agent.drive_set_flush(False) await bundle.step() - - toffee.info("MSHR hit detection test completed successfully.") + post_state = fifo_state() + assert post_state == flushed_state + toffee.info("FIFO CP30 flush behavior verified successfully.") + @toffee_test.testcase -async def test_low_index_priority_fetch(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_CP31_fetch_miss_process(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点 CP31.3: 低索引优先级策略(fetch MSHR) - 验证fetchDemux优先分配低索引的MSHR + Covers MISSUNIT CP31.x: fetch miss acceptance, duplicate filtering, and low-index MSHR allocation. """ - toffee.info("\n--- Testing Low Index Priority for Fetch MSHRs ---") + dut = icachemissunit_env.dut agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - # 验证所有fetch MSHR都ready - for i in range(4): - mshr_ready = getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}")._io._req_ready.value - assert mshr_ready == 1, f"Fetch MSHR {i} should be ready initially" - - # 发送请求,应该分配给MSHR 0 - toffee.info("Sending first fetch request - should go to MSHR 0") - send_result = await agent.drive_send_fetch_request(blkPaddr=0x1000, vSetIdx=0x10) - assert send_result["send_success"] is True, "First request should succeed" - await bundle.step() - - # 验证MSHR 0被占用,其他仍然ready - assert getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_0")._io._req_ready.value == 0, "MSHR 0 should be occupied" - for i in range(1, 4): - mshr_ready = getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}")._io._req_ready.value - assert mshr_ready == 1, f"Fetch MSHR {i} should still be ready" - - # 发送第二个请求,应该分配给MSHR 1 - toffee.info("Sending second fetch request - should go to MSHR 1") - send_result = await agent.drive_send_fetch_request(blkPaddr=0x2000, vSetIdx=0x20) - assert send_result["send_success"] is True, "Second request should succeed" + + def fetch_mshr_ready_vec(): + return [ + int(getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}")._io._req_ready.value) + for i in range(4) + ] + + async def send_fetch_once(blk, vset): + info = await agent.drive_send_fetch_request(blkPaddr=blk, vSetIdx=vset) + assert info["send_success"], f"Fetch ({hex(blk)}, {hex(vset)}) should be accepted" + await bundle.step() + + toffee.info("\n--- [MISSUNIT] CP31 fetch miss process ---") + + await agent.fencei_func(0) + await agent.drive_set_flush(False) await bundle.step() - - # 验证MSHR 1被占用 - assert getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_1")._io._req_ready.value == 0, "MSHR 1 should be occupied" - for i in range(2, 4): - mshr_ready = getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}")._io._req_ready.value - assert mshr_ready == 1, f"Fetch MSHR {i} should still be ready" - - toffee.info("Low index priority test for fetch MSHRs completed successfully.") + + assert fetch_mshr_ready_vec() == [1, 1, 1, 1], "Fetch MSHRs must start idle" + + fetch_reqs = [ + (0x1000, 0x10), + (0x2000, 0x20), + (0x3000, 0x30), + (0x4000, 0x40), + ] + + toffee.info("CP31.1 & CP31.3: new misses take lowest-index available fetch MSHRs.") + for idx, (blk, vset) in enumerate(fetch_reqs): + await send_fetch_once(blk, vset) + ready_vec = fetch_mshr_ready_vec() + expected = [0 if j <= idx else 1 for j in range(4)] + assert ready_vec == expected, ( + f"Low-index allocation violated after request #{idx}: expected {expected}, got {ready_vec}" + ) + + toffee.info("CP31.2: duplicate miss hits existing entry and is filtered.") + dup_blk, dup_vset = fetch_reqs[0] + await send_fetch_once(dup_blk, dup_vset) + fetch_hit = int(get_internal_signal(dut, "fetch_hit")) + demux_valid = int(get_internal_signal(dut, "fetch_demux_valid")) + assert fetch_hit == 1, "Duplicate miss should assert fetch_hit" + assert demux_valid == 0, "Duplicate miss should not re-enter fetch demux" + assert fetch_mshr_ready_vec() == [0, 0, 0, 0], "Duplicate miss must not consume additional MSHR" + + toffee.info("MISSUNIT CP31 checks completed successfully.") + @toffee_test.testcase -async def test_low_index_priority_prefetch(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_CP32_prefetch_miss_process(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点 CP32.3: 低索引优先级策略(prefetch MSHR) - 验证prefetchDemux优先分配低索引的MSHR + Covers MISSUNIT CP32.x: prefetch miss acceptance, duplicate filtering, and FIFO ordering. """ - toffee.info("\n--- Testing Low Index Priority for Prefetch MSHRs ---") + dut = icachemissunit_env.dut agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - # 发送第一个prefetch请求,应该分配给MSHR 0,chosen应该为0 - assert bundle.prefetchDemux._io_chosen.value == 0,f"The first prefetch request should go to MSHR 0" - send_result = await agent.drive_send_prefetch_req(blkPaddr=0x3000, vSetIdx=0x30) - assert send_result["send_success"] is True, "First prefetch request should succeed" - await bundle.step() - - # 验证MSHR 0被占用,其他的未被占用 - assert getattr(bundle.ICacheMissUnit_._prefetchMSHRs, f"_0")._io._req_ready.value == 0, "Prefetch MSHR 0 should be occupied" - for i in range(1, 10): - mshr_ready = getattr(bundle.ICacheMissUnit_._prefetchMSHRs, f"_{i}")._io._req_ready.value - assert mshr_ready == 1, f"preFetch MSHR {i} should still be ready" - - # 检查chosen值应该为1(最低索引) - assert bundle.prefetchDemux._io_chosen.value == 1,f"The second prefetch request should go to MSHR 1" - # 发送第二个prefetch请求,应该分配给MSHR 1 - toffee.info("Sending second prefetch request - should go to MSHR 1") - send_result = await agent.drive_send_prefetch_req(blkPaddr=0x4000, vSetIdx=0x40) - assert send_result["send_success"] is True, "Second prefetch request should succeed" + def prefetch_mshr_ready_vec(): + return [ + int(getattr(bundle.ICacheMissUnit_._prefetchMSHRs, f"_{i}")._io._req_ready.value) + for i in range(10) + ] + + async def send_prefetch_once(blk, vset): + info = await agent.drive_send_prefetch_req(blkPaddr=blk, vSetIdx=vset) + assert info["send_success"], f"Prefetch ({hex(blk)}, {hex(vset)}) should be accepted" + await bundle.step() + + toffee.info("\n--- [MISSUNIT] CP32 prefetch miss process ---") + + await agent.fencei_func(0) + await agent.drive_set_flush(False) await bundle.step() - - # 验证MSHR 1被占用 - assert getattr(bundle.ICacheMissUnit_._prefetchMSHRs, f"_1")._io._req_ready.value == 0, "Prefetch MSHR 1 should be occupied" - for i in range(2, 10): - mshr_ready = getattr(bundle.ICacheMissUnit_._prefetchMSHRs, f"_{i}")._io._req_ready.value - assert mshr_ready == 1, f"preFetch MSHR {i} should still be ready" - - toffee.info("Low index priority test for prefetch MSHRs completed successfully.") -@toffee_test.testcase -async def test_fifo_priority_ordering(icachemissunit_env: ICacheMissUnitEnv): - """ - 测试点: 验证prefetch MSHR的FIFO优先级顺序 - 确保先入队的prefetch请求先被处理 - """ - toffee.info("\n--- Testing FIFO Priority Ordering for Prefetch ---") - agent = icachemissunit_env.agent - bundle = icachemissunit_env.bundle - dut = icachemissunit_env.dut - # 发送多个prefetch请求填充FIFO - toffee.info("Filling FIFO with prefetch requests") - acquire_list = [] - for i in range(5): # 发送5个请求 - blkPaddr = 0x5000 + i * 0x1000 - vSetIdx = 0x50 + i - - send_result = await agent.drive_send_prefetch_req(blkPaddr=blkPaddr, vSetIdx=vSetIdx) - assert send_result["send_success"] is True, f"Prefetch request {i} should succeed" - - # 等待acquire产生 - acquire_info = await agent.drive_get_acquire_request(timeout_cycles=5) - assert acquire_info is not None, f"Should get acquire for request {i}" - acquire_list.append(acquire_info) - - # 确认acquire - await agent.drive_acknowledge_acquire(cycles=1) - - toffee.info(f"Request {i}: source_id={acquire_info['source']}") - - # 验证source ID的顺序应该是递增的(4, 5, 6, 7, 8),符合FIFO顺序 - toffee.info("Verifying FIFO ordering of source IDs") - for i in range(len(acquire_list)): - expected_source = 4 + i # prefetch MSHR从4开始 - actual_source = acquire_list[i]["source"] - assert actual_source == expected_source, f"Request {i} source should be {expected_source}, got {actual_source}" - - toffee.info("FIFO priority ordering test completed successfully.") + assert prefetch_mshr_ready_vec() == [1] * 10, "Prefetch MSHRs must start idle" -@toffee_test.testcase -async def test_acquire_arbitration_priority(icachemissunit_env: ICacheMissUnitEnv): - """ - 测试点 CP34: acquireArb仲裁逻辑 - 验证fetch请求优先于prefetch请求的场景 - """ - toffee.info("\n--- Testing Acquire Arbitration Priority ---") - agent = icachemissunit_env.agent - bundle = icachemissunit_env.bundle - - # 测试策略:使用不同的地址确保两个请求都会产生acquire - toffee.info("Step 1: Send prefetch request to unique address") - - # 发送prefetch请求到一个独特的地址 - prefetch_result = await agent.drive_send_prefetch_req(blkPaddr=0x8000, vSetIdx=0x80) - assert prefetch_result["send_success"] is True, "Prefetch request should succeed" - - # 等待prefetch进入MSHR并准备好acquire - await bundle.step(2) - - # 发送fetch请求到另一个独特的地址 - toffee.info("Step 2: Send fetch request to different address") - fetch_result = await agent.drive_send_fetch_request(blkPaddr=0x9000, vSetIdx=0x90) - assert fetch_result["send_success"] is True, "Fetch request should succeed" - - # 等待两个acquire请求都准备好 - await bundle.step(3) - - # 收集所有的acquire请求 - acquire_requests = [] - toffee.info("Step 3: Collecting acquire requests...") - - for attempt in range(3): # 尝试获取多个acquire请求 - acquire_info = await agent.drive_get_and_acknowledge_acquire(timeout_cycles=5) - if acquire_info is not None: - acquire_requests.append(acquire_info) - toffee.info(f" Acquire {len(acquire_requests)}: source={acquire_info['source']}, address=0x{acquire_info['address']:x}") - else: - toffee.info(f" No more acquire requests found (attempt {attempt + 1})") - break - - toffee.info(f"Total acquire requests collected: {len(acquire_requests)}") - - # 检查仲裁结果 - fetch_sources = [req["source"] for req in acquire_requests if req["source"] < 4] - prefetch_sources = [req["source"] for req in acquire_requests if req["source"] >= 4] - - toffee.info(f"Fetch sources: {fetch_sources}") - toffee.info(f"Prefetch sources: {prefetch_sources}") - - # 验证至少有一个请求被处理 - assert len(acquire_requests) >= 1, "Should get at least one acquire request" - - # 如果只有fetch请求,说明prefetch可能被过滤了或者命中了缓存 - if len(prefetch_sources) == 0 and len(fetch_sources) > 0: - toffee.info("ⓘ Only fetch requests generated acquires (prefetch may have been filtered)") - toffee.info("✓ Test passed: Fetch requests are being processed correctly") - elif len(prefetch_sources) > 0 and len(fetch_sources) > 0: - toffee.info("✓ Test passed: Both fetch and prefetch requests generated acquires") - # 检查优先级 - if len(acquire_requests) >= 2: - first_is_fetch = acquire_requests[0]["source"] < 4 - if first_is_fetch: - toffee.info("✓ Fetch request has priority (processed first)") - else: - toffee.info("ⓘ Prefetch request processed first (fair arbitration)") - elif len(prefetch_sources) > 0 and len(fetch_sources) == 0: - toffee.info("ⓘ Only prefetch requests generated acquires") - toffee.info("✓ Test passed: Prefetch requests are being processed correctly") - else: - toffee.info("⚠ No acquire requests generated - this may indicate an issue") - assert False, "No acquire requests were generated" - - toffee.info("✓ Arbitration logic test completed successfully") + prefetch_reqs = [ + (0x6000, 0x11), + (0x7000, 0x12), + (0x8000, 0x13), + ] + + toffee.info("CP32.1: enqueue new prefetch misses and observe FIFO pointer advance.") + for idx, (blk, vset) in enumerate(prefetch_reqs): + await send_prefetch_once(blk, vset) + enq_ptr = int(get_internal_signal(dut, "enq_ptr_value")) + assert enq_ptr == (idx + 1) % 10 + + toffee.info("CP32.3 & CP32.4: drain FIFO in-order and verify priorityFIFO head indices.") + for expected_idx, (blk, vset) in enumerate(prefetch_reqs): + fifo_head = int(get_internal_signal(dut, "fifo_deq_bits")) + assert fifo_head == expected_idx, ( + f"priorityFIFO head mismatch before dequeue #{expected_idx}: expected {expected_idx}, got {fifo_head}" + ) + acquire = await agent.drive_get_and_acknowledge_acquire(timeout_cycles=80, ack_cycles=1) + assert acquire is not None, "Expected acquire for enqueued prefetch" + assert acquire["source"] == 4 + expected_idx, ( + f"Prefetch acquire source mismatch: expected {4 + expected_idx}, got {acquire['source']}" + ) + await bundle.step() + + toffee.info("CP32.2: duplicate prefetch hit should be filtered.") + dup_blk, dup_vset = prefetch_reqs[1] + await send_prefetch_once(dup_blk, dup_vset) + prefetch_hit = int(get_internal_signal(dut, "prefetch_hit")) + demux_valid = int(get_internal_signal(dut, "prefetch_demux_valid")) + assert prefetch_hit == 1, "Duplicate prefetch should assert prefetch_hit" + assert demux_valid == 0, "Prefetch demux must stall when hit occurs" + + toffee.info("MISSUNIT CP32 checks completed successfully.") @toffee_test.testcase -async def test_grant_beat_collection(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_CP33_MSHR_manage(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点 CP35: Grant数据接收与beat收集 - 验证多beat数据的正确收集和last_fire信号 + Covers MISSUNIT CP33.1/CP33.2: MSHR lookup hits within same pipe and across fetch/prefetch groups. """ - toffee.info("\n--- Testing Grant Beat Collection ---") + dut = icachemissunit_env.dut agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - # 发送fetch请求 - test_addr = 0x8000 - test_idx = 0x80 - send_result = await agent.drive_send_fetch_request(blkPaddr=test_addr, vSetIdx=test_idx) - assert send_result["send_success"] is True, "Fetch request should succeed" - - # 获取acquire - acquire_info = await agent.drive_get_acquire_request() - assert acquire_info is not None, "Should get acquire request" - await agent.drive_acknowledge_acquire(cycles=1) - - # 设置victim way - await agent.drive_set_victim_way(way=1) - - # 发送第一个beat的Grant - toffee.info("Sending first beat of Grant data") - first_beat_data = 0x1111111122222222 - bundle.io._mem._grant._valid.value = 1 - bundle.io._mem._grant._bits._opcode.value = 0x5 # opcode[0] = 1 - bundle.io._mem._grant._bits._source.value = acquire_info["source"] - bundle.io._mem._grant._bits._data.value = first_beat_data - bundle.io._mem._grant._bits._corrupt.value = 0 - await bundle.step() - - # 验证last_fire应该为0(还有更多beat) - assert bundle.ICacheMissUnit_.last_fire.value == 0, "last_fire should be 0 for first beat" - - # 发送第二个beat的Grant - toffee.info("Sending second beat of Grant data") - second_beat_data = 0x3333333344444444 - bundle.io._mem._grant._bits._data.value = second_beat_data + + def prefetch_mshr_ready_vec(): + return [ + int(getattr(bundle.ICacheMissUnit_._prefetchMSHRs, f"_{i}")._io._req_ready.value) + for i in range(10) + ] + + async def send_fetch(blk, vset): + info = await agent.drive_send_fetch_request(blkPaddr=blk, vSetIdx=vset) + assert info["send_success"], f"Fetch ({hex(blk)}, {hex(vset)}) should handshake" + await bundle.step() + + async def send_prefetch(blk, vset): + info = await agent.drive_send_prefetch_req(blkPaddr=blk, vSetIdx=vset) + assert info["send_success"], f"Prefetch ({hex(blk)}, {hex(vset)}) should handshake" + await bundle.step() + + toffee.info("\n--- [MISSUNIT] CP33 MSHR manage ---") + + await agent.fencei_func(0) + await agent.drive_set_flush(False) await bundle.step() - - # 验证last_fire应该为1(最后一个beat) - assert bundle.ICacheMissUnit_.last_fire.value == 1, "last_fire should be 1 for last beat" - - bundle.io._mem._grant._valid.value = 0 + + fetch_addr = 0x9000 + fetch_idx = 0x21 + + toffee.info("CP33.1a: Create a fetch MSHR entry.") + await send_fetch(fetch_addr, fetch_idx) + + toffee.info("CP33.1b: Duplicate fetch should assert fetchHit (same-pipe lookup).") + await send_fetch(fetch_addr, fetch_idx) + fetch_hit = int(get_internal_signal(dut, "fetch_hit")) + fetch_demux_valid = int(get_internal_signal(dut, "fetch_demux_valid")) + assert fetch_hit == 1, "Duplicate fetch should hit existing entry" + assert fetch_demux_valid == 0, "Fetch demux must not forward duplicate request" + + toffee.info("CP33.2a: Prefetch of same line should see fetch MSHRs via lookups.") + # Drive fetch/prefetch concurrently to cover CP33.3 (same-cycle same-line lookup). + bundle.io._fetch._req._bits._blkPaddr.value = fetch_addr + bundle.io._fetch._req._bits._vSetIdx.value = fetch_idx + bundle.io._fetch._req._valid.value = 1 + bundle.io._prefetch_req._bits._blkPaddr.value = fetch_addr + bundle.io._prefetch_req._bits._vSetIdx.value = fetch_idx + bundle.io._prefetch_req._valid.value = 1 await bundle.step() - - # 验证last_fire_r为1 - assert bundle.ICacheMissUnit_.last_fire_r.value == 1, "last_fire_r should be 1 after last beat" - - toffee.info("Grant beat collection test completed successfully.") + bundle.io._fetch._req._valid.value = 0 + bundle.io._prefetch_req._valid.value = 0 + prefetch_hit = int(get_internal_signal(dut, "prefetch_hit")) + prefetch_demux_valid = int(get_internal_signal(dut, "prefetch_demux_valid")) + assert prefetch_hit == 1, "Prefetch should detect existing fetch MSHR entry" + assert prefetch_demux_valid == 0, "Prefetch demux must stay idle when hit occurs" + + toffee.info("CP33.1c: Allocate a standalone prefetch entry (miss expected).") + prefetch_addr = 0xB000 + prefetch_idx = 0x35 + ready_before_prefetch = prefetch_mshr_ready_vec() + await send_prefetch(prefetch_addr, prefetch_idx) + ready_after_prefetch = prefetch_mshr_ready_vec() + assert ready_before_prefetch[0] == 1 and ready_after_prefetch[0] == 0, \ + "Unique prefetch should consume the lowest-index prefetch MSHR" + assert ready_before_prefetch[1:] == ready_after_prefetch[1:], \ + "Only one prefetch MSHR should be consumed for a single miss" + + toffee.info("CP33.1d: Duplicate prefetch should assert prefetchHit (same-pipe lookup).") + await send_prefetch(prefetch_addr, prefetch_idx) + dup_prefetch_hit = int(get_internal_signal(dut, "prefetch_hit")) + dup_prefetch_demux_valid = int(get_internal_signal(dut, "prefetch_demux_valid")) + assert dup_prefetch_hit == 1, "Duplicate prefetch should hit existing prefetch MSHR entry" + assert dup_prefetch_demux_valid == 0, "Prefetch demux remains idle on duplicate hit" + assert prefetch_mshr_ready_vec() == ready_after_prefetch, \ + "Duplicate prefetch must not allocate additional MSHRs" + + toffee.info("CP33.2b: Fetch same line should detect prefetch MSHR entry.") + await send_fetch(prefetch_addr, prefetch_idx) + fetch_hit_cross = int(get_internal_signal(dut, "fetch_hit")) + fetch_demux_valid_cross = int(get_internal_signal(dut, "fetch_demux_valid")) + assert fetch_hit_cross == 1, "Fetch should detect prefetch MSHR entry" + assert fetch_demux_valid_cross == 0, "Fetch demux remains idle on hit" + + toffee.info("MISSUNIT CP33 checks completed successfully.") @toffee_test.testcase -async def test_victim_way_update(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_CP34_acquireArb_arbitration(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点 CP36: 替换策略更新 - 验证acquire fire时victim信号的正确生成 + Covers MISSUNIT CP34.1: acquireArb prioritizes fetch MSHRs over prefetch MSHRs. """ - toffee.info("\n--- Testing Victim Way Update ---") agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - # 发送fetch请求 - send_result = await agent.drive_send_fetch_request(blkPaddr=0x9000, vSetIdx=0x90) - assert send_result["send_success"] is True, "Fetch request should succeed" - - # 等待acquire信号 - await bundle.step(3) - - # 验证当acquire valid时,检查victim信号 - if bundle.io._mem._acquire._valid.value == 1: - toffee.info("Acquire is valid, checking victim signals") - - # 设置acquire ready来触发fire - bundle.io._mem._acquire._ready.value = 1 + + async def send_fetch(blk, vset): + info = await agent.drive_send_fetch_request(blkPaddr=blk, vSetIdx=vset) + assert info["send_success"], "Fetch request should handshake" await bundle.step() - - # 验证victim valid信号 - assert bundle.io._victim._vSetIdx._valid.value == 1, "Victim valid should be high when acquire fires" - - # 验证victim bits包含正确的vSetIdx - victim_idx = bundle.io._victim._vSetIdx._bits.value - expected_idx = 0x90 - assert victim_idx == expected_idx, f"Victim vSetIdx should be {hex(expected_idx)}, got {hex(victim_idx)}" - - bundle.io._mem._acquire._ready.value = 0 - - toffee.info("Victim way update test completed successfully.") + + async def send_prefetch(blk, vset): + info = await agent.drive_send_prefetch_req(blkPaddr=blk, vSetIdx=vset) + assert info["send_success"], "Prefetch request should handshake" + await bundle.step() + + toffee.info("\n--- [MISSUNIT] CP34 acquire arbiter ---") + + await agent.fencei_func(0) + await agent.drive_set_flush(False) + await bundle.step() + + async def next_acquire_source(): + acquire = await agent.drive_get_and_acknowledge_acquire(timeout_cycles=40, ack_cycles=1) + assert acquire is not None, "Expected acquire from arbiter" + return acquire["source"] + + toffee.info("CP34.1a: Three independent fetches should consume sources 0/1/2 in order.") + fetch_seq = [ + (0xC000, 0x40), + (0xC010, 0x41), + (0xC020, 0x42), + ] + for expected_src, (blk, vset) in enumerate(fetch_seq): + await send_fetch(blk=blk, vset=vset) + src = await next_acquire_source() + assert src == expected_src, f"Fetch #{expected_src} should use source {expected_src}, got {src}" + + toffee.info("CP34.1b: Prefetch should use prefetch source (>=4) once fetch slots are occupied.") + await send_prefetch(blk=0xD000, vset=0x50) + prefetch_src = await next_acquire_source() + assert prefetch_src == 4, f"Prefetch acquire should use source 4, got {prefetch_src}" + + toffee.info("MISSUNIT CP34 checks completed successfully.") + @toffee_test.testcase -async def test_sram_write_conditions(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_CP35_grant_accept_and_refill(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点 CP37: SRAM写回条件 - 验证在不同条件下Meta/Data写信号的生成 + Covers MISSUNIT CP35.x: grant beat handling, readBeatCnt sequencing, last_fire/id tracking, and corrupt flag behavior. """ - toffee.info("\n--- Testing SRAM Write Conditions ---") agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - # 测试正常写入情况 - toffee.info("Testing normal SRAM write (no flush/fencei)") - test_addr = 0xA000 - test_idx = 0xA0 - - # 完整的fetch流程 - send_result = await agent.drive_send_fetch_request(blkPaddr=test_addr, vSetIdx=test_idx) - acquire_info = await agent.drive_get_acquire_request() - await agent.drive_acknowledge_acquire(cycles=1) - await agent.drive_set_victim_way(way=2) - - # 发送正常的Grant数据 - grant_data = [0xAAAAAAAABBBBBBBB, 0xCCCCCCCCDDDDDDDD] - await agent.drive_respond_with_grant(source_id=acquire_info['source'], data_beats=grant_data) - - await bundle.step(2) # 等待写信号稳定 - - # 验证在last_fire_r=1时,meta_write和data_write都有效(假设无flush/fencei) - if bundle.ICacheMissUnit_.last_fire_r.value == 1: - toffee.info("Checking SRAM write signals") - if bundle.io._flush.value == 0 and bundle.io._fencei.value == 0: - # 在正常情况下应该写SRAM - toffee.info("Normal condition: should write to SRAM") - # Note: 实际的write valid可能还依赖于其他条件,这里主要验证逻辑 - - # 验证fetch响应总是生成 - assert bundle.io._fetch._resp._valid.value == 1, "Fetch response should always be generated" - - toffee.info("SRAM write conditions test completed successfully.") + dut = icachemissunit_env.dut + + def fetch_mshr_ready_vec(): + return [ + int(getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}")._io._req_ready.value) + for i in range(4) + ] + + async def send_fetch_for_acquire(blk, vset): + info = await agent.drive_send_fetch_request(blkPaddr=blk, vSetIdx=vset) + assert info["send_success"], "Fetch request should handshake" + await bundle.step() + acquire = await agent.drive_get_and_acknowledge_acquire(timeout_cycles=40, ack_cycles=1) + assert acquire is not None, "Expected acquire after fetch miss" + return acquire["source"] + + def sample_grant_state(): + return { + "readBeatCnt": int(get_internal_signal(dut, "readBeatCnt")), + "last_fire": int(get_internal_signal(dut, "last_fire")), + "last_fire_r": int(get_internal_signal(dut, "last_fire_r")), + "id_r": int(get_internal_signal(dut, "id_r")), + "corrupt_r": int(get_internal_signal(dut, "corrupt_r")), + } + + toffee.info("\n--- [MISSUNIT] CP35 grant accept & refill ---") + + await agent.fencei_func(0) + await agent.drive_set_flush(False) + await bundle.step() + + # CP35.0 / CP35.3-start: MSHR occupancy before grants + toffee.info("CP35.0/35.3-start: verify fetch MSHR idle → occupied after request.") + initial_ready = fetch_mshr_ready_vec() + assert initial_ready == [1, 1, 1, 1], "Fetch MSHRs should start idle" + source = await send_fetch_for_acquire(blk=0xE000, vset=0x60) + busy_ready = fetch_mshr_ready_vec() + busy_index = next((i for i, r in enumerate(busy_ready) if r == 0), None) + assert busy_index is not None, "One fetch MSHR should be occupied after sending request" + toffee.info(f"CP35.3-start: fetch MSHR {busy_index} busy (vector {busy_ready})") + + # CP35.1: first grant beat + toffee.info("CP35.1: apply first grant beat and check readBeatCnt toggles.") + data_beat0 = 0x11223344556677889900AABBCCDDEEFF + await agent.drive_respond_with_grant( + source_id=source, + data_beats=[data_beat0], + is_corrupt_list=[False], + ) + state_after_first = sample_grant_state() + assert int(get_internal_signal(dut, "respDataReg_0")) == data_beat0, "respDataReg_0 should capture first beat data" + assert state_after_first["readBeatCnt"] == 1, "readBeatCnt should increment after first beat" + + # CP35.2 + CP35.4: second beat, last_fire, corrupt latch + toffee.info("CP35.2/CP35.4: send second grant beat.") + data_beat1 = 0xFFEEDDCCBBAA00998877665544332211 + await agent.drive_respond_with_grant( + source_id=source, + data_beats=[data_beat1], + is_corrupt_list=[True], + ) + state_after_second = sample_grant_state() + resp1 = int(get_internal_signal(dut, "respDataReg_1")) + assert resp1 == data_beat1, "respDataReg_1 should capture second beat data" + assert state_after_second["readBeatCnt"] == 0, "readBeatCnt should reset after second beat" + assert state_after_second["corrupt_r"] == 1, "corrupt_r should latch high during corrupt beat" + + # CP35.3-end: check last_fire_r/id_r/corrupt_r latch and release MSHR + toffee.info("CP35.3-end: verify last_fire_r/id_r latch.") + state_after_last_fire = sample_grant_state() + assert state_after_last_fire["last_fire_r"] == 1, "last_fire_r should latch high in the next cycle" + assert state_after_last_fire["id_r"] == source, "id_r should capture grant source" + assert state_after_last_fire["corrupt_r"] == 1, "corrupt_r remains high until response window completes" + + await bundle.step() + state_after_clear = sample_grant_state() + assert state_after_clear["corrupt_r"] == 0, "corrupt_r should clear after response window" + assert state_after_clear["last_fire_r"] == 0, "last_fire_r should drop after one cycle" + + final_ready = fetch_mshr_ready_vec() + assert final_ready == initial_ready, "Fetch MSHR should be released after grant completes" + toffee.info(f"CP35.3-end: fetch MSHR released (vector {final_ready})") + + toffee.info("MISSUNIT CP35 checks completed successfully.") @toffee_test.testcase -async def test_no_write_with_flush_fencei(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_CP36_Replacer(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点 CP37.2: 有flush/fencei时不写SRAM但仍发送响应 - 专门测试flush/fencei条件下的SRAM写回抑制 + Covers MISSUNIT CP36.x: Replacer update (victim set idx + waymask). """ - toffee.info("\n--- Testing No SRAM Write with Flush/Fencei ---") agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - # 测试flush情况 - toffee.info("Step 1: Testing with flush signal") - test_addr = 0xB000 - test_idx = 0xB0 - - # 发送fetch请求 - send_result = await agent.drive_send_fetch_request(blkPaddr=test_addr, vSetIdx=test_idx) - acquire_info = await agent.drive_get_acquire_request() - await agent.drive_acknowledge_acquire(cycles=1) - await agent.drive_set_victim_way(way=1) - - # 在发送Grant之前设置flush信号 - toffee.info("Setting flush signal before Grant") - bundle.io._flush.value = 1 - await bundle.step(1) - - # 发送Grant数据 - grant_data = [0x1111111122222222, 0x3333333344444444] - await agent.drive_respond_with_grant(source_id=acquire_info['source'], data_beats=grant_data) - - # 等待处理完成并检查信号 - await bundle.step(3) - - # 验证CP37.2覆盖点条件 - if bundle.ICacheMissUnit_.last_fire_r.value == 1: - meta_write_valid = bundle.io._meta_write._valid.value - data_write_valid = bundle.io._data_write._valid.value - fetch_resp_valid = bundle.io._fetch._resp._valid.value - flush_active = bundle.io._flush.value - - toffee.info(f"Flush scenario - meta_write_valid: {meta_write_valid}, data_write_valid: {data_write_valid}") - toffee.info(f" fetch_resp_valid: {fetch_resp_valid}, flush: {flush_active}") - - # CP37.2 期望:flush时不写SRAM但仍发送响应 - if flush_active == 1: - toffee.info("✓ CP37.2 condition detected: flush active, checking SRAM write suppression") - if meta_write_valid == 0 and data_write_valid == 0 and fetch_resp_valid == 1: - toffee.info("✓ CP37.2 Coverage achieved: No SRAM write but fetch response generated") - else: - toffee.info(f"ⓘ CP37.2 partial: write suppression may depend on other conditions") - - # 响应应该总是生成 - assert fetch_resp_valid == 1, "Fetch response should always be generated even with flush" - - # 清除flush信号 - bundle.io._flush.value = 0 - await bundle.step(2) - - # 测试fencei情况 - toffee.info("Step 2: Testing with fencei signal") - test_addr = 0xC000 - test_idx = 0xC0 - - # 发送另一个fetch请求 - send_result = await agent.drive_send_fetch_request(blkPaddr=test_addr, vSetIdx=test_idx) - acquire_info = await agent.drive_get_acquire_request() - await agent.drive_acknowledge_acquire(cycles=1) - await agent.drive_set_victim_way(way=3) - - # 在发送Grant之前设置fencei信号 - toffee.info("Setting fencei signal before Grant") - bundle.io._fencei.value = 1 - await bundle.step(1) - - # 发送Grant数据 - grant_data = [0x5555555566666666, 0x7777777788888888] - await agent.drive_respond_with_grant(source_id=acquire_info['source'], data_beats=grant_data) - - # 等待处理完成并检查信号 - await bundle.step(3) - - # 验证CP37.2覆盖点条件(fencei版本) - if bundle.ICacheMissUnit_.last_fire_r.value == 1: - meta_write_valid = bundle.io._meta_write._valid.value - data_write_valid = bundle.io._data_write._valid.value - fetch_resp_valid = bundle.io._fetch._resp._valid.value - fencei_active = bundle.io._fencei.value - - toffee.info(f"Fencei scenario - meta_write_valid: {meta_write_valid}, data_write_valid: {data_write_valid}") - toffee.info(f" fetch_resp_valid: {fetch_resp_valid}, fencei: {fencei_active}") - - # CP37.2 期望:fencei时不写SRAM但仍发送响应 - if fencei_active == 1: - toffee.info("✓ CP37.2 condition detected: fencei active, checking SRAM write suppression") - if meta_write_valid == 0 and data_write_valid == 0 and fetch_resp_valid == 1: - toffee.info("✓ CP37.2 Coverage achieved: No SRAM write but fetch response generated") - else: - toffee.info(f"ⓘ CP37.2 partial: write suppression may depend on other conditions") - - # 响应应该总是生成 - assert fetch_resp_valid == 1, "Fetch response should always be generated even with fencei" - - # 清除fencei信号 - bundle.io._fencei.value = 0 - await bundle.step(2) - - toffee.info("No SRAM write with flush/fencei test completed successfully.") + dut = icachemissunit_env.dut + + toffee.info("\n--- [MISSUNIT] CP36 replacer update ---") + + await agent.fencei_func(0) + await agent.drive_set_flush(False) + await bundle.step() + + toffee.info("CP36.1: send fetch miss and check victim vSetIdx update.") + victim_way = 2 + await agent.drive_set_victim_way(victim_way) + test_addr = 0xF000 + test_vset = 0x70 + send_result = await agent.drive_send_fetch_request(test_addr, test_vset) + assert send_result["send_success"], "Fetch miss should be accepted for replacer test" + + acquire = await agent.drive_get_acquire_request(timeout_cycles=40) + assert acquire is not None, "Expected acquire for replacer test" + + # CP36.1: victim should update on acquire fire + bundle.io._mem._acquire._ready.value = 1 + await bundle.step() + victim_valid = int(get_internal_signal(dut, "victim_valid")) + victim_bits = bundle.io._victim._vSetIdx._bits.value + assert victim_valid == 1, "Victim update valid should be asserted on acquire fire" + assert victim_bits == test_vset, f"Victim vSetIdx should match request ({test_vset}), got {victim_bits}" + bundle.io._mem._acquire._ready.value = 0 + await bundle.step() + + toffee.info("CP36.2: verify replacer waymask matches victim way.") + grant_data = [ + 0x0123456789ABCDEF_0123456789ABCDEF, + 0xFEDCBA9876543210_FEDCBA9876543210, + ] + await agent.drive_respond_with_grant( + source_id=acquire["source"], + data_beats=grant_data, + is_corrupt_list=[False, False], + ) + response = await agent.drive_get_fetch_response() + assert response is not None, "Fetch response should be produced after grant" + expected_waymask = 1 << victim_way + observed_way = int(get_internal_signal(dut, "mshr_resp_way")) + assert observed_way == victim_way, \ + f"mshr_resp_way should match victim way {victim_way}, got {observed_way}" + assert response["waymask"] == expected_waymask, \ + f"Waymask should match victim way {victim_way}, expected {expected_waymask:#x}, got {response['waymask']:#x}" + + toffee.info("MISSUNIT CP36 checks completed successfully.") @toffee_test.testcase -async def test_flush_fencei_mshr_behavior(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_CP37_SRAM_writeback(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点 CP38: flush/fencei对MSHR的影响 - 验证flush只影响prefetch MSHR,fencei影响所有MSHR + Covers MISSUNIT CP37.x: SRAM writeback controls/data. """ - toffee.info("\n--- Testing Flush/Fencei MSHR Behavior ---") agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - # 测试fencei清除所有MSHR - toffee.info("Testing fencei clears all MSHRs") - - # 先发送一些请求占用MSHR - await agent.drive_send_fetch_request(blkPaddr=0xB000, vSetIdx=0xB0) - await agent.drive_send_prefetch_req(blkPaddr=0xC000, vSetIdx=0xC0) - await bundle.step(2) - - # 验证MSHR被占用 - fetch_0_ready_before = getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_0")._io._req_ready.value - prefetch_0_ready_before = getattr(bundle.ICacheMissUnit_._prefetchMSHRs, f"_0")._io._req_ready.value - toffee.info(f"Before fencei - Fetch MSHR 0 ready: {fetch_0_ready_before}, Prefetch MSHR 0 ready: {prefetch_0_ready_before}") - - # 应用fencei - await agent.fencei_func(1) - await bundle.step(5) - - # 验证所有MSHR的req_ready都变为0 - for i in range(4): - fetch_ready = getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}")._io._req_ready.value - assert fetch_ready == 0, f"Fetch MSHR {i} should not be ready during fencei" - - for i in range(10): - prefetch_ready = getattr(bundle.ICacheMissUnit_._prefetchMSHRs, f"_{i}")._io._req_ready.value - assert prefetch_ready == 0, f"Prefetch MSHR {i} should not be ready during fencei" - - # 清除fencei + dut = icachemissunit_env.dut + + def expected_waymask(way): + return 1 << way + + toffee.info("\n--- [MISSUNIT] CP37 SRAM writeback ---") + await agent.fencei_func(0) - await bundle.step(5) - - # 测试flush只影响prefetch MSHR - toffee.info("Testing flush affects only prefetch MSHRs") - - # 发送fetch和prefetch请求 - await agent.drive_send_fetch_request(blkPaddr=0xD000, vSetIdx=0xD0) - await agent.drive_send_prefetch_req(blkPaddr=0xE000, vSetIdx=0xE0) - await bundle.step(2) - - # 应用flush - await agent.drive_set_flush(True) - await bundle.step(2) - - # 验证fetch MSHR不受影响,prefetch MSHR受影响 - # Note: 具体的行为可能需要根据实际实现调整验证逻辑 - await agent.drive_set_flush(False) - await bundle.step(2) - - toffee.info("Flush/Fencei MSHR behavior test completed successfully.") + await bundle.step() -@toffee_test.testcase -async def test_mshr_release_after_grant(icachemissunit_env: ICacheMissUnitEnv): - """ - 测试点: MSHR在Grant完成后的正确释放 - 验证MSHR状态更新与释放逻辑 - """ - toffee.info("\n--- Testing MSHR Release After Grant ---") - agent = icachemissunit_env.agent - bundle = icachemissunit_env.bundle - - # 发送fetch请求 - test_addr = 0xF000 - test_idx = 0xF0 - send_result = await agent.drive_send_fetch_request(blkPaddr=test_addr, vSetIdx=test_idx) - assert send_result["send_success"] is True, "Fetch request should succeed" - await bundle.step(3) - - # 检查是否有acquire请求生成(说明进入了MSHR) - acquire_info = await agent.drive_get_acquire_request(timeout_cycles=5) - if acquire_info is None: - toffee.info("⚠ No acquire generated - request may have hit cache") - toffee.info("✓ Test skipped: Cannot test MSHR release without miss") - return - - toffee.info(f"Acquire generated: source={acquire_info['source']}, address=0x{acquire_info['address']:x}") - - # 验证对应的MSHR被占用 - mshr_source = acquire_info['source'] - if mshr_source < 4: # fetch MSHR - mshr_ready = getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_{mshr_source}")._io._req_ready.value - toffee.info(f"Fetch MSHR {mshr_source} req_ready = {mshr_ready}") - if mshr_ready == 1: - toffee.info("⚠ MSHR is still ready - request may not have been processed as expected") - else: - toffee.info(f"Prefetch MSHR source {mshr_source} - skipping ready check") - - # 完成acquire handshake + test_addr = 0x12345000 + test_vset = 0x5A + victim_way = 1 + + await agent.drive_set_victim_way(victim_way) + send_result = await agent.drive_send_fetch_request(test_addr, test_vset) + assert send_result["send_success"], "Fetch miss should be accepted for SRam writeback test" + + acquire = await agent.drive_get_acquire_request(timeout_cycles=40) + assert acquire is not None, "Expected acquire for SRAM writeback test" await agent.drive_acknowledge_acquire(cycles=1) - await agent.drive_set_victim_way(way=3) - - grant_data = [0x1234567890ABCDEF, 0xFEDCBA0987654321] - await agent.drive_respond_with_grant(source_id=acquire_info['source'], data_beats=grant_data) - - # 等待Grant处理完成 - await bundle.step(5) - - # 验证MSHR状态(对于fetch MSHR) - if mshr_source < 4: - final_mshr_ready = getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_{mshr_source}")._io._req_ready.value - toffee.info(f"After Grant: Fetch MSHR {mshr_source} req_ready = {final_mshr_ready}") - if final_mshr_ready == 1: - toffee.info(f"✓ MSHR {mshr_source} successfully released after Grant") - else: - toffee.info(f"ⓘ MSHR {mshr_source} still busy - may need more time or different conditions") - else: - toffee.info(f"✓ Grant processed for prefetch MSHR {mshr_source}") - - toffee.info("MSHR release after grant test completed successfully.") + + grant_data = [ + 0x11112222333344445555666677778888, + 0x9999AAAABBBBCCCCDDDDEEEEFFFF0000, + ] + await agent.drive_respond_with_grant( + source_id=acquire["source"], + data_beats=grant_data, + is_corrupt_list=[False, False], + ) + + response = await agent.drive_get_fetch_response() + assert response is not None, "Fetch response should be produced after grant" + + # CP37.1: meta/data write valids asserted when difftest_valid true + assert bundle.io._meta_write._valid.value == 1, "meta_write.valid should be high when difftest_valid" + assert bundle.io._data_write._valid.value == 1, "data_write.valid should be high when difftest_valid" + + # CP37.2: meta/data contents reflect miss info + meta_bits = bundle.io._meta_write._bits + data_bits = bundle.io._data_write._bits + expected_phy = test_addr >> 6 + assert meta_bits._virIdx.value == test_vset, "meta virIdx mismatch" + assert meta_bits._phyTag.value == expected_phy, "meta phyTag mismatch" + assert meta_bits._waymask.value == expected_waymask(victim_way), "meta waymask mismatch" + assert meta_bits._bankIdx.value == (test_vset & 0x1), "meta bankIdx mismatch" + + assert data_bits._virIdx.value == test_vset, "data virIdx mismatch" + assert data_bits._waymask.value == expected_waymask(victim_way), "data waymask mismatch" + assert data_bits._data.value == int(response["data"]), "data payload mismatch" + + toffee.info("MISSUNIT CP37 checks completed successfully.") @toffee_test.testcase -async def test_waymask_generation(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_CP38_mainpipe_iprefetchpipe_response(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点: waymask生成逻辑 - 验证根据victim way正确生成waymask + Covers MISSUNIT CP38.x: fetch response generation for fetch/prefetch paths. """ - toffee.info("\n--- Testing Waymask Generation ---") agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - test_ways = [0, 1, 2, 3] # 测试所有4种way - - for way in test_ways: - toffee.info(f"Testing waymask generation for way {way}") - - # 发送请求 - test_addr = 0x10000 + way * 0x1000 - test_idx = 0x10 + way - - send_result = await agent.drive_send_fetch_request(blkPaddr=test_addr, vSetIdx=test_idx) - assert send_result["send_success"] is True, f"Fetch request for way {way} should succeed" - - # 处理acquire - acquire_info = await agent.drive_get_acquire_request() - await agent.drive_acknowledge_acquire(cycles=1) - await bundle.step() - - # 在发送Grant之前设置victim way(这是关键时序) - await agent.drive_set_victim_way(way=way) - toffee.info(f"Set victim way to {way} before Grant") - - # 发送Grant - grant_data = [0x1111111111111111, 0x2222222222222222] - await agent.drive_respond_with_grant(source_id=acquire_info['source'], data_beats=grant_data) - - # 获取响应并验证waymask - response_info = await agent.drive_get_fetch_response(timeout_cycles=10) - assert response_info is not None, f"Should get fetch response for way {way}" - - expected_waymask = 1 << way # 独热编码 - actual_waymask = response_info["waymask"] - - toffee.info(f"Way {way}: Expected waymask={expected_waymask}, Actual waymask={actual_waymask}") - - # 对于way 0,waymask应该是1,这是正确的 - # 对于其他way,可能需要调整测试策略 - if way == 0: - assert actual_waymask == expected_waymask, f"Waymask for way {way} should be {expected_waymask}, got {actual_waymask}" - toffee.info(f"✓ Way {way} waymask generation verified: {actual_waymask}") + dut = icachemissunit_env.dut + + async def run_miss_and_capture(fetch=True, corrupt=False): + addr = 0x2000 if fetch else 0x3000 + vset = 0x22 if fetch else 0x33 + if fetch: + result = await agent.drive_send_fetch_request(addr, vset) else: - # 对于非0 way,先观察实际行为 - toffee.info(f"ⓘ Way {way} waymask generation observed: expected={expected_waymask}, actual={actual_waymask}") - if actual_waymask == expected_waymask: - toffee.info(f"✓ Way {way} waymask generation works correctly!") - else: - toffee.info(f"ⓘ Way {way} waymask differs from expected - may need timing adjustment") - - await bundle.step(3) - - toffee.info("Waymask generation test completed successfully.") + result = await agent.drive_send_prefetch_req(addr, vset) + assert result["send_success"], "Miss request should be accepted" + acquire = await agent.drive_get_acquire_request(timeout_cycles=40) + assert acquire is not None, "Expected acquire after miss" + await agent.drive_acknowledge_acquire(cycles=1) + grant_words = [ + 0xAAAABBBBCCCCDDDDEEEEFFFF11112222, + 0x3333444455556666777788889999AAAA, + ] + await agent.drive_respond_with_grant( + source_id=acquire["source"], + data_beats=grant_words, + is_corrupt_list=[corrupt, corrupt], + ) + resp = await agent.drive_get_fetch_response() + assert resp is not None, "Fetch response should be produced" + return resp, addr, vset, corrupt + + toffee.info("\n--- [MISSUNIT] CP38 main/prefetch response ---") + + await agent.fencei_func(0) + await agent.drive_set_flush(False) + await bundle.step() + + resp_fetch, fetch_addr, fetch_vset, _ = await run_miss_and_capture(fetch=True, corrupt=False) + assert resp_fetch["blkPaddr"] == fetch_addr, "fetch blkPaddr mismatch" + assert resp_fetch["vSetIdx"] == fetch_vset, "fetch vSetIdx mismatch" + assert resp_fetch["data"] == bundle.io._data_write._bits._data.value, "fetch data payload mismatch" + assert resp_fetch["corrupt"] is False, "fetch corrupt flag should be false" + + resp_prefetch, prefetch_addr, prefetch_vset, _ = await run_miss_and_capture(fetch=False, corrupt=True) + assert resp_prefetch["blkPaddr"] == prefetch_addr, "prefetch blkPaddr mismatch" + assert resp_prefetch["vSetIdx"] == prefetch_vset, "prefetch vSetIdx mismatch" + assert resp_prefetch["corrupt"] is True, "prefetch corrupt flag should propagate" + + toffee.info("MISSUNIT CP38 checks completed successfully.") @toffee_test.testcase -async def test_prefetch_same_address_as_fetch(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_CP39_flush_fencei_operation(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点: prefetch请求与fetch请求地址相同时的hit检测 - 验证文档中提到的特殊hit条件 + Covers MISSUNIT CP39.x: flush/fencei behavior before and after MSHR fire. """ - toffee.info("\n--- Testing Prefetch Hit on Same Address as Fetch ---") agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - test_addr = 0x20000 - test_idx = 0x200 - - # 同时发送fetch和prefetch请求到相同地址 - toffee.info("Sending fetch and prefetch requests to same address simultaneously") - - # 设置fetch请求 - bundle.io._fetch._req._valid.value = 1 - bundle.io._fetch._req._bits._blkPaddr.value = test_addr - bundle.io._fetch._req._bits._vSetIdx.value = test_idx - - # 设置prefetch请求到相同地址 - bundle.io._prefetch_req._valid.value = 1 - bundle.io._prefetch_req._bits._blkPaddr.value = test_addr - bundle.io._prefetch_req._bits._vSetIdx.value = test_idx - + dut = icachemissunit_env.dut + + async def send_fetch(addr, vset): + info = await agent.drive_send_fetch_request(addr, vset) + assert info["send_success"], "Fetch miss should be accepted" + + async def wait_for_acquire(): + acquire = await agent.drive_get_acquire_request(timeout_cycles=40) + assert acquire is not None, "Expected acquire" + return acquire + + def fetch_mshr_ready_vec(): + return [ + int(getattr(bundle.ICacheMissUnit_._fetchMSHRs, f"_{i}")._io._req_ready.value) + for i in range(4) + ] + + toffee.info("\n--- [MISSUNIT] CP39 flush/fencei ---") + + await agent.fencei_func(0) + await agent.drive_set_flush(False) await bundle.step() - - # 验证prefetch hit(由于与fetch地址相同) - prefetch_hit = bundle.ICacheMissUnit_.prefetchHit.value - toffee.info(f"Prefetch hit status: {prefetch_hit}") - - # 根据文档,当prefetch与fetch地址相同且fetch valid时,prefetch应该hit - assert prefetch_hit == 1, "Prefetch should hit when address matches concurrent fetch request" - - # 清理 - bundle.io._fetch._req._valid.value = 0 - bundle.io._prefetch_req._valid.value = 0 + + # CP39.1: fencei before fire should block all MSHRs + toffee.info("CP39.1: assert fencei before any acquire fires.") + await agent.fencei_func(1) await bundle.step() - - toffee.info("Prefetch same address as fetch test completed successfully.") + ready_vec = fetch_mshr_ready_vec() + assert ready_vec == [0, 0, 0, 0], "Fencei should force all fetch MSHRs ready low" + acquire = await agent.drive_get_acquire_request(timeout_cycles=5) + assert acquire is None, "Fencei asserted -> no acquire should be observed" + await agent.fencei_func(0) + + # CP39.2: flush prevents prefetch MSHR from firing + toffee.info("CP39.2: assert flush, only fetch requests may fire.") + await agent.drive_set_flush(True) + await send_fetch(0x5000, 0x50) + prefetch = await agent.drive_send_prefetch_req(0x7000, 0x70) + assert not prefetch["send_success"], "Prefetch request should be blocked under flush" + acquire_fetch = await wait_for_acquire() + assert acquire_fetch["source"] == 0, "Only fetch MSHR should fire under flush" + await agent.drive_acknowledge_acquire(cycles=2) + for _ in range(6): + if bundle.io._mem._acquire._valid.value == 0: + break + await bundle.step() + else: + raise AssertionError("Acquire valid should drop after handshake") + prefetch_acquire = await agent.drive_get_acquire_request(timeout_cycles=5) + assert prefetch_acquire is None, "Prefetch should be blocked under flush" + await agent.drive_set_flush(False) + + # CP39.3: flush after fire suppresses SRAM writes but still responds + toffee.info("CP39.3: assert flush after acquire fired, ensure SRAM write suppressed.") + await send_fetch(0x8000, 0x80) + acquire = await wait_for_acquire() + await agent.drive_acknowledge_acquire(cycles=1) + await agent.drive_set_flush(True) + grant_data = [ + 0xAAAA5555AAAA5555AAAA5555AAAA5555, + 0xBBBB6666BBBB6666BBBB6666BBBB6666, + ] + await agent.drive_respond_with_grant( + source_id=acquire["source"], + data_beats=grant_data, + is_corrupt_list=[False, False], + ) + resp = await agent.drive_get_fetch_response() + assert resp is not None, "Fetch response should still be produced under flush" + assert bundle.io._meta_write._valid.value == 0, "meta write should be suppressed when flush asserted after fire" + assert bundle.io._data_write._valid.value == 0, "data write should be suppressed when flush asserted after fire" + await agent.drive_set_flush(False) + + toffee.info("MISSUNIT CP39 checks completed successfully.") @toffee_test.testcase -async def test_demux_chosen_signal(icachemissunit_env: ICacheMissUnitEnv): +async def test_MISSUNIT_addational_all_mshr_lookup_coverage(icachemissunit_env: ICacheMissUnitEnv): """ - 测试点: Demux chosen信号的正确性 - 验证demux选择的MSHR索引是否正确 + Drives enough fetch/prefetch misses to exercise every MSHR instance + (4 fetch + 10 prefetch) and then re-issues matching requests to trigger + both io_lookUps_0_hit and io_lookUps_1_hit in every entry. This ensures + the per-instance RTL files (ICacheMSHR*.sv) are covered. """ - toffee.info("\n--- Testing Demux Chosen Signal ---") agent = icachemissunit_env.agent bundle = icachemissunit_env.bundle - - # 清空所有MSHR - await agent.fencei_func(1) - await bundle.step(5) + dut = icachemissunit_env.dut + + toffee.info("\n--- [MISSUNIT] CP40 full MSHR lookup coverage ---") await agent.fencei_func(0) - await bundle.step(5) - - # 发送prefetch请求并观察chosen信号 - for expected_chosen in range(3): # 测试前3个MSHR - toffee.info(f"Sending prefetch request {expected_chosen}") - - # 记录发送前的chosen值 - chosen_before = bundle.prefetchDemux._io_chosen.value - toffee.info(f"Chosen before request {expected_chosen}: {chosen_before}") - - send_result = await agent.drive_send_prefetch_req( - blkPaddr=0x30000 + expected_chosen * 0x1000, - vSetIdx=0x300 + expected_chosen + await agent.drive_set_flush(False) + + fetch_slots = 4 + prefetch_slots = 10 + + fetch_miss_list = [(0x1000 + i * 0x1000, 0x10 + i) for i in range(fetch_slots)] + prefetch_miss_list = [(0xA000 + i * 0x1000, 0x60 + i) for i in range(prefetch_slots)] + + async def allocate_and_collect(miss_list, sender): + acquire_sources = [] + for idx, (addr, vset) in enumerate(miss_list): + await agent.drive_set_victim_way(idx & 0x3) + info = await sender(addr, vset) + assert info["send_success"], f"MSHR allocation {idx} should succeed" + acquire = await agent.drive_get_acquire_request(timeout_cycles=40) + assert acquire is not None, "Expected acquire for miss" + acquire_sources.append(acquire["source"]) + await agent.drive_acknowledge_acquire(cycles=1) + return acquire_sources + + fetch_sources = await allocate_and_collect(fetch_miss_list, agent.drive_send_fetch_request) + + async def send_prefetch(addr, vset): + result = await agent.drive_send_prefetch_req(addr, vset, timeout_cycles=20) + return result + + prefetch_sources = [] + for idx, (addr, vset) in enumerate(prefetch_miss_list): + result = await send_prefetch(addr, vset) + assert result["send_success"], f"Prefetch miss {idx} should be accepted" + acquire = await agent.drive_get_acquire_request(timeout_cycles=40) + assert acquire is not None, "Prefetch miss should generate acquire" + prefetch_sources.append(acquire["source"]) + await agent.drive_acknowledge_acquire(cycles=1) + + toffee.info("Prefetch requests probing existing fetch entries (cross-hit).") + for addr, vset in fetch_miss_list: + info = await agent.drive_send_prefetch_req(addr, vset, timeout_cycles=5) + assert info["send_success"], "Prefetch request should still handshake" + dup = await agent.drive_get_acquire_request(timeout_cycles=5) + assert dup is None, "Prefetch hit must not create a new acquire" + + toffee.info("Fetch requests probing existing prefetch entries (cross-hit).") + for addr, vset in prefetch_miss_list: + info = await agent.drive_send_fetch_request(addr, vset) + assert info["send_success"], "Fetch request should still handshake" + dup = await agent.drive_get_acquire_request(timeout_cycles=5) + assert dup is None, "Fetch hit must not create a new acquire" + + async def service_grants(source_id): + grant_data = [ + 0x11112222333344445555666677778888, + 0x9999AAAABBBBCCCCDDDDEEEEFFFF0000, + ] + await agent.drive_respond_with_grant( + source_id=source_id, + data_beats=grant_data, + is_corrupt_list=[False, False], ) - assert send_result["send_success"] is True, f"Prefetch request {expected_chosen} should succeed" - - await bundle.step(2) - - # 验证对应的MSHR被占用 - mshr_ready = getattr(bundle.ICacheMissUnit_._prefetchMSHRs, f"_{expected_chosen}")._io._req_ready.value - assert mshr_ready == 0, f"Prefetch MSHR {expected_chosen} should be occupied" - - toffee.info(f"MSHR {expected_chosen} successfully occupied") - - toffee.info("Demux chosen signal test completed successfully.") + await bundle.step() + + for source in fetch_sources + prefetch_sources: + await service_grants(source) + + toffee.info("Re-issuing after refill completes to confirm entries free again.") + fetch_sources_second = await allocate_and_collect(fetch_miss_list, agent.drive_send_fetch_request) + prefetch_sources_second = await allocate_and_collect(prefetch_miss_list, agent.drive_send_prefetch_req) + + for source in fetch_sources_second + prefetch_sources_second: + await service_grants(source) + + toffee.info("CP40: All MSHR entries exercised; hits observed without extra acquires and entries verified free after refill.")