計算機アーキテクチャ特論 レポート課題 (ヒント)


以下に課題実現のためのヒントを示す。
※質問・疑問は随時受け付ける。

課題1. 命令パイプラインシミュレータの作成

以下の作業を行い、制御ハザードに対応できるようにする。
  1. 非パイプラインプロセッサ用の遅延分岐処理コードを無効化
    mips.h の先頭の方にある以下の行を削除あるいは コメントアウトする。
    // #define	DELAYED_BRANCH
    

  2. MAステージ処理の変更
    MAステージ中で PC に値を代入している部分を全て削除 あるいはコメントアウトする。
    PC の値の更新をすべて IFステージで統一して行うようにする。

  3. WBステージ処理の変更
    WBステージ中で、JAL/JALR命令で31番レジスタあるいは rdレジスタ に保存される関数呼び出しの戻り先アドレスを NPC + 4に変更する。 (NPCでは正しくないので注意)
    以下のような変更を行う。(JALR命令についても同様の変更を行う)
    ===== 変更前 =====
    ...
        case 0x03:		// JAL
    #ifdef	DELAYED_BRANCH
    	Regs[31] = NPC + 4;
    #else
    	Regs[31] = NPC;
    #endif
    ...
    
    ===== 変更後 =====
    ...
        case 0x03:		// JAL
    	Regs[31] = NPC + 4;
    ...
    

  4. IFステージ処理の変更
    命令の読み込み処理後に、 IS・RRステージ間のパイプラインレジスタ(IS_RR_reg )を調べ、 そこにジャンプ/分岐命令が格納されている場合は次のサイクルで使用する PC の値をIS・RRステージ間のパイプラインレジスタ(IS_RR_reg ) 内の NPC の値に基づいて算出する。
    ジャンプ/分岐命令が格納されていない場合はそのまま PC の値を変更しない。
    (つまり、NPC (値はPC + 4)のままにしておくということ)
    具体的な処理内容については、配付資料 図A.24, A.25を参考にすること。


    例えば、以下のような実装を行う。

ここまで出来たところで、制御ハザードに対応できるか動作テストを行う。
以下のテストプログラム test3 を使うとよい。
アセンブリソース/ 逆アセンブルリスト/ ダンプテキストファイル


追加:
レジスタのアップデートのタイミングの問題への対処
WBステージで行なわれるレジスタの更新のタイミングと RRステージで行なわれるレジスタの値読み出しのタイミングの扱いが、 配付資料に書かれているものとシミュレータで少し異なる。
シミュレータでは、WBステージで更新されたレジスタの値は次のサイクルでしか 読み出しができない。
一方、配付資料で説明されている方法では、現在のサイクルの前半でレジスタの値が 更新され、後半ではレジスタの値が読み出せるようになるので、 同じサイクル中に値が読み出せる。
そのため、RRステージで読み出されるレジスタの値が異なる場合が生じることになるため、 これに対処する必要がある。
そこで、レジスタの値更新処理について、以下の変更を行なう。

  1. mipsクラスのメンバ関数として以下を追加する。
    void
    mips::pre_update_register()
    {
        int last_updated_regno = 0;
        sim_inst ir = MA_WB_reg.get_IR();
    
        if ((is_ALU_inst(ir) && !is_muldiv_inst(ir)) ||
            is_mfhilo_inst(ir))
            last_updated_regno = rd(ir);
        else if (is_ALUimm_inst(ir) || is_load_inst(ir))
            last_updated_regno = rt(ir);
        else if (opcode(ir) == 0x00 && funct(ir) == 0x09)   // JALR
            last_updated_regno = rd(ir);
        else if (opcode(ir) == 0x03)                        // JAL
            last_updated_regno = 31;
    
        // no action if the updated register No. is 0.
        if (last_updated_regno == 0)
            return;
    
        if (last_updated_regno == rs(IS_RR_reg.get_IR())) {
            RR_EX_reg.set_A(Regs[last_updated_regno]);
            cout << " update RR_EX_reg's A="
                 << hex << Regs[last_updated_regno] << endl;
        }
        if (last_updated_regno == rt(IS_RR_reg.get_IR())) {
            RR_EX_reg.set_B(Regs[last_updated_regno]);
            cout << " update RR_EX_reg's B="
                 << hex << Regs[last_updated_regno] << endl;
        }
    }
    
  2. mipsクラスのメンバ関数update_piperegs()の最初に 以下のコードを追加
        // work-around code for register update timing problem
        pre_update_register();
    

以上までの作業で、mipsy シミュレータのパイプライン化はほぼ完了 である。
適当なプログラム(ある程度複雑な動作をするものがよい)を作って動作確認を 行うこと。


文責:大津