ubtb This is the set-associative variant of the Micro BTB, as opposed to the fully-associative FAMicroBTB. It uses indexed addressing like a cache.
Structure:
256 sets by default (configurable)
Each entry stores: tag, is_br, 2-bit counter, offset (signed)
Offset is stored instead of full target for space efficiency
Target = PC + slot_offset + stored_offset
Key Features:
SyncReadMem for meta and BTB storage (1-cycle read latency)
Write bypass to handle read-after-write hazards
Offset-based target calculation for reduced storage
参数 1 2 3 4 5 6 7 8 case class BoomMicroBTBParams ( nSets: Int = 256, offsetSz: Int = 13 )val nWrBypassEntries = 2 val btbEntrySz = offsetSz
基本组成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class MicroBTBEntry extends Bundle { val offset = SInt (offsetSz.W ) }class MicroBTBMeta extends Bundle { val is_br = Bool () val tag = UInt (tagSz.W ) val ctr = UInt (2. W ) }val meta = SyncReadMem (nSets, Vec (bankWidth, UInt (btbMetaSz.W )))val btb = SyncReadMem (nSets, Vec (bankWidth, UInt (btbEntrySz.W )))val wrbypass_idxs = Reg (Vec (nWrBypassEntries, UInt (log2Ceil(nSets).W )))val wrbypass = Reg (Vec (nWrBypassEntries, Vec (bankWidth, new MicroBTBMeta )))val wrbypass_enq_idx = RegInit (0. U (log2Ceil(nWrBypassEntries).W ))
读取数据 1 2 3 4 5 6 7 8 9 val s1_req_rbtb = VecInit (btb.read(s0_idx , s0_valid).map(_.asTypeOf(new MicroBTBEntry )))val s1_req_rmeta = VecInit (meta.read(s0_idx, s0_valid).map(_.asTypeOf(new MicroBTBMeta )))val s1_req_tag = s1_idx >> log2Ceil(nSets)val s1_resp = Wire (Vec (bankWidth, Valid (UInt (vaddrBitsExtended.W ))))val s1_taken = Wire (Vec (bankWidth, Bool ()))val s1_is_br = Wire (Vec (bankWidth, Bool ()))val s1_is_jal = Wire (Vec (bankWidth, Bool ()))
查询结果 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 for (w <- 0 until bankWidth) { s1_resp(w).valid := !doing_reset && s1_valid && s1_req_tag(tagSz-1 ,0 ) === s1_req_rmeta(w).tag s1_resp(w).bits := (s1_pc.asSInt + (w << 1 ).S + s1_req_rbtb(w).offset).asUInt s1_is_br(w) := !doing_reset && s1_resp(w).valid && s1_req_rmeta(w).is_br s1_is_jal(w) := !doing_reset && s1_resp(w).valid && !s1_req_rmeta(w).is_br s1_taken(w) := !doing_reset && (!s1_req_rmeta(w).is_br || s1_req_rmeta(w).ctr(1 )) s1_meta.ctrs(w) := s1_req_rmeta(w).ctr }for (w <- 0 until bankWidth) { io.resp.f1(w).predicted_pc := s1_resp(w) io.resp.f1(w).is_br := s1_is_br(w) io.resp.f1(w).is_jal := s1_is_jal(w) io.resp.f1(w).taken := s1_taken(w) io.resp.f2(w) := RegNext (io.resp.f1(w)) io.resp.f3(w) := RegNext (io.resp.f2(w)) } io.f3_meta := RegNext (RegNext (s1_meta.asUInt))
更新 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 val wrbypass_hits = VecInit ((0 until nWrBypassEntries) map { i => !doing_reset && wrbypass_idxs(i) === s1_update_idx(log2Ceil(nSets)-1 ,0 ) })val wrbypass_hit = wrbypass_hits.reduce(_||_)val wrbypass_hit_idx = PriorityEncoder (wrbypass_hits)val max_offset_value = (~(0. U )((offsetSz-1 ).W )).asSIntval min_offset_value = Cat (1. B , (0. U )((offsetSz-1 ).W )).asSIntval new_offset_value = (s1_update.bits.target.asSInt - (s1_update.bits.pc + (s1_update.bits.cfi_idx.bits << 1 )).asSInt)val s1_update_wbtb_data = Wire (new MicroBTBEntry ) s1_update_wbtb_data.offset := new_offset_valueval s1_update_wbtb_mask = (UIntToOH (s1_update_cfi_idx) & Fill (bankWidth, s1_update.bits.cfi_idx.valid && s1_update.valid && s1_update.bits.cfi_taken && s1_update.bits.is_commit_update))val s1_update_wmeta_mask = ((s1_update_wbtb_mask | s1_update.bits.br_mask) & Fill (bankWidth, s1_update.valid && s1_update.bits.is_commit_update))val s1_update_wmeta_data = Wire (Vec (bankWidth, new MicroBTBMeta ))for (w <- 0 until bankWidth) { s1_update_wmeta_data(w).tag := s1_update_idx >> log2Ceil(nSets) s1_update_wmeta_data(w).is_br := s1_update.bits.br_mask(w) val was_taken = (s1_update.bits.cfi_idx.valid && s1_update.bits.cfi_idx.bits === w.U && ( (s1_update.bits.cfi_is_br && s1_update.bits.cfi_taken) || s1_update.bits.cfi_is_jal ) ) val old_bim_value = Mux (wrbypass_hit, wrbypass(wrbypass_hit_idx)(w).ctr, s1_update_meta.ctrs(w)) s1_update_wmeta_data(w).ctr := bimWrite(old_bim_value, was_taken) }
写 sram 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 btb.write( Mux (doing_reset, reset_idx, s1_update_idx), Mux (doing_reset, VecInit (Seq .fill(bankWidth) { 0. U (btbEntrySz.W ) }), VecInit (Seq .fill(bankWidth) { s1_update_wbtb_data.asUInt })), Mux (doing_reset, (~(0. U (bankWidth.W ))), s1_update_wbtb_mask).asBools ) meta.write( Mux (doing_reset, reset_idx, s1_update_idx), Mux (doing_reset, VecInit (Seq .fill(bankWidth) { 0. U (btbMetaSz.W ) }), VecInit (s1_update_wmeta_data.map(_.asUInt))), Mux (doing_reset, (~(0. U (bankWidth.W ))), s1_update_wmeta_mask).asBools ) when (s1_update_wmeta_mask =/= 0. U ) { when (wrbypass_hit) { wrbypass(wrbypass_hit_idx) := s1_update_wmeta_data } .otherwise { wrbypass(wrbypass_enq_idx) := s1_update_wmeta_data wrbypass_idxs(wrbypass_enq_idx) := s1_update_idx wrbypass_enq_idx := WrapInc (wrbypass_enq_idx, nWrBypassEntries) } }
框图说明 1. 整体架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ μBTB (Micro Branch Target Buffer) │ │ params: nSets=256 │ │ │ │ 特点: 直接映射 (Direct-Mapped), 无 Way 选择, 更快的访问 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 主存储结构 │ │ │ │ │ │ │ │ ┌────────────────────────────┐ ┌────────────────────────────┐ │ │ │ │ │ ubtb_meta │ │ ubtb_data │ │ │ │ │ │ [256 sets] │ │ [256 sets] │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌──────────────────────┐ │ │ ┌──────────────────────┐ │ │ │ │ │ │ │ Set 0: │ │ │ │ Set 0: │ │ │ │ │ │ │ │ [bankWidth entries] │ │ │ │ [bankWidth entries] │ │ │ │ │ │ │ │ ┌────┬────┬──────┐ │ │ │ │ ┌──────────────┐ │ │ │ │ │ │ │ │ │ctr │is_br│ tag │ │ │ │ │ │ offset │ │ │ │ │ │ │ │ │ │(2b)│(1b) │(tag)│ │ │ │ │ │ (13 bits) │ │ │ │ │ │ │ │ │ └────┴────┴──────┘ │ │ │ │ └──────────────┘ │ │ │ │ │ │ │ │ ... │ │ │ │ ... │ │ │ │ │ │ │ ├──────────────────────┤ │ │ ├──────────────────────┤ │ │ │ │ │ │ │ Set 1 │ │ │ │ Set 1 │ │ │ │ │ │ │ ├──────────────────────┤ │ │ ├──────────────────────┤ │ │ │ │ │ │ │ ... │ │ │ │ ... │ │ │ │ │ │ │ ├──────────────────────┤ │ │ ├──────────────────────┤ │ │ │ │ │ │ │ Set 255 │ │ │ │ Set 255 │ │ │ │ │ │ │ └──────────────────────┘ │ │ └──────────────────────┘ │ │ │ │ │ └────────────────────────────┘ └────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Write Bypass Buffer │ │ │ │ [nWrBypassEntries = 2 entries] │ │ │ │ │ │ │ │ ┌──────────────────────────────────────────────────────────────────┐ │ │ │ │ │ Entry 0: idx + [bankWidth × MicroBTBMeta] │ │ │ │ │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ │ │ Entry 1: idx + [bankWidth × MicroBTBMeta] │ │ │ │ │ └──────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 用途: 解决 SRAM 写后读 (RAW) 冲突,提供最新的计数器值 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
2. 数据结构详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 ┌─────────────────────────────────────────────────────────────────┐ │ MicroBTBEntry (数据项) │ ├─────────────────────────────────────────────────────────────────┤ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ offset (13 bits) │ │ │ │ 分支目标的 PC 偏移量 (有符号) │ │ │ │ │ │ │ │ 注意: μBTB 没有 extended 位! │ │ │ │ 只能处理 ±4KB 范围内的跳转 │ │ │ │ 远距离跳转需要依赖 BTB │ │ │ └─────────────────────────────────────────────────────────┘ │ │ 共 13 bits │ └─────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────┐ │ MicroBTBMeta (元数据) │ ├─────────────────────────────────────────────────────────────────┤ │ ┌────────┬─────────┬──────────────────────────────────────┐ │ │ │ ctr │ is_br │ tag │ │ │ │ (2 bits)│ (1 bit) │ (tagSz bits) │ │ │ └────────┴─────────┴──────────────────────────────────────┘ │ │ │ │ 字段说明: │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ ctr (2-bit 饱和计数器): │ │ │ │ 00 = 强不跳转 (Strongly Not Taken) │ │ │ │ 01 = 弱不跳转 (Weakly Not Taken) │ │ │ │ 10 = 弱跳转 (Weakly Taken) │ │ │ │ 11 = 强跳转 (Strongly Taken) │ │ │ │ │ │ │ │ is_br: │ │ │ │ 0 = JAL (无条件跳转, 总是 taken) │ │ │ │ 1 = BR (条件分支, 根据 ctr 预测) │ │ │ │ │ │ │ │ tag: 地址高位, 用于验证是否命中 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ 共 (tagSz + 3) bits │ └─────────────────────────────────────────────────────────────────┘
3. 地址索引方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 PC 地址结构 ┌─────────────────────────────────────────────────────────────────┐ │ vaddrBitsExtended │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌───────────────────┬──────────────┬──────────────────┬───┐ │ │ │ tag │ idx │ fetchWidth偏移 │ 0 │ │ │ │ (tagSz) │ log2(nSets) │ log2(fetchWidth)│ │ │ │ │ │ = 8 bits │ │ │ │ │ └───────────────────┴──────────────┴──────────────────┴───┘ │ │ │ │ 与 BTB 的区别: │ │ - μBTB: nSets=256, idx=8 bits │ │ - BTB: nSets=128, idx=7 bits │ │ - μBTB 更多 set,但没有 way (直接映射) │ │ │ └─────────────────────────────────────────────────────────────────┘ --- 查询流程 (Lookup) 查询流程图 │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Stage 0 (S0): 发起读请求 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 输入: s0_idx (从 PC 提取的索引) │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 并行读取 Meta 和 Data (直接映射,无需选择 Way): │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ │ │ meta.read │ │ btb.read │ │ │ │ │ │ (s0_idx) │ │ (s0_idx) │ │ │ │ │ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ Vec[bankWidth] Vec[bankWidth] │ │ │ │ MicroBTBMeta MicroBTBEntry │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Stage 1 (S1): Tag 比较 & 预测生成 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 输入: s1_req_rmeta, s1_req_rbtb (读出的数据) │ │ s1_req_tag (从 s1_idx 提取的 tag) │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 对每个 bank slot (w = 0 to bankWidth-1): │ │ │ │ │ │ │ │ 1. Tag 比较 (判断命中): │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ hit = (s1_req_tag == s1_req_rmeta(w).tag) │ │ │ │ │ │ │ │ │ │ │ │ s1_resp(w).valid = !doing_reset && │ │ │ │ │ │ s1_valid && hit │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 2. 计算目标地址 (PC 相对): │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ target = s1_pc + (w << 1) + offset │ │ │ │ │ │ ↑ ↑ ↑ │ │ │ │ │ │ 基地址 slot偏移 分支偏移 │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 3. 生成分支类型: │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ s1_is_br(w) = valid && is_br │ │ │ │ │ │ s1_is_jal(w) = valid && !is_br │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 4. 预测是否 Taken (使用 2-bit 计数器): │ │ │ │ ┌─────────────────────────────────────────────────┐ │ │ │ │ │ s1_taken(w) = !is_br || ctr[1] │ │ │ │ │ │ ↑ ↑ │ │ │ │ │ │ JAL总是taken BR看ctr最高位 │ │ │ │ │ │ │ │ │ │ │ │ ctr[1]=0 → Not Taken (00, 01) │ │ │ │ │ │ ctr[1]=1 → Taken (10, 11) │ │ │ │ │ └─────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 输出响应 (F1/F2/F3) │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ F1 (S1 周期,最快响应!): │ │ │ │ io.resp.f1(w).predicted_pc = s1_resp(w) │ │ │ │ io.resp.f1(w).is_br = s1_is_br(w) │ │ │ │ io.resp.f1(w).is_jal = s1_is_jal(w) │ │ │ │ io.resp.f1(w).taken = s1_taken(w) │ │ │ │ │ │ │ │ F2 (S2 周期): │ │ │ │ io.resp.f2(w) = RegNext(io.resp.f1(w)) │ │ │ │ │ │ │ │ F3 (S3 周期): │ │ │ │ io.resp.f3(w) = RegNext(io.resp.f2(w)) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ 关键区别: μBTB 在 F1 就能给出预测结果! │ │ BTB 需要到 F2 才能给出结果 │ │ │ └─────────────────────────────────────────────────────────────────┘ --- 更新流程 (Update) 更新流程图 │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 接收更新请求 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 输入: s1_update (来自后端的更新信息) │ │ - s1_update.bits.pc : 分支指令 PC │ │ - s1_update.bits.target : 实际跳转目标 │ │ - s1_update.bits.cfi_idx : 分支在 fetch block 中的位置 │ │ - s1_update.bits.cfi_taken : 分支是否 taken │ │ - s1_update.bits.br_mask : 哪些位置是分支指令 │ │ - s1_update.bits.cfi_is_br : CFI 是否是条件分支 │ │ - s1_update.bits.cfi_is_jal : CFI 是否是 JAL │ │ - s1_update.bits.meta.ctrs : 预测时的计数器值 │ │ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Write Bypass 检查 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 检查 Write Bypass Buffer 是否有更新的值: │ │ │ │ │ │ │ │ for i in 0..nWrBypassEntries: │ │ │ │ wrbypass_hits(i) = (wrbypass_idxs(i) == update_idx) │ │ │ │ │ │ │ │ wrbypass_hit = wrbypass_hits.reduce(OR) │ │ │ │ wrbypass_hit_idx = PriorityEncoder(wrbypass_hits) │ │ │ │ │ │ │ │ 用途: SRAM 有 1 周期延迟,bypass 提供最新写入的值 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 计算更新数据 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ BTB Data 更新: │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ new_offset = target - (pc + cfi_idx << 1) │ │ │ │ │ │ │ │ s1_update_wbtb_data.offset = new_offset │ │ │ │ │ │ │ │ 注意: μBTB 不检查 offset 是否超出范围! │ │ │ │ 远跳转会被截断,依赖 BTB 处理 │ │ │ │ │ │ │ │ 写入掩码: │ │ │ │ wbtb_mask = OH(cfi_idx) & cfi_valid & │ │ │ │ cfi_taken & is_commit_update │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ Meta 更新 (包含计数器更新): │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ for each bank slot w: │ │ │ │ │ │ │ │ wmeta_data(w).tag = update_idx >> log2(nSets) │ │ │ │ wmeta_data(w).is_br = br_mask(w) │ │ │ │ │ │ │ │ // 判断该 slot 是否被 taken │ │ │ │ was_taken = cfi_idx == w && │ │ │ │ ((cfi_is_br && cfi_taken) || cfi_is_jal) │ │ │ │ │ │ │ │ // 获取旧的计数器值 (优先使用 bypass) │ │ │ │ old_ctr = wrbypass_hit ? │ │ │ │ wrbypass(hit_idx)(w).ctr : │ │ │ │ update_meta.ctrs(w) │ │ │ │ │ │ │ │ // 2-bit 饱和计数器更新 │ │ │ │ wmeta_data(w).ctr = bimWrite(old_ctr, was_taken) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 2-bit 饱和计数器更新逻辑 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ bimWrite 函数 │ │ │ │ │ │ │ │ Taken │ │ │ │ ┌─────────┐ │ │ │ │ │ │ │ │ │ │ ┌─────────▼─────────┐ │ │ │ │ ┌────┤ 00 (强NT) │◄───┐ │ │ │ │ │ └─────────┬─────────┘ │ │ │ │ │ │ │ Taken │ Not Taken │ │ │ │ │ Not Taken ▼ │ │ │ │ │ │ ┌─────────────────────┐ │ │ │ │ │ │ │ 01 (弱NT) │──┘ │ │ │ │ │ └─────────┬───────────┘ │ │ │ │ │ │ Taken │ │ │ │ │ ▼ │ │ │ │ │ ┌─────────────────────┐ │ │ │ │ │ │ 10 (弱T) │──┐ │ │ │ │ │ └─────────┬───────────┘ │ Not Taken │ │ │ │ │ │ Taken │ │ │ │ │ │ ▼ │ │ │ │ │ │ ┌─────────────────────┐ │ │ │ │ │ └───►│ 11 (强T) │◄─┘ │ │ │ │ └─────────┬───────────┘ │ │ │ │ │ │ │ │ │ └─────────┐ │ │ │ │ Taken │ │ │ │ │ (保持饱和) │ │ │ │ │ │ │ │ 代码逻辑: │ │ │ │ if (v==3 && taken) → 3 (饱和) │ │ │ │ if (v==0 && !taken) → 0 (饱和) │ │ │ │ if (taken) → v + 1 │ │ │ │ else → v - 1 │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 执行写入操作 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 1. 写入 SRAM (直接映射,无需选择 Way): │ │ │ │ │ │ │ │ btb.write( │ │ │ │ addr = doing_reset ? reset_idx : s1_update_idx, │ │ │ │ data = [wbtb_data × bankWidth], │ │ │ │ mask = wbtb_mask │ │ │ │ ) │ │ │ │ │ │ │ │ meta.write( │ │ │ │ addr = doing_reset ? reset_idx : s1_update_idx, │ │ │ │ data = wmeta_data, │ │ │ │ mask = wmeta_mask │ │ │ │ ) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 2. 更新 Write Bypass Buffer: │ │ │ │ │ │ │ │ if (wmeta_mask != 0): │ │ │ │ if (wrbypass_hit): │ │ │ │ // 更新已有条目 │ │ │ │ wrbypass(hit_idx) := wmeta_data │ │ │ │ else: │ │ │ │ // 分配新条目 (FIFO) │ │ │ │ wrbypass(enq_idx) := wmeta_data │ │ │ │ wrbypass_idxs(enq_idx) := update_idx │ │ │ │ enq_idx := WrapInc(enq_idx, 2) │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ --- μBTB vs BTB 对比 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ μBTB vs BTB 对比 │ ├────────────────────┬────────────────────────┬───────────────────────────────────┤ │ 特性 │ μBTB │ BTB │ ├────────────────────┼────────────────────────┼───────────────────────────────────┤ │ 组织结构 │ 直接映射 (Direct) │ 2-way 组相联 │ │ │ nSets=256 │ nSets=128, nWays=2 │ ├────────────────────┼────────────────────────┼───────────────────────────────────┤ │ 响应延迟 │ F1 (最快!) │ F2 │ ├────────────────────┼────────────────────────┼───────────────────────────────────┤ │ 目标地址存储 │ 仅 offset (13-bit) │ offset + eBTB (完整地址) │ │ │ 范围: ±4KB │ 支持任意远距离跳转 │ ├────────────────────┼────────────────────────┼───────────────────────────────────┤ │ 分支预测 │ 内置 2-bit 饱和计数器 │ 无 (仅提供目标地址) │ │ │ 可直接预测 taken/NT │ 需要配合其他预测器 │ ├────────────────────┼────────────────────────┼───────────────────────────────────┤ │ Write Bypass │ 有 (2 entries) │ 无 │ │ │ 解决 RAW 冲突 │ │ ├────────────────────┼────────────────────────┼───────────────────────────────────┤ │ 存储开销 │ 较小 │ 较大 (多 Way + eBTB) │ ├────────────────────┼────────────────────────┼───────────────────────────────────┤ │ 冲突处理 │ 直接覆盖 (容易冲突) │ 多 Way 减少冲突 │ ├────────────────────┼────────────────────────┼───────────────────────────────────┤ │ 用途 │ 快速首次预测 │ 精确预测, 远距离跳转 │ │ │ 第一级预测器 │ 第二级预测器 │ └────────────────────┴────────────────────────┴───────────────────────────────────┘ --- 关键设计要点总结 ┌─────────────────────────────────────────────────────────────────┐ │ μBTB 设计要点总结 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. 极速响应 - F1 输出: │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ • μBTB 专门优化为低延迟 │ │ │ │ • 在 F1 阶段就能给出预测,比 BTB 快 1 个周期 │ │ │ │ • 适合作为前端流水线的第一级预测器 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ 2. 直接映射 - 无 Way 选择: │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ • 省去 Way 选择逻辑,减少延迟 │ │ │ │ • 代价是更高的冲突率 │ │ │ │ • 通过更多 Sets (256 vs 128) 部分缓解 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ 3. 内置 2-bit 计数器: │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ • 可以直接进行 taken/not-taken 预测 │ │ │ │ • BTB 不包含计数器,需要配合 BIM 等预测器 │ │ │ │ • 简化预测流程,减少组件间依赖 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ 4. Write Bypass Buffer: │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ • 2 个条目的小型缓冲区 │ │ │ │ • 解决 SRAM 写后读 (RAW) 延迟问题 │ │ │ │ • 保证计数器更新的正确性 │ │ │ │ • FIFO 替换策略 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ 5. 无远距离跳转支持: │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ • 只有 13-bit offset,范围 ±4KB │ │ │ │ • 没有 eBTB 扩展 │ │ │ │ • 远距离跳转依赖 BTB 处理 │ │ │ │ • 保持 μBTB 结构简单、低延迟 │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ │ 6. 流水线集成: │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ S0: SRAM 读取 │ │ │ │ S1/F1: Tag 比较 + 预测输出 (μBTB 主输出) │ │ │ │ F2: RegNext 传递 (BTB 主输出) │ │ │ │ F3: RegNext 传递 (最终确认) │ │ │ └──────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘
update 的过程 1. 整体时序概览 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ μBTB 更新的完整生命周期 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 时间线: │ │ │ │ ══════════════════════════════════════════════════════════════════════════ │ │ │ 预测阶段 │ 执行阶段 │ 提交阶段 │ 更新阶段 │ │ │ ══════════════════════════════════════════════════════════════════════════ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────┐ ┌─────────┐ ┌──────────┐ │ │ │ F1预测 │ ─────────────────────► │ 分支执行 │───►│ 提交更新 │ │ │ │ 保存meta│ │ 得到结果 │ │ s1_update│ │ │ └─────────┘ └─────────┘ └──────────┘ │ │ │ │ │ │ │ ctr 值随 meta 一路传递 │ │ │ └───────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ 预测阶段 (S0 → S1 → F3) │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ S0 周期: 发起 SRAM 读取 │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ PC = 0x1000 │ │ │ │ s0_idx = PC[11:4] = 0x00 (假设) │ │ │ │ │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ meta SRAM │ │ btb SRAM │ │ │ │ │ │ read(0x00) │ │ read(0x00) │ │ │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ (下一周期可用) (下一周期可用) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ S1 周期: 读取完成,生成预测,保存 meta │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ SRAM 返回数据: │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ │ │ s1_req_rmeta[bankWidth]: │ │ │ │ │ │ slot 0: {tag=0xABC, is_br=1, ctr=10} ← 条件分支,弱跳转 │ │ │ │ │ │ slot 1: {tag=0xDEF, is_br=0, ctr=11} ← JAL │ │ │ │ │ │ slot 2: {tag=0x000, is_br=0, ctr=00} ← 无效 │ │ │ │ │ │ slot 3: {tag=0x123, is_br=1, ctr=01} ← 条件分支,弱不跳转 │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 生成预测 & 保存 meta: │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ │ │ s1_meta.ctrs[0] = 10 ← 保存当前 ctr 值! │ │ │ │ │ │ s1_meta.ctrs[1] = 11 │ │ │ │ │ │ s1_meta.ctrs[2] = 00 │ │ │ │ │ │ s1_meta.ctrs[3] = 01 │ │ │ │ │ │ │ │ │ │ │ │ 这些 ctr 值会随预测结果一起传递给后端! │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ F3 周期: meta 输出到流水线 │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ io.f3_meta := RegNext(RegNext(s1_meta.asUInt)) │ │ │ │ │ │ │ │ meta (包含 ctrs) 随预测结果一起发送给后端 │ │ │ │ 后端会在分支执行/提交时把这个 meta 带回来 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
3. 更新请求到达 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ 更新请求结构 (s1_update) │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 经过 N 个周期后,分支执行完毕,提交时发送更新请求: │ │ │ │ s1_update.valid = true │ │ s1_update.bits: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ 基本信息: │ │ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ │ │ pc = 0x1000 (分支指令所在 fetch block) │ │ │ │ │ │ target = 0x1200 (实际跳转目标) │ │ │ │ │ │ cfi_idx.valid = true (有控制流指令) │ │ │ │ │ │ cfi_idx.bits = 0 (在 slot 0) │ │ │ │ │ │ cfi_taken = true (实际跳转了) │ │ │ │ │ │ cfi_is_br = true (是条件分支) │ │ │ │ │ │ cfi_is_jal = false (不是 JAL) │ │ │ │ │ │ is_commit_update= true (提交时更新) │ │ │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 分支掩码: │ │ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ │ │ br_mask = 0b1001 (slot 0 和 slot 3 是分支指令) │ │ │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 预测时保存的 meta (从 F3 带回来的): │ │ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ │ │ meta.ctrs[0] = 10 ← 预测时 slot 0 的 ctr 值 │ │ │ │ │ │ meta.ctrs[1] = 11 │ │ │ │ │ │ meta.ctrs[2] = 00 │ │ │ │ │ │ meta.ctrs[3] = 01 │ │ │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
4. Write Bypass 检查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Step 1: Write Bypass 检查 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ s1_update_idx = 0x00 (从 update.pc 提取) │ │ │ │ 检查 bypass buffer: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ Write Bypass Buffer 当前状态: │ │ │ │ ┌─────────┬──────────────────────────────────────────────────────┐ │ │ │ │ │ Entry 0 │ idx=0x05, meta=[ctr=11, ctr=00, ctr=10, ctr=01] │ │ │ │ │ ├─────────┼──────────────────────────────────────────────────────┤ │ │ │ │ │ Entry 1 │ idx=0x10, meta=[ctr=00, ctr=11, ctr=01, ctr=10] │ │ │ │ │ └─────────┴──────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ 比较: │ │ │ │ wrbypass_hits[0] = (0x05 == 0x00) = false │ │ │ │ wrbypass_hits[1] = (0x10 == 0x00) = false │ │ │ │ │ │ │ │ wrbypass_hit = false ← 没有命中! │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 结论: 使用 s1_update_meta.ctrs (预测时保存的值) │ │ │ │ ═══════════════════════════════════════════════════════════════════════════ │ │ │ │ 另一种情况 - Bypass 命中: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ 假设 bypass buffer 状态: │ │ │ │ ┌─────────┬──────────────────────────────────────────────────────┐ │ │ │ │ │ Entry 0 │ idx=0x00, meta=[ctr=11, ...] ← 刚刚更新过! │ │ │ │ │ └─────────┴──────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ wrbypass_hits[0] = (0x00 == 0x00) = true ✓ │ │ │ │ wrbypass_hit = true │ │ │ │ wrbypass_hit_idx = 0 │ │ │ │ │ │ │ │ 使用 wrbypass[0].ctr = 11 (bypass 中的最新值) │ │ │ │ 而不是 s1_update_meta.ctrs = 10 (过时的值) │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
5. 计算更新数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Step 2: 计算 BTB Data (offset) │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 计算 offset: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ 分支指令 PC = pc + (cfi_idx << 1) │ │ │ │ = 0x1000 + (0 << 1) │ │ │ │ = 0x1000 │ │ │ │ │ │ │ │ new_offset = target - 分支指令PC │ │ │ │ = 0x1200 - 0x1000 │ │ │ │ = 0x200 (512) │ │ │ │ │ │ │ │ s1_update_wbtb_data.offset = 0x200 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 计算 wbtb_mask (哪些 slot 需要写入 BTB data): │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ 条件: cfi_idx.valid && cfi_taken && is_commit_update │ │ │ │ true && true && true = true │ │ │ │ │ │ │ │ s1_update_wbtb_mask = UIntToOH(cfi_idx) & Fill(bankWidth, true) │ │ │ │ = UIntToOH(0) & 0b1111 │ │ │ │ = 0b0001 & 0b1111 │ │ │ │ = 0b0001 │ │ │ │ ││││ │ │ │ │ │││└─ slot 0: 写入 ✓ (taken 的分支) │ │ │ │ ││└── slot 1: 不写 │ │ │ │ │└─── slot 2: 不写 │ │ │ │ └──── slot 3: 不写 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Step 3: 计算 Meta Data │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 计算 wmeta_mask (哪些 slot 需要写入 meta): │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ s1_update_wmeta_mask = (wbtb_mask | br_mask) & is_commit_update │ │ │ │ = (0b0001 | 0b1001) & 0b1111 │ │ │ │ = 0b1001 & 0b1111 │ │ │ │ = 0b1001 │ │ │ │ ││││ │ │ │ │ │││└─ slot 0: 写入 ✓ (是分支) │ │ │ │ ││└── slot 1: 不写 │ │ │ │ │└─── slot 2: 不写 │ │ │ │ └──── slot 3: 写入 ✓ (是分支) │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 对每个 slot 计算 wmeta_data: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ │ │ Slot 0 的计算 │ │ │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ │ │ │ │ tag = s1_update_idx >> log2(nSets) = 更新地址的高位 │ │ │ │ │ │ is_br = br_mask[0] = 1 (是条件分支) │ │ │ │ │ │ │ │ │ │ │ │ was_taken 判断: │ │ │ │ │ │ cfi_idx.valid = true │ │ │ │ │ │ cfi_idx.bits == 0 = true (就是 slot 0) │ │ │ │ │ │ cfi_is_br && cfi_taken = true && true = true │ │ │ │ │ │ → was_taken = true │ │ │ │ │ │ │ │ │ │ │ │ 获取旧 ctr: │ │ │ │ │ │ wrbypass_hit = false │ │ │ │ │ │ → old_ctr = s1_update_meta.ctrs[0] = 10 (二进制) │ │ │ │ │ │ │ │ │ │ │ │ 更新 ctr (bimWrite): │ │ │ │ │ │ old_ctr = 10 (弱跳转), was_taken = true │ │ │ │ │ │ → new_ctr = 10 + 1 = 11 (强跳转) │ │ │ │ │ │ │ │ │ │ │ │ wmeta_data[0] = {tag=..., is_br=1, ctr=11} │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ │ │ Slot 3 的计算 │ │ │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ │ │ │ │ │ │ │ │ tag = 更新地址的高位 │ │ │ │ │ │ is_br = br_mask[3] = 1 (是条件分支) │ │ │ │ │ │ │ │ │ │ │ │ was_taken 判断: │ │ │ │ │ │ cfi_idx.bits == 3 = false (CFI 在 slot 0,不是 slot 3) │ │ │ │ │ │ → was_taken = false │ │ │ │ │ │ │ │ │ │ │ │ 获取旧 ctr: │ │ │ │ │ │ old_ctr = s1_update_meta.ctrs[3] = 01 (弱不跳转) │ │ │ │ │ │ │ │ │ │ │ │ 更新 ctr (bimWrite): │ │ │ │ │ │ old_ctr = 01, was_taken = false │ │ │ │ │ │ → new_ctr = 01 - 1 = 00 (强不跳转) │ │ │ │ │ │ │ │ │ │ │ │ wmeta_data[3] = {tag=..., is_br=1, ctr=00} │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
6. 执行 SRAM 写入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Step 4: 写入 SRAM │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ BTB Data 写入: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ btb.write( │ │ │ │ addr = 0x00, │ │ │ │ data = [0x200, 0x200, 0x200, 0x200], ← 所有 slot 用同一个 offset │ │ │ │ mask = [1, 0, 0, 0] ← 只写 slot 0 │ │ │ │ ) │ │ │ │ │ │ │ │ SRAM 操作: │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ │ │ Set 0x00: │ │ │ │ │ │ slot 0: offset = 0x200 ← 写入新值 │ │ │ │ │ │ slot 1: (保持不变) │ │ │ │ │ │ slot 2: (保持不变) │ │ │ │ │ │ slot 3: (保持不变) │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ Meta 写入: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ meta.write( │ │ │ │ addr = 0x00, │ │ │ │ data = [wmeta_data[0], wmeta_data[1], wmeta_data[2], wmeta_data[3]],│ │ │ │ mask = [1, 0, 0, 1] ← 写 slot 0 和 slot 3 │ │ │ │ ) │ │ │ │ │ │ │ │ SRAM 操作: │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ │ │ Set 0x00: │ │ │ │ │ │ slot 0: {tag, is_br=1, ctr=11} ← 写入,ctr: 10→11 │ │ │ │ │ │ slot 1: (保持不变) │ │ │ │ │ │ slot 2: (保持不变) │ │ │ │ │ │ slot 3: {tag, is_br=1, ctr=00} ← 写入,ctr: 01→00 │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
7. 更新 Write Bypass Buffer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Step 5: 更新 Write Bypass │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 条件: wmeta_mask != 0 (有 meta 被写入) │ │ 0b1001 != 0 → true │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ wrbypass_hit = false,所以分配新条目: │ │ │ │ │ │ │ │ 之前 bypass buffer: │ │ │ │ ┌─────────┬──────────────────────────────────────────────────────┐ │ │ │ │ │ Entry 0 │ idx=0x05, meta=[...] │ │ │ │ │ ├─────────┼──────────────────────────────────────────────────────┤ │ │ │ │ │ Entry 1 │ idx=0x10, meta=[...] │ │ │ │ │ └─────────┴──────────────────────────────────────────────────────┘ │ │ │ │ wrbypass_enq_idx = 0 │ │ │ │ │ │ │ │ 执行写入: │ │ │ │ wrbypass[0] := wmeta_data │ │ │ │ wrbypass_idxs[0] := 0x00 │ │ │ │ wrbypass_enq_idx := WrapInc(0, 2) = 1 │ │ │ │ │ │ │ │ 之后 bypass buffer: │ │ │ │ ┌─────────┬──────────────────────────────────────────────────────┐ │ │ │ │ │ Entry 0 │ idx=0x00, meta=[ctr=11, -, -, ctr=00] ← 新写入! │ │ │ │ │ ├─────────┼──────────────────────────────────────────────────────┤ │ │ │ │ │ Entry 1 │ idx=0x10, meta=[...] (保持) │ │ │ │ │ └─────────┴──────────────────────────────────────────────────────┘ │ │ │ │ wrbypass_enq_idx = 1 ← 下次写 entry 1 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
8. 完整流程总结图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ μBTB 更新完整流程 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ s1_update 到达 │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 1. 提取 update_idx │ │ │ │ s1_update_idx = update.pc 的索引部分 │ │ │ └───────────────────────────────┬─────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 2. 检查 Write Bypass │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ for each bypass entry: │ │ │ │ │ │ hit = (bypass_idx == update_idx) │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────┬─────────────────────────────────────────┘ │ │ │ │ │ ┌──────────────┴──────────────┐ │ │ ▼ ▼ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Bypass Hit │ │ Bypass Miss │ │ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌───────────────────────────┐ ┌───────────────────────────┐ │ │ │ old_ctr = bypass[hit].ctr │ │ old_ctr = update_meta.ctr │ │ │ │ (最新值) │ │ (预测时保存的值) │ │ │ └─────────────┬─────────────┘ └─────────────┬─────────────┘ │ │ │ │ │ │ └──────────────┬───────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 3. 计算新值 │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ new_offset = target - (pc + cfi_idx << 1) │ │ │ │ │ │ new_ctr = bimWrite(old_ctr, was_taken) │ │ │ │ │ │ new_tag = update_idx >> log2(nSets) │ │ │ │ │ │ new_is_br = br_mask[slot] │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────┬─────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 4. 计算写入掩码 │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ wbtb_mask = OH(cfi_idx) & cfi_valid & taken & commit │ │ │ │ │ │ wmeta_mask = (wbtb_mask | br_mask) & commit │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────┬─────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 5. 写入 SRAM │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ btb.write(addr=idx, data=offset, mask=wbtb_mask) │ │ │ │ │ │ meta.write(addr=idx, data=wmeta, mask=wmeta_mask) │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────┬─────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ 6. 更新 Write Bypass Buffer │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ if (wmeta_mask != 0): │ │ │ │ │ │ if (bypass_hit): │ │ │ │ │ │ bypass[hit_idx] := wmeta_data // 更新已有条目 │ │ │ │ │ │ else: │ │ │ │ │ │ bypass[enq_idx] := wmeta_data // 分配新条目 │ │ │ │ │ │ bypass_idxs[enq_idx] := idx │ │ │ │ │ │ enq_idx := (enq_idx + 1) % 2 // FIFO │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 更新完成 ✓ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
9. Bypass 解决的实际问题示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Bypass 解决 RAW 冲突的实际例子 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 场景: 循环中的分支,连续两次提交更新 │ │ │ │ for (int i = 0; i < 100; i++) { // 分支在 0x1000 │ │ ... │ │ } │ │ │ │ ═══════════════════════════════════════════════════════════════════════════ │ │ │ │ Cycle N: 第一次迭代提交,分支 taken │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ update arrives: idx=0x10, ctr from meta = 10 │ │ │ │ bypass check: miss │ │ │ │ old_ctr = 10 (from meta) │ │ │ │ new_ctr = 10 + 1 = 11 │ │ │ │ SRAM write: ctr = 11 │ │ │ │ bypass write: entry[0] = {idx=0x10, ctr=11} │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ Cycle N+1: 第二次迭代提交,分支又 taken │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ update arrives: idx=0x10, ctr from meta = 10 ← 还是旧值! │ │ │ │ (因为 meta 是预测时保存的,那时 SRAM 里是 10) │ │ │ │ │ │ │ │ bypass check: HIT! entry[0].idx == 0x10 │ │ │ │ old_ctr = 11 (from bypass, 最新值!) │ │ │ │ new_ctr = 11 + 1 = ... 饱和到 11 │ │ │ │ SRAM write: ctr = 11 │ │ │ │ bypass update: entry[0] = {idx=0x10, ctr=11} │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 如果没有 bypass: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ Cycle N+1: │ │ │ │ old_ctr = 10 (from meta, 过时!) │ │ │ │ new_ctr = 10 + 1 = 11 │ │ │ │ SRAM write: ctr = 11 │ │ │ │ │ │ │ │ 两次 taken,结果都是 ctr=11,丢失了一次更新! │ │ │ │ 正确应该: taken 两次后 ctr 应该保持 11 (饱和) │ │ │ │ 但如果初始是 01,两次 taken 应该是 01→10→11 │ │ │ │ 没有 bypass 会变成 01→10, 01→10 (错误!) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
总结
阶段
操作
数据来源
预测
读 SRAM,保存 ctr 到 meta
SRAM
执行
meta 随预测结果传递到后端
流水线寄存器
提交
发送 update,带回 meta
后端
更新
检查 bypass,获取最新 ctr
bypass > meta
更新
计算新 ctr,写 SRAM
bimWrite
更新
写 bypass buffer
wmeta_data
Write Bypass 的本质: 一个 2 条目的小缓存,记录最近写入 SRAM 的数据,用于解决 SRAM 写延迟导致的 RAW 冲突,保证计数器更新的正确性。
mask 这是 Chisel SyncReadMem 的 带掩码写入 (Masked Write) 功能,允许只更新一个 Set 中的部分 slot,而不是全部覆盖。
1 2 3 4 5 6 SyncReadMem .write 的签名def write (addr: UInt , data: Vec [T ], mask: Seq [Bool ]): Unit
图解 Mask 写入机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Masked Write 工作原理 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 假设 bankWidth = 4,写入 addr = 0x10 │ │ │ │ 写入参数: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ addr = 0x10 │ │ │ │ data = [0x200, 0x200, 0x200, 0x200] ← 4 个 slot 的数据 │ │ │ │ mask = [true, false, false, true] ← 0b1001 │ │ │ │ ───── ───── ───── ──── │ │ │ │ slot0 slot1 slot2 slot3 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ SRAM Set 0x10 写入前: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ slot 0: 0x100 (旧值) │ │ │ │ slot 1: 0x050 (旧值) │ │ │ │ slot 2: 0x080 (旧值) │ │ │ │ slot 3: 0x0A0 (旧值) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ Mask 作用: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ slot 0: mask[0]=true → 写入 data[0]=0x200 ✓ │ │ │ │ slot 1: mask[1]=false → 保持旧值 0x050 ✗ │ │ │ │ slot 2: mask[2]=false → 保持旧值 0x080 ✗ │ │ │ │ slot 3: mask[3]=true → 写入 data[3]=0x200 ✓ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ SRAM Set 0x10 写入后: │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ slot 0: 0x200 ← 新值 │ │ │ │ slot 1: 0x050 ← 保持不变 │ │ │ │ slot 2: 0x080 ← 保持不变 │ │ │ │ slot 3: 0x200 ← 新值 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
代码中的两种情况 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Reset vs Normal Update │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 情况 1: doing_reset = true (复位阶段) │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ addr = reset_idx (0, 1, 2, ... nSets-1 依次清零) │ │ │ │ data = [0, 0, 0, 0] │ │ │ │ mask = ~0 = 0b1111 = [true, true, true, true] │ │ │ │ │ │ │ │ 效果: 整个 Set 的所有 slot 全部清零 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 情况 2: doing_reset = false (正常更新) │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ addr = s1_update_idx │ │ │ │ data = [wbtb_data, wbtb_data, wbtb_data, wbtb_data] │ │ │ │ mask = s1_update_wbtb_mask (例如 0b0001) │ │ │ │ │ │ │ │ 效果: 只更新 mask 为 1 的 slot,其他保持不变 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
为什么需要 Mask? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ Mask 的必要性 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 一个 Fetch Block 可能包含多个分支,但只有部分需要更新: │ │ │ │ ┌─────────┬─────────┬─────────┬─────────┐ │ │ │ slot 0 │ slot 1 │ slot 2 │ slot 3 │ │ │ │ BR (T) │ ADD │ SUB │ BR (NT) │ │ │ │ taken! │ 非分支 │ 非分支 │ 未执行 │ │ │ └─────────┴─────────┴─────────┴─────────┘ │ │ │ │ 这次更新: │ │ - slot 0 的分支 taken 了,需要更新 offset │ │ - slot 1, 2 不是分支,不更新 │ │ - slot 3 的分支还没执行到 (被 slot 0 跳走了),不更新 │ │ │ │ wbtb_mask = 0b0001 → 只写 slot 0 │ │ │ │ 如果没有 mask,强制写入所有 slot: │ │ - slot 1, 2, 3 的有效数据会被错误覆盖! │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
硬件实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ┌─────────────────────────────────────────────────────────────────────────────────┐ │ SRAM Mask 硬件实现 │ ├─────────────────────────────────────────────────────────────────────────────────┤ │ │ │ 每个 slot 有独立的写使能信号: │ │ │ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ addr │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ ┌─────────────┐ │ │ │ │ │ 地址译码器 │ │ │ │ │ └──────┬──────┘ │ │ │ │ │ word_select │ │ │ │ ▼ │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ │ │ SRAM Array │ │ │ │ │ │ ┌─────────┬─────────┬─────────┬─────────┐ │ │ │ │ │ │ │ slot 0 │ slot 1 │ slot 2 │ slot 3 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ WE[0] │ WE[1] │ WE[2] │ WE[3] │ ← 独立写使能│ │ │ │ │ │ │ ↑ │ ↑ │ ↑ │ ↑ │ │ │ │ │ │ │ └────┼────┴────┼────┴────┼────┴────┼────┘ │ │ │ │ │ └───────┼─────────┼─────────┼─────────┼──────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ mask[0] mask[1] mask[2] mask[3] │ │ │ │ (true) (false) (false) (true) │ │ │ │ │ │ │ │ │ │ │ │ WE[0]=1 WE[1]=0 WE[2]=0 WE[3]=1 │ │ │ │ 写入! 不写 不写 写入! │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────────┘
总结
概念
说明
mask
布尔向量,指定哪些 slot 真正写入
mask[i]=true
slot i 写入 data[i]
mask[i]=false
slot i 保持原值不变
Reset 时
mask 全 1,清零整个 Set
Update 时
mask 只在需要更新的 slot 为 1
硬件实现
每个 slot 有独立的写使能 (Write Enable)
这就是 字节/字掩码写入 (Byte/Word Mask Write),是 SRAM 的标准功能,Chisel 的 SyncReadMem 直接支持这个特性。