以下に課題実現のためのヒントを示す。
※質問・疑問は随時受け付ける。
注意:各パイプステージは一つ前のサイクルで更新されたパイプライン
レジスタの内容を参照する必要がある。
そのため、一つ前の値を記録する変数と現在の(パイプステージによって更新される)
値を記録する変数を用意する必要がある。
例えば、
値を読み出すためのメンバ関数 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;
...
}
***** mipsクラスのメンバ変数として追加 ***** ... // // pipeline registers (下記以外の変数名でもよい) // pipereg IF_IS_reg; pipereg IS_RR_reg; pipereg RR_EX_reg; pipereg EX_MA_reg; pipereg MA_WB_reg; ...
注意:
前のステージから(何も処理をせずに)そのまま次のステージに受け渡す値を
コピーすることを忘れないようにすること。
例えば、プログラムカウンタ 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);
...
***** 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);
}
※後述のデータハザードに対応するため(パイプラインインタロック
機能を実現するため)に、後でこの関数の中身を(一部)変更することになる。
※この変更に伴い mipsクラスのメンバ変数 phase は不要になるので、 削除して構わない。***** cpu::execute() ***** ... // process each pipeline stage IF(); IS(); RR(); EX(); MA(); WB(); // update pipeline registers update_piperegs(); // update pipeline registers ...(以下省略)...
※現時点では PC の値が常にPC = NPC; あるいは PC += 4;
+4したアドレスに 更新されるようにしておく。
ここまで出来たところで、基本パイプラインの動作テストを行う。
以下のテストプログラム test1 を使うとよい。
アセンブリソース/
逆アセンブルリスト/
ダンプテキストファイル