BOOMv3のフロントエンド、特に分岐予測の部分についてちょっと実装を確認したくなった。 以下ドキュメントとソースコードを読んだ時のメモ。
- F0
- PCの生成、命令キャッシュに投げる。
- TLBにも投げる?
- BTBに投げる
- F1
- BTBの参照
- BIMの参照
- RASの参照:早すぎない?
- F2: だいたいここでいろんなもののレスポンスが返ってくる
- IMEM Response QueueとBTB Response Queueに貯められる
- F3
- BTB Response Queueの先頭からパケットを取り出してBR Checkerにかける
- 同じタイミングでIMEMから読みだしてBR DecodeとBR Checkerにかける?
- その結果をBTBに返してアップデートする?
- 結果はそれぞれFetch BufferとFetch Target Queueに返される
興味事項
- BRはどういったことを解析する?
- RASの解析タイミングは?
ソースコード中のf3
がIMEM Response Queueだと思われる。
val f3 = withReset(reset.asBool || f3_clear) { Module(new Queue(new FrontendResp, 1, pipe=true, flow=false)) }
f3
の入力条件。s2で命令キャッシュから正しくフェッチされた場合。
f3.io.enq.valid := (s2_valid && !f2_clear &&
(icache.io.resp.valid || ((s2_tlb_resp.ae.inst || s2_tlb_resp.pf.inst) && !s2_tlb_miss))
)
f3.io.enq.bits.pc := s2_vpc
f3.io.enq.bits.data := Mux(s2_xcpt, 0.U, icache.io.resp.bits.data)
f3.io.enq.bits.ghist := s2_ghist
f3.io.enq.bits.mask := fetchMask(s2_vpc)
f3.io.enq.bits.xcpt := s2_tlb_resp
f3.io.enq.bits.fsrc := s2_fsrc
f3.io.enq.bits.tsrc := s2_tsrc
f3_bpd_resp
がBTB Response Queueだと思われる。まあこれQueueって書いてあるけどエントリ1本なので単なるパイプだな。
// Queue up the bpd resp as well, incase f4 backpressures f3 // This is "flow" because the response (enq) arrives in f3, not f2 val f3_bpd_resp = withReset(reset.asBool || f3_clear) { Module(new Queue(new BranchPredictionBundle, 1, pipe=true, flow=true)) }
IMEM Response Queueに呼応してf3_bpd_resp
に入れる。タイミングを合わせているみたいだ。
// The BPD resp comes in f3
f3_bpd_resp.io.enq.valid := f3.io.deq.valid && RegNext(f3.io.enq.ready)
f3_bpd_resp.io.enq.bits := bpd.io.resp.f3
BranchDecodeについて。命令キャッシュから来たものを突っ込むようだ。
BranchDecodeの1ライン目は最初のHalfを突っ込むっポイ。これは考え方は一緒。
if (w == 0) { val inst0 = Cat(bank_data(15,0), f3_prev_half) val inst1 = bank_data(31,0) val exp_inst0 = ExpandRVC(inst0) val exp_inst1 = ExpandRVC(inst1) val pc0 = (f3_aligned_pc + (i << log2Ceil(coreInstBytes)).U - 2.U) val pc1 = (f3_aligned_pc + (i << log2Ceil(coreInstBytes)).U) val bpd_decoder0 = Module(new BranchDecode) bpd_decoder0.io.inst := exp_inst0 bpd_decoder0.io.pc := pc0 val bpd_decoder1 = Module(new BranchDecode) bpd_decoder1.io.inst := exp_inst1 bpd_decoder1.io.pc := pc1
出力はこんな感じ。ret/call/targetとかをデコードするっぽい。
class BranchDecodeSignals(implicit p: Parameters) extends BoomBundle { val is_ret = Bool() val is_call = Bool() val target = UInt(vaddrBitsExtended.W) val cfi_type = UInt(CFI_SZ.W) // Is this branch a short forwards jump? val sfb_offset = Valid(UInt(log2Ceil(icBlockBytes).W)) // Is this instruction allowed to be inside a sfb? val shadowable = Bool() }
デコーダから出力される情報はだいたいこんな感じらしい。
val f3_is_rvc = Wire(Vec(fetchWidth, Bool())) val f3_redirects = Wire(Vec(fetchWidth, Bool())) val f3_targs = Wire(Vec(fetchWidth, UInt(vaddrBitsExtended.W))) val f3_cfi_types = Wire(Vec(fetchWidth, UInt(CFI_SZ.W))) val f3_shadowed_mask = Wire(Vec(fetchWidth, Bool())) val f3_fetch_bundle = Wire(new FetchBundle) val f3_mask = Wire(Vec(fetchWidth, Bool())) val f3_br_mask = Wire(Vec(fetchWidth, Bool())) val f3_call_mask = Wire(Vec(fetchWidth, Bool())) val f3_ret_mask = Wire(Vec(fetchWidth, Bool())) val f3_npc_plus4_mask = Wire(Vec(fetchWidth, Bool())) val f3_btb_mispredicts = Wire(Vec(fetchWidth, Bool()))
これらの情報は、f3_fetch_bundleというものにまとめられてf4_btb_correctionsに渡される。その名の通りBTBの修正をここで行うっぽいなあ。
f4_btb_corrections.io.enq.valid := f3.io.deq.fire && f3_btb_mispredicts.reduce(_||_) && enableBTBFastRepair.B f4_btb_corrections.io.enq.bits := DontCare f4_btb_corrections.io.enq.bits.is_mispredict_update := false.B f4_btb_corrections.io.enq.bits.is_repair_update := false.B f4_btb_corrections.io.enq.bits.btb_mispredicts := f3_btb_mispredicts.asUInt f4_btb_corrections.io.enq.bits.pc := f3_fetch_bundle.pc f4_btb_corrections.io.enq.bits.ghist := f3_fetch_bundle.ghist f4_btb_corrections.io.enq.bits.lhist := f3_fetch_bundle.lhist f4_btb_corrections.io.enq.bits.meta := f3_fetch_bundle.bpd_meta
f4からのアップデートと、FTQからのアップデートの両方を調停してBTBをアップデートする。