概述
本次更新涵盖 3
个提交(7cfbf11、97f44b8、68d2e77),涉及
5 个文件,净变更 +332 / -222 行。成果:所有性能测试通过,主频
70MHz,Cache 布局优化。
核心改动分为三部分:分支重定向去重与取指数据一致性修复(core_top.v
/ pc_reg.v)、Difftest Store
事件精确化(core_top.v)、I-Cache / D-Cache 状态机重构与
Block RAM 映射(icache_two_way.v /
dcache_two_way.v)。
修改内容
1.
分支重定向去重:id_br_sent
文件:core_top.v:362-376
问题:当流水线停顿时(如 I-Cache Miss 等待 AXI
响应),ID 阶段的分支判定结果 id_br_taken_safe
会持续有效。每个时钟周期都会重新触发分支重定向,导致 PC
被反复改写、延迟槽指令被反复冲刷,引发流水线行为混乱。
修复:新增 id_br_sent
寄存器,记录已发送过的分支重定向。当同一 PC
的分支已被处理且流水线未推进时,抑制重复触发:
1 | reg id_br_sent; |
当流水线正常推进(if_ready_go && id_allow_in)时清除标记,允许下一条分支被处理。
2. PC 寄存器简化:移除
pc_br_taken_memory
文件:pc_reg.v:22-70
问题:前一版本在 pc_reg.v 中引入了
pc_target_memory / pc_br_taken_memory
延迟寄存器,试图捕获被错过的分支脉冲。但该机制引入了额外的状态复杂性,且在
id_br_sent 去重逻辑生效后已不再需要。
修复:移除 pc_target_memory 和
pc_br_taken_memory 相关的全部逻辑,回归直接处理:
1 | end else if (pc_br_taken && pipline_is_not_stalled) begin |
分支重定向仍然在 casez 之前处理,保证 IF
停顿时不会错过跳转。id_br_sent 保证了不会重复触发。
3. 取指数据 PC
匹配:inst_fetch_pc
文件:core_top.v:298-360
问题:I-Cache 命中响应或 AXI
取指返回的数据可能对应之前的 PC(流水线停顿期间 PC
未变但请求已发出)。原设计使用 inst_sram_rdata_safe
作为缓存数据,但不检查该数据是否对应当前
if_pc,导致停顿恢复后使用了陈旧的取指结果。
修复:新增 inst_fetch_pc
寄存器,在取指请求被接受时(inst_sram_req && inst_sram_addr_ok)记录对应的
PC。if_inst 选择逻辑增加 PC 匹配检查:
1 | assign if_inst = rst ? 32'h02800000 : |
同时新增 if_suppress_dup_fetch_req,当当前周期
inst_sram_addr 与上一周期已采样的
if_last_sampled_pc 相同时,抑制重复取指请求,避免 AXI
总线上的无效事务。
4. Difftest Store 事件精确化
文件:core_top.v:2451-2537
问题:原 Difftest Store 事件使用 1-bit 的
cmt_inst_st_en 和 32-bit 的
cmt_memvis_data,无法区分 Store 的字节粒度(st.b / st.h /
st.w)。此外,cmt_memvis_paddr 直接使用虚拟地址,未经过 DMW
翻译,导致 NEMU 比对时物理地址不匹配。
修复:
4a. 字节使能:cmt_inst_st_en 从 1-bit
扩展为 8-bit,根据 wb_wdram_num
生成对应的字节使能掩码:
1 | cmt_inst_st_en <= wb_is_store ? ((wb_wdram_num == 2'b00) ? 8'b0000_0100 : |
4b. 数据扩展:cmt_memvis_data 从 32-bit
扩展为 64-bit,低 32 位为字节对齐后的 Store 数据,高 32 位补零:
1 | cmt_memvis_data <= (wb_wdram_num == 2'b01) ? {32'b0, ({24'b0, wb_dram_wdata[7:0]} << (8 * wb_data_addr[1:0]))} : |
4c. DMW 物理地址翻译:新增
wb_store_paddr,根据
csr_crmd_da、csr_crmd_pg、DMW 窗口配置将 Store
的虚拟地址翻译为物理地址:
1 | assign wb_store_paddr = (csr_crmd_da && !csr_crmd_pg) ? wb_data_addr : |
cmt_memvis_paddr 改为使用
wb_store_paddr,使 Difftest 比对使用正确的物理地址。
4d. MMIO Store 识别:新增
wb_store_mmio,识别地址范围 0xbfe0_0000 –
0xbfff_ffff 的 MMIO Store 操作,确保外设写入也被上报给
NEMU。
5. Cache 状态机重构:新增
S_LOOKUP 状态
文件:icache_two_way.v、dcache_two_way.v
问题:原设计中,I-Cache 和 D-Cache 在
S_IDLE 状态接收到请求后,同周期内完成 Tag
查找、命中判定、数据选择和状态跳转。这条组合逻辑路径包含 SRAM 读取 + Tag
比较 + 数据 MUX,在 FPGA 上是时序瓶颈。同时,所有 Cache
阵列(data0/data1)使用 reg
声明,综合工具默认映射为分布式 RAM(LUT RAM),无法利用 FPGA 的 Block
RAM 资源。
修复:新增 S_LOOKUP 状态,将 Tag
查找与命中处理分为两个周期:
1 | 原:S_IDLE → (命中) S_HIT_RESP / (未命中) S_REF_REQ / (Uncached) S_UNC_REQ |
在 S_IDLE 接收请求时,将对应 Index 的 Cache
行数据读入寄存器 data0_r / data1_r:
1 | S_IDLE: begin |
影响:Tag 查找路径从 “SRAM 读取 + 比较 + MUX” 缩短为 “寄存器读取 + 比较 + MUX”,关键路径延迟显著降低。代价是命中延迟增加 1 周期。
6. Cache 参数化与 Block RAM 映射
文件:icache_two_way.v、dcache_two_way.v
修复:将硬编码的 Cache 参数提取为
localparam:
1 | localparam CACHE_INDEX_BITS = 8; |
所有阵列声明改用参数化宽度:
1 | reg [CACHE_TAG_BITS-1:0] tag0 [0:CACHE_LINES-1]; |
(* ram_style = "block" *) 属性指示综合工具将 Cache
数据阵列映射为 Block RAM,而非分布式 LUT RAM。对于 256 项 × 128-bit
的阵列(每路 4KB),Block RAM 的读取延迟为 1 个时钟周期(同步读),与
S_LOOKUP 状态的流水化设计匹配。
Tag
阵列(tag0/tag1)和元数据阵列(valid0/valid1/dirty0/dirty1/used)保持分布式
RAM,因为它们的读取需要在 S_IDLE 同周期完成以决定
S_LOOKUP 的跳转方向。
7. 调试日志清理
文件:core_top.v、pc_reg.v
移除了 7cfbf11 提交中引入的 CoreMark PC
调试日志([CMPCDBG]、[PCDBG]),共约 70 行
$display 代码。这些日志在 97f44b8
提交中确认问题已修复后删除。
修改文件清单
| 文件 | 变更 |
|---|---|
core_top.v |
分支去重、取指 PC 匹配、Difftest Store 精确化、调试日志清理 |
pc_reg.v |
移除 pc_br_taken_memory 延迟逻辑,回归直接处理 |
icache_two_way.v |
新增 S_LOOKUP 状态、参数化、Block RAM 映射 |
dcache_two_way.v |
新增 S_LOOKUP 状态、参数化、Block RAM 映射 |
ID_REg.v |
移除多余空行 |