FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

オープンソース・アウトオブオーダCPU NaxRiscvを概観する (12. PcPluginの実装解析)

PcPluginの実装の解析

github.com

とりあえずまずはChatGPTにこのソースコードを解説してもらおうと思う。

- ジャンプロジックの設定:

ジャンプロジックでは、登録されたジャンプ要求を優先度に応じてソートし、優先度が高いものから順に評価します。
ジャンプ命令が有効であれば、それに応じてプログラムカウンタが更新されます。これは、異なるジャンプグループを適切に扱うことで複数のジャンプ要求をまとめて処理します。

- プログラムカウンタの初期化とインクリメントロジック:

初期化ロジックでは、システムが完全に起動するまでのサイクル数を計算し、その間はプログラムカウンタの更新を遅延させます。
PCの計算では、ジャンプ命令による更新がない場合は通常のインクリメントを行います。ジャンプ命令が発行された場合は、プログラムカウンタをジャンプ先のアドレスに設定し直します。

最初のジャンプロジックの設定は以下のソースコードの部分だと思われる:

    val jump = new Area {
      val sortedByStage = jumpsSpec.sortWith(_.priority > _.priority)
      val valids = sortedByStage.map(_.interface.valid)
      val cmds = sortedByStage.map(_.interface.payload)
      val oh = OHMasking.firstV2(valids.asBits)

      val grouped = sortedByStage.groupByLinked(_.aggregationPriority).toList.sortBy(_._1).map(_._2)
      var target = PC()
      for(group <- grouped){
        val indexes = group.map(e => sortedByStage.indexOf(e))
        val goh = indexes.map(i => oh(i))
        val mux = OhMux.or(goh, group.map(_.interface.pc))
        if(group == grouped.head) target := mux else when(goh.orR){
          KeepAttribute(target)
          target \= mux
        }
      }

      val pcLoad = Flow(JumpCmd(pcWidth = widthOf(PC)))
      pcLoad.valid := jumpsSpec.map(_.interface.valid).orR
      pcLoad.pc    := target
    }

これが以下のようにVerilogに変換されている。もう何が何だか分からない。

  reg        [39:0]   PcPlugin_logic_jump_target_1;
  wire       [5:0]    _zz_PcPlugin_logic_jump_oh;
  wire                _zz_PcPlugin_logic_jump_oh_1;
  wire                _zz_PcPlugin_logic_jump_oh_2;
  wire                _zz_PcPlugin_logic_jump_oh_3;
  wire                _zz_PcPlugin_logic_jump_oh_4;
  wire                _zz_PcPlugin_logic_jump_oh_5;
  reg        [5:0]    _zz_PcPlugin_logic_jump_oh_6;
  wire       [5:0]    PcPlugin_logic_jump_oh;
  (* keep , syn_keep *) wire       [39:0]   PcPlugin_logic_jump_target /* synthesis syn_keep = 1 */ ;
  wire                _zz_PcPlugin_logic_jump_target_1;
  wire                PcPlugin_logic_jump_pcLoad_valid;
  wire       [39:0]   PcPlugin_logic_jump_pcLoad_payload_pc;
    PcPlugin_logic_jump_target_1 = PcPlugin_logic_jump_target;
      PcPlugin_logic_jump_target_1 = (_zz_PcPlugin_logic_jump_target_1 ? BtbPlugin_setup_btbJump_payload_pc : 40'h0000000000);
  assign _zz_PcPlugin_logic_jump_oh = {BtbPlugin_setup_btbJump_valid,{FetchCachePlugin_setup_redoJump_valid,{AlignerPlugin_setup_sequenceJump_valid,{DecoderPredictionPlugin_setup_decodeJump_valid,{CommitPlugin_setup_jump_valid,PrivilegedPlugin_setup_jump_valid}}}}};
  assign _zz_PcPlugin_logic_jump_oh_1 = _zz_PcPlugin_logic_jump_oh[0];
  assign _zz_PcPlugin_logic_jump_oh_2 = _zz_PcPlugin_logic_jump_oh[1];
  assign _zz_PcPlugin_logic_jump_oh_3 = _zz_PcPlugin_logic_jump_oh[2];
  assign _zz_PcPlugin_logic_jump_oh_4 = _zz_PcPlugin_logic_jump_oh[3];
  assign _zz_PcPlugin_logic_jump_oh_5 = _zz_PcPlugin_logic_jump_oh[4];
    _zz_PcPlugin_logic_jump_oh_6[0] = (_zz_PcPlugin_logic_jump_oh_1 && (! 1'b0));
    _zz_PcPlugin_logic_jump_oh_6[1] = (_zz_PcPlugin_logic_jump_oh_2 && (! _zz_PcPlugin_logic_jump_oh_1));
    _zz_PcPlugin_logic_jump_oh_6[2] = (_zz_PcPlugin_logic_jump_oh_3 && (! (|{_zz_PcPlugin_logic_jump_oh_2,_zz_PcPlugin_logic_jump_oh_1})));
    _zz_PcPlugin_logic_jump_oh_6[3] = (_zz_PcPlugin_logic_jump_oh_4 && (! (|{_zz_PcPlugin_logic_jump_oh_3,{_zz_PcPlugin_logic_jump_oh_2,_zz_PcPlugin_logic_jump_oh_1}})));
    _zz_PcPlugin_logic_jump_oh_6[4] = (_zz_PcPlugin_logic_jump_oh_5 && (! (|{_zz_PcPlugin_logic_jump_oh_4,{_zz_PcPlugin_logic_jump_oh_3,{_zz_PcPlugin_logic_jump_oh_2,_zz_PcPlugin_logic_jump_oh_1}}})));
    _zz_PcPlugin_logic_jump_oh_6[5] = (_zz_PcPlugin_logic_jump_oh[5] && (! (|{_zz_PcPlugin_logic_jump_oh_5,{_zz_PcPlugin_logic_jump_oh_4,{_zz_PcPlugin_logic_jump_oh_3,{_zz_PcPlugin_logic_jump_oh_2,_zz_PcPlugin_logic_jump_oh_1}}}})));
  assign PcPlugin_logic_jump_oh = _zz_PcPlugin_logic_jump_oh_6;
  assign PcPlugin_logic_jump_target = ((((PcPlugin_logic_jump_oh[0] ? PrivilegedPlugin_setup_jump_payload_pc : 40'h0000000000) | (PcPlugin_logic_jump_oh[1] ? CommitPlugin_setup_jump_payload_pc : 40'h0000000000)) | ((PcPlugin_logic_jump_oh[2] ? DecoderPredictionPlugin_setup_decodeJump_payload_pc : 40'h0000000000) | (PcPlugin_logic_jump_oh[3] ? AlignerPlugin_setup_sequenceJump_payload_pc : 40'h0000000000))) | (PcPlugin_logic_jump_oh[4] ? FetchCachePlugin_setup_redoJump_payload_pc : 40'h0000000000));
  assign _zz_PcPlugin_logic_jump_target_1 = PcPlugin_logic_jump_oh[5];
  assign when_PcPlugin_l55 = (|_zz_PcPlugin_logic_jump_target_1);
  assign PcPlugin_logic_jump_pcLoad_valid = (|{PrivilegedPlugin_setup_jump_valid,{CommitPlugin_setup_jump_valid,{BtbPlugin_setup_btbJump_valid,{DecoderPredictionPlugin_setup_decodeJump_valid,{AlignerPlugin_setup_sequenceJump_valid,FetchCachePlugin_setup_redoJump_valid}}}}});
  assign PcPlugin_logic_jump_pcLoad_payload_pc = PcPlugin_logic_jump_target_1;
    if(PcPlugin_logic_jump_pcLoad_valid) begin
    if(PcPlugin_logic_jump_pcLoad_valid) begin
      PcPlugin_logic_fetchPc_pc = PcPlugin_logic_jump_pcLoad_payload_pc;
    if(PcPlugin_logic_jump_pcLoad_valid) begin

例えば jump_oh の実装は以下の感じでめっちゃややこしくなっている。

  assign _zz_PcPlugin_logic_jump_oh = {BtbPlugin_setup_btbJump_valid,{FetchCachePlugin_setup_redoJump_valid,{AlignerPlugin_setup_sequenceJump_valid,{DecoderPredictionPlugin_setup_decodeJump_valid,{CommitPlugin_setup_jump_valid,PrivilegedPlugin_setup_jump_valid}}}}};
  assign _zz_PcPlugin_logic_jump_oh_1 = _zz_PcPlugin_logic_jump_oh[0];
  assign _zz_PcPlugin_logic_jump_oh_2 = _zz_PcPlugin_logic_jump_oh[1];
  assign _zz_PcPlugin_logic_jump_oh_3 = _zz_PcPlugin_logic_jump_oh[2];
  assign _zz_PcPlugin_logic_jump_oh_4 = _zz_PcPlugin_logic_jump_oh[3];
  assign _zz_PcPlugin_logic_jump_oh_5 = _zz_PcPlugin_logic_jump_oh[4];
    _zz_PcPlugin_logic_jump_oh_6[0] = (_zz_PcPlugin_logic_jump_oh_1 && (! 1'b0));
    _zz_PcPlugin_logic_jump_oh_6[1] = (_zz_PcPlugin_logic_jump_oh_2 && (! _zz_PcPlugin_logic_jump_oh_1));
    _zz_PcPlugin_logic_jump_oh_6[2] = (_zz_PcPlugin_logic_jump_oh_3 && (! (|{_zz_PcPlugin_logic_jump_oh_2,_zz_PcPlugin_logic_jump_oh_1})));
    _zz_PcPlugin_logic_jump_oh_6[3] = (_zz_PcPlugin_logic_jump_oh_4 && (! (|{_zz_PcPlugin_logic_jump_oh_3,{_zz_PcPlugin_logic_jump_oh_2,_zz_PcPlugin_logic_jump_oh_1}})));
    _zz_PcPlugin_logic_jump_oh_6[4] = (_zz_PcPlugin_logic_jump_oh_5 && (! (|{_zz_PcPlugin_logic_jump_oh_4,{_zz_PcPlugin_logic_jump_oh_3,{_zz_PcPlugin_logic_jump_oh_2,_zz_PcPlugin_logic_jump_oh_1}}})));
    _zz_PcPlugin_logic_jump_oh_6[5] = (_zz_PcPlugin_logic_jump_oh[5] && (! (|{_zz_PcPlugin_logic_jump_oh_5,{_zz_PcPlugin_logic_jump_oh_4,{_zz_PcPlugin_logic_jump_oh_3,{_zz_PcPlugin_logic_jump_oh_2,_zz_PcPlugin_logic_jump_oh_1}}}})));
  assign PcPlugin_logic_jump_oh = _zz_PcPlugin_logic_jump_oh_6;
  assign PcPlugin_logic_jump_target = ((((PcPlugin_logic_jump_oh[0] ? PrivilegedPlugin_setup_jump_payload_pc : 40'h0000000000) | (PcPlugin_logic_jump_oh[1] ? CommitPlugin_setup_jump_payload_pc : 40'h0000000000)) | ((PcPlugin_logic_jump_oh[2] ? DecoderPredictionPlugin_setup_decodeJump_payload_pc : 40'h0000000000) | (PcPlugin_logic_jump_oh[3] ? AlignerPlugin_setup_sequenceJump_payload_pc : 40'h0000000000))) | (PcPlugin_logic_jump_oh[4] ? FetchCachePlugin_setup_redoJump_payload_pc : 40'h0000000000));

次のfetchPCがPCのインクリメントの部分だと思われる。

    val fetchPc = new Area{
      //PC calculation without Jump
      val output = Stream(PC)
      val pcReg = Reg(PC) init(resetVector) addAttribute(Verilator.public)
      val correction = False
      val correctionReg = RegInit(False) setWhen(correction) clearWhen(output.fire)
      val corrected = correction || correctionReg
      val pcRegPropagate = False
      val inc = RegInit(False) clearWhen(correction || pcRegPropagate) setWhen(output.fire) clearWhen(!output.valid && output.ready)
      val pc = pcReg + (U(inc) << sliceRange.high+1)


      val flushed = False

      when(inc) {
        pc(sliceRange) := 0
      }

      when(jump.pcLoad.valid) {
        correction := True
        pc := jump.pcLoad.pc
        flushed := True
      }

      when(init.booted && (output.ready || correction || pcRegPropagate)){
        pcReg := pc
      }

      pc(0) := False
      if(!RVC) pc(1) := False

      val fetcherHalt = False
      output.valid := !fetcherHalt && init.booted
      output.payload := pc
    }

これも以下のようにVerilogに変換されていた。

  assign _zz_PcPlugin_logic_fetchPc_pc_1 = ({3'd0,PcPlugin_logic_fetchPc_inc} <<< 2'd3);
  assign _zz_PcPlugin_logic_fetchPc_pc = {36'd0, _zz_PcPlugin_logic_fetchPc_pc_1};
    PcPlugin_logic_fetchPc_correction = 1'b0;
      PcPlugin_logic_fetchPc_correction = 1'b1;
  assign PcPlugin_logic_fetchPc_output_fire = (PcPlugin_logic_fetchPc_output_valid && PcPlugin_logic_fetchPc_output_ready);
  assign PcPlugin_logic_fetchPc_corrected = (PcPlugin_logic_fetchPc_correction || PcPlugin_logic_fetchPc_correctionReg);
  assign PcPlugin_logic_fetchPc_pcRegPropagate = 1'b0;
  assign when_PcPlugin_l82 = (PcPlugin_logic_fetchPc_correction || PcPlugin_logic_fetchPc_pcRegPropagate);
  assign when_PcPlugin_l82_1 = ((! PcPlugin_logic_fetchPc_output_valid) && PcPlugin_logic_fetchPc_output_ready);
    PcPlugin_logic_fetchPc_pc = (PcPlugin_logic_fetchPc_pcReg + _zz_PcPlugin_logic_fetchPc_pc);
    if(PcPlugin_logic_fetchPc_inc) begin
      PcPlugin_logic_fetchPc_pc[2 : 2] = 1'b0;
      PcPlugin_logic_fetchPc_pc = PcPlugin_logic_jump_pcLoad_payload_pc;
    PcPlugin_logic_fetchPc_pc[0] = 1'b0;
    PcPlugin_logic_fetchPc_pc[1] = 1'b0;
    PcPlugin_logic_fetchPc_flushed = 1'b0;
      PcPlugin_logic_fetchPc_flushed = 1'b1;
  assign when_PcPlugin_l98 = (PcPlugin_logic_init_booted && ((PcPlugin_logic_fetchPc_output_ready || PcPlugin_logic_fetchPc_correction) || PcPlugin_logic_fetchPc_pcRegPropagate));
  assign PcPlugin_logic_fetchPc_fetcherHalt = 1'b0;
  assign PcPlugin_logic_fetchPc_output_valid = ((! PcPlugin_logic_fetchPc_fetcherHalt) && PcPlugin_logic_init_booted);
  assign PcPlugin_logic_fetchPc_output_payload = PcPlugin_logic_fetchPc_pc;
  assign PcPlugin_logic_fetchPc_output_ready = FetchPlugin_stages_0_ready;
  assign FetchPlugin_stages_0_valid = PcPlugin_logic_fetchPc_output_valid;
  assign FetchPlugin_stages_0_Fetch_FETCH_PC = PcPlugin_logic_fetchPc_output_payload;
      PcPlugin_logic_fetchPc_pcReg <= 40'h0080000000;
      PcPlugin_logic_fetchPc_correctionReg <= 1'b0;
      PcPlugin_logic_fetchPc_inc <= 1'b0;
      if(PcPlugin_logic_fetchPc_correction) begin
        PcPlugin_logic_fetchPc_correctionReg <= 1'b1;
      if(PcPlugin_logic_fetchPc_output_fire) begin
        PcPlugin_logic_fetchPc_correctionReg <= 1'b0;
        PcPlugin_logic_fetchPc_inc <= 1'b0;
      if(PcPlugin_logic_fetchPc_output_fire) begin
        PcPlugin_logic_fetchPc_inc <= 1'b1;
        PcPlugin_logic_fetchPc_inc <= 1'b0;
        PcPlugin_logic_fetchPc_pcReg <= PcPlugin_logic_fetchPc_pc;