3 |
BSVにおけるコマンドバッファ制御 (12) |
前の記事で、ChatGPTのコード中に、busyなら受け付けないとあったのが気になったので確認します。
start_playに疑義があります。忙しかったらやらないのではプリエンプションを実装とはいえないのでは。
「ready は常に True」はハンドシェークの意味を成しません。
Producer が valid
をパルスで出したい場合、同サイクルにready
が1でなければ
転送が成立しません。
そこで Consumer 側は「受け取った直後だけready=0
に落とす」ことで
1 サイクル確実にハンドシェークを区切ります。
修正版 mkConsumer4_Preemptive
module mkConsumer4_Preemptive (ConsumerRaw);
Reg#(Bool) busy <- mkReg(False); // 今まさに演奏中か
Reg#(Bool) hasNewCmd <- mkReg(False); // 未処理コマンド保持
Reg#(UInt#(4)) last <- mkRegU;
Reg#(Bool) ready_reg <- mkReg(True); // 出力する ready
/* Producer から毎サイクル呼ばれるメソッド
- always_ready/always_enabled: 条件文を書かない */
(* always_ready, always_enabled *)
method Action soundPreempt(UInt#(4) d);
// 受信したサイクルで ready = 0 に落とす
ready_reg <= False;
last <= d; // 最新コードを保持
hasNewCmd <= True;
endmethod
/* ready 出力メソッドは最後に置く */
method Bool rdy_o();
return ready_reg;
endmethod
/* 受信直後の次サイクルで ready を 1 に戻す */
rule raise_ready (!ready_reg);
ready_reg <= True;
endrule
/* 新コマンドがあればいつでも演奏を開始(busy を更新) */
rule start_play (hasNewCmd);
$display("%0t play %h", $time, last);
busy <= True;
hasNewCmd <= False;
endrule
/* ここでは 1 サイクルで演奏完了とする簡易例 */
rule finish_play (busy && ready_reg);
busy <= False;
endrule
endmodule
挙動
サイクル | Producer.valid | Consumer.ready | 動作 |
---|---|---|---|
t | 1 | 1 | 転送成立 → Consumer.ready を 0 へ |
t+1 | 0 | 0→1 | ready が復活。 busy が上がり演奏開始 |
busy中 | 1 | 1 | 新 valid が来れば上書きし即プリエンプト |
このようにready
を 1 サイクルだけ落とすことで
- ハンドシェーク単位は厳密に 1 サイクル
- それでも
busy
中にいつでも新コードを受理し、即プリエンプション
が両立します。
Leave a Comment