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


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

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

以下の作業を行い、パイプライン処理の基本動作が行えるようにする。
  1. パイプラインレジスタを実現するクラス pipereg を作成
    mips クラス定義の "temporary values"とコメントして いる辺りのメンバ変数(+PC)を管理するための pipereg クラスを作成する。

    注意:各パイプステージは一つ前のサイクルで更新されたパイプライン レジスタの内容を参照する必要がある。 そのため、一つ前の値を記録する変数と現在の(パイプステージによって更新される) 値を記録する変数を用意する必要がある。

    例えば、 値を読み出すためのメンバ関数 get_XXX() では一つ前の値を記録した 変数の値を返し、 値を書き込むためのメンバ関数 set_XXX() では現在の値を記録する 変数に対して書き込むように作るとよい。 その上で、1サイクル分の各ステージの処理がおわった後で、レジスタの値を 現在の値を一つ前の値を記録している変数にコピーするメンバ関数 update() を用意する。
    また、メンバ変数を初期化するメンバ関数 reset() を用意する。

    具体的には、以下のようなクラス定義を行うとよい。

    ***** pipereg.h *****
    
    class pipereg {
        struct regs {
            bool        valid;
            sim_addr    PC;
    
    	//
    	// temporary values
    	//
            ...
        } prev, next;
        ...
    public:
        pipereg() { reset(); }
    
        ~pipereg() { }
    
        //
        // setter
        //
        void set_valid()            { next.valid = true; }
        void set_invalid()          { next.valid = false; }
    
        void set_PC(sim_addr pc);
        ...
    
        //
        // getter
        //
        bool is_valid()             { return prev.valid; }
        bool is_ready()             { return !next.valid; }
    
        sim_addr get_PC();
        ...
    
        void update(bool stall_f);
        void reset();
    };       
    
    ***** pipereg.cc *****
    
    void
    pipereg::set_PC(sim_addr pc)
    {
        next.PC = pc;
    }
    
    sim_addr
    pipereg::get_PC()
    {
        return prev.PC;
    }
    
    void
    pipereg::update(bool stall_f)
    {
        if (!stall_f) {
            prev = next;
            next.valid = false;
       }
    }
    
    void
    pipereg::reset()
    {
        prev.valid = next.valid = false;
        prev.PC = next.PC = 0;
        ...
    }       
    

  2. パイプライン化により不要になるメンバ変数を削除
    mips クラス定義内の "temporary values" 付近のメンバ変数を削除 あるいはコメントアウトする。

  3. パイプラインレジスタ 5個をメンバ変数として宣言
    IF〜WBの隣接する各パイプステージ間に1個ずつ(合計5個) パイプラインレジスタ(pipereg クラスの変数)を宣言する。
    このとき、どのパイプステージ間であるのかが分かるような名前にする。
    (例えば、IF_IS_reg など)
    ***** mipsクラスのメンバ変数として追加 *****
    ...
    //
    // pipeline registers (下記以外の変数名でもよい)
    //
    pipereg     IF_IS_reg;
    pipereg     IS_RR_reg;
    pipereg     RR_EX_reg;
    pipereg     EX_MA_reg;
    pipereg     MA_WB_reg;
    ...
    

  4. 各パイプステージの処理の記述
    mips クラスメンバ関数 IF()WB() の中身を修正して、 パイプラインレジスタの内容に対して各パイプステージの入力データの読み出しと パイプステージの処理結果の書き込みを行うように書き換えることで、 各パイプステージの処理を記述する。

    注意:
    前のステージから(何も処理をせずに)そのまま次のステージに受け渡す値を コピーすることを忘れないようにすること。
    例えば、プログラムカウンタ PC の値等は、入力した値を そのまま次のステージに受け渡すために、(出力側の)パイプラインレジスタに メンバ関数 set_PC() を使って書き込んでおく必要がある。

    例えば、IS() は以下のような書き出しになる。

    void
    mips::IS()
    {
        if (!IF_IS_reg.is_valid())        // check whether pipereg is valid
            return;
    
        cout << "---------- IS ----------" << endl;;
    
        // from previous stage
        sim_addr PC = IF_IS_reg.get_PC();
        sim_addr NPC = IF_IS_reg.get_NPC();
    
        cout << "PC=" << hex << PC << endl;
        cout << "NPC=" << hex << NPC << endl;
    
        // pass through to next stage
        IS_RR_reg.set_valid();
        IS_RR_reg.set_PC(PC);
        IS_RR_reg.set_NPC(NPC);
    
    ...
    

  5. 各パイプラインレジスタの更新処理の記述
    各パイプステージ間のパイプラインレジスタの値について、 現在の値を一つ前の値を記録している変数にコピーする処理を行うメンバ関数 update_piperegs()mips クラスに追加して中身を実装する。
    ***** mipsクラスのメンバ関数として追加 *****
    void
    mips::update_piperegs()
    {
        IF_IS_reg.update(false);
        IS_RR_reg.update(false);
        RR_EX_reg.update(false);
        EX_MA_reg.update(false);
        MA_WB_reg.update(false);
    }
    
    ※後述のデータハザードに対応するため(パイプラインインタロック 機能を実現するため)に、後でこの関数の中身を(一部)変更することになる。

  6. 命令サイクル処理の変更
    mips クラスのメンバ関数 update() 内で命令サイクルを 実現しているループの中身を書き換える。
    毎サイクル IF()WB() が実行されるように変更する。
    ***** cpu::execute() *****
    ...
    
    // process each pipeline stage
    IF();
    IS();
    RR();
    EX();
    MA();
    WB();
    
    // update pipeline registers
    update_piperegs();        // update pipeline registers
    
    ...(以下省略)...
    
    ※この変更に伴い mipsクラスのメンバ変数 phase は不要になるので、 削除して構わない。

  7. IFステージにて PC の値を更新するコードを追加
    IFステージの最後で以下を追加する。
    PC = NPC;  あるいは  PC += 4;
    
    ※現時点では PC の値が常に +4 したアドレスに 更新されるようにしておく。
    後述の制御ハザードに対応する作業でこの部分のコードを変更し、 ジャンプ/分岐命令に対応する。

ここまで出来たところで、基本パイプラインの動作テストを行う。
以下のテストプログラム test1 を使うとよい。
アセンブリソース/ 逆アセンブルリスト/ ダンプテキストファイル


文責:大津