2

現在、asm シミュレーター用のパイプラインを作成しています。私はマルチスレッドでこれを行うつもりです。現在、フェッチ、デコード、実行、ライトバックのそれぞれに対応する 4 つのスレッドがあります。正しいパイプラインをコーディングする方法について混乱しています。これが私のコードの一部です:

private void Fetch()
{
    while(_continue)
    {
        fetchEvent.WaitOne();
        if (!_continue) break;
        lock (irLock)
        {
            try
            {
                // Do stuff to fetch instruction...

                catch (IndexOutOfRangeException e) { Err = "Segmentation Fault at Line " + _pc.ToString(); }
                catch (Exception e) { Err = e.Message + "Line " + _pc.ToString(); }
                _pc++;

                GiveTurnTo(2); // used these 2 lines to handle mutual exclusion
                WaitTurn(1);
            }
        }
    }

    private void Decode()
    {
        while (_continue)
        {
            decodeEvent.WaitOne();
            if (!_continue) break;

            lock (irLock)
            {
                WaitTurn(2);
                Console.WriteLine("decode works");
                GiveTurnTo(1);
            }


        }
    }

    private void Execute()
    {
        while (_continue)
        {
            exeEvent.WaitOne();
            if (!_continue) break;

            lock (irLock)
            {
                //WaitTurn(3);
                Console.WriteLine("Am I, execute, getting a turn?");
                // GiveTurnTo(4);
            }

        }

    }

    private void WriteBack()
    {
        while (_continue)
        {
            wbEvent.WaitOne();
            if (!_continue) break;

            lock (irLock)
            {
                Console.WriteLine("Am I, Write Back, getting a turn?");
                //GiveTurnTo(1);
                // WaitTurn(4);
            }

        }

    }
}

このメソッドを使用して、ボタンのクリックでサイクルを実行します。

public void nextInstruction()
{
    fetchEvent.Set();
    decodeEvent.Set();
    exeEvent.Set();
    wbEvent.Set();
}
I was thinking of changing nextInstruction() to this:

public void nextInstruction()
{
    fetchEvent.Set();
}

各ボタンのクリックは常に Fetch で始まります。その後、Fetch メソッド内に set イベントを配置して、サイクルの次の部分 (Decode) を呼び出し、次のメソッドに対して同じことを行うことができるのではないかと考えました。私はこのようなものになります:

private void Fetch()
{
    while(_continue)
    {
        // Do stuff....
        decodeEvent.Set();
    }
}

private void Decode()
{
    while (_continue)
    {
        // Do stuff...
        exeEvent.Set();
    }
}

private void Execute()
{
    while (_continue)
    {
        // Do stuff...
        wbEvent.Set();
    }
}
private void WriteBack()
{
    while (_continue)
    {
        // Do stuff...
    }
}

それらの設定されたイベントを公開するのではなく、何らかのロジックでアクティブ化する必要があると思いますが、どのようなロジックかはわかりません。たぶん、ボタンをクリックしてアクティブにすることができます。これにより、命令がサイクルに渡されるタイミングを制御できます。理論的には、これでパイプラインの構造が得られると思います。誰でもこれについて意見を提供できますか? これは、自動リセット イベントだけで実現できますか?

私が提供したコードの最初のブロックを見ると、ロックを使用しようとしたことがわかりますが、一度に 1 つのスレッドしか実行できませんでした。などの形式になるようにしたいですFetch0, {Decode0, Fetch1}, {Execute0, Decode1, Fetch3},...。この場合、ロックは必要ですか?

4

2 に答える 2

1

スレッドはこれには適していません。スレッドをスリープ状態にする前に、ステップごとに行う作業が少なすぎます。それをシミュレートするより良い方法は、単一のスレッドが各ステップを逆の順序で実行することです。これにより、実際のシステムと同じようにシステムに作業が波及します。

public void nextInstruction()
{
    WriteBack();
    Execute();
    Decode();
    Fetch();
}

なぜ逆の順序で処理するのかを理解するために、繰り返し呼び出すときにシステムがどのように機能するかを見てみましょう。

  • コール 1
    1. WriteBack()入力バッファは空です。
    2. Execute()入力バッファは空です。
    3. Decode()入力バッファは空です。
    4. Fetech()最初の命令を取得し、 の入力バッファに入れますDecode()
  • コール 2
    1. WriteBack()入力バッファは空です。
    2. Execute()入力バッファは空です。
    3. Decode()入力バッファからフェッチされた最初の命令を取得し、デコードされた命令を入力バッファに配置しExecute()ます。
    4. Fetech()2 番目の命令を取得し、 の入力バッファーに入れますDecode()
  • コール 3
    1. WriteBack()入力バッファは空です。
    2. Execute()デコードされた最初の命令を入力バッファから取得して処理し、結果を toWriteBack()の入力バッファに配置します。
    3. Decode()フェッチされた2 番目の命令をその入力バッファーから取得して処理し、デコードされた命令をExecute()の入力バッファーに配置します。
    4. Fetech()3 番目の命令を取得し、それを の入力バッファーに入れますDecode()
  • コール 4
    1. WriteBack()入力バッファから処理された最初の値を取得し、メモリ ストアに書き戻します。
    2. Execute()入力バッファからデコードされた2 番目の命令を取得して処理し、結果を toWriteBack()の入力バッファに配置します。
    3. Decode()フェッチされた3 番目の命令をその入力バッファーから取得して処理し、デコードされた命令をExecute()の入力バッファーに配置します。
    4. Fetech()4 番目の命令を取得し、それを の入力バッファーに入れますDecode()
  • コール 5
    1. WriteBack()処理された2 番目の値を入力バッファーから取得し、メモリ ストアに書き戻します。
    2. Execute()デコードされた3 番目の命令を入力バッファーから取得して処理し、結果を toWriteBack()の入力バッファーに配置します。
    3. Decode()フェッチされた4 番目の命令をその入力バッファーから取得して処理し、デコードされた命令をExecute()の入力バッファーに配置します。
    4. Fetech()5 番目の命令を取得し、それを の入力バッファーに入れますDecode()
  • コール 6
    1. WriteBack()処理された3 番目の値を入力バッファーから取得し、メモリ ストアに書き戻します。
    2. Execute()入力バッファからデコードされた4 番目の命令を取得して処理し、結果を toWriteBack()の入力バッファに配置します。
    3. Decode()フェッチされた5 番目の命令をその入力バッファーから取得して処理し、デコードされた命令をExecute()の入力バッファーに配置します。
    4. Fetech()6 番目の命令を取得し、 の入力バッファに入れますDecode()
  • など。

パイプラインがいっぱいになるまでに 4 回の呼び出しが必要です。これは実際に発生することです。最初の命令を取得するにはより多くのサイクルが必要FetchですWriteBackが、パイプラインから出てくる命令間の時間は比較するとはるかに短くなっています。

古いシステムを見ると、実際にはパイプラインがいっぱいになることはなく、1 つの命令が 1 つのシミュレーション ステップですべてのステージを通過できることがわかります。

  • コール 1
    1. Fetech()最初の命令を取得し、 の入力バッファに入れますDecode()
    2. Decode()フェッチされた最初の命令を入力バッファから取得して処理し、デコードされた命令をExecute()の入力バッファに配置します。
    3. Execute()デコードされた最初の命令を入力バッファから取得して処理し、結果を toWriteBack()の入力バッファに配置します。
    4. WriteBack()入力バッファから処理された最初の値を取得し、メモリ ストアに書き戻します。
  • コール 2
    1. Fetech()2 番目の命令を取得し、 の入力バッファーに入れますDecode()
    2. Decode()フェッチされた2 番目の命令をその入力バッファーから取得して処理し、デコードされた命令をExecute()の入力バッファーに配置します。
    3. Execute()入力バッファからデコードされた2 番目の命令を取得して処理し、結果を toWriteBack()の入力バッファに配置します。
    4. WriteBack()処理された2 番目の値を入力バッファーから取得し、メモリ ストアに書き戻します。

1 回の呼び出しで、2 番目の命令をフェッチする前に、命令がフェッチされてから書き戻されました。

于 2013-11-16T04:20:32.570 に答える
1

パイプラインは、イベントの同期または非同期を発生させるプロセスです。オブザーバーはパイプラインに登録され、これらのイベントについて通知されます。パイプラインは状態を格納して、オブザーバーが状態を操作できるようにします。非同期イベントの場合、スレッドがいつ完了したか作業の準備ができたかを知るためのコールバックだけでなく、状態の周りで同期が行われるようにする必要があります。パイプラインを使用する大まかな高レベルの例を次に示します。

public class OnFetchEvent : AsyncEvent {}
public class OnDecodeEvent : AsyncEvent {}
public class OnExecuteEvent : AsyncEvent {}
public class OnWritebackEvent : AsyncEvent {}

public class FetchObserver : Observer,
   IObserve<OnFetchEvent>
{
   public void OnEvent(OnFetchEvent @event)
   {
      ....do some stuff

      // raise the next event
      RaiseEvent<OnDecodeEvent>(); 
   }
}

public class Pipeline
{
   public void RaiseEvent<TEvent>()
   {
      if (typeof(TEvent) is AsyncEvent)
        ...create thread and raise the event which will notify the appropriate 
           observers of the event in the newly created thread
   }

  }

使用法:

pipeline.RegisterObserver<FetchObserver>()
  .AndObserver<DecodeObserver>()
  .AndObserver<ExecuteObserver>()
  .AndObserver<WriteBackObserver>();

pipeline.RaiseEvent<OnFetchEvent>();

これで 1 サイクルが処理されます。イベントは非同期で発生します (つまり、オブザーバーはスレッドごとに実行されます)。複数のサイクルを持つには、パイプラインの非同期 (独自のスレッド) を開始する必要があります。そして、必要に応じてパイプライン RaiseEvent を呼び出します。これにより、スレッドを使用してパイプラインを利用するための高レベルのアプローチが提供されることを願っています。

于 2013-11-16T08:55:51.783 に答える