31 |
BSVにおけるコマンドバッファ制御 (2) |
まず、ChatGPTに、BSVの2線ハンドシェイクアルゴリズムを尋ねてみます。
bluespecのモジュールでは自動的に2線方式のハンドシェークが行われるが、そのアルゴリズムを解説してください。
以下、Bluespec がモジュール間/ルール間で自動的に挿入する「2線ハンドシェーク」の 正確なアルゴリズム をまとめ直します。
1. ハンドシェークの基本モデル
各メソッド呼び出し(あるいはルール起動)には、必ず以下の2本の信号が対応します。
信号 | BSV上の名前 | 役割 |
---|---|---|
Ready | can_fire() |
Callee(呼ばれる側)が 実行可能 かを示す純組合せフラグ (ready) |
Enable | will_fire |
Caller(呼ぶ側)が 実際に起動 する1サイクルパルス (enable) |
- Ready (
can_fire
) は、FIFO なら!full
、Register なら常にTrue
、独自モジュールなら内部状態から算出される組合せ信号です。 - Enable (
will_fire
) は後述のルール・スケジューラによって自動合成される一周期パルス信号で、これが Callee の*_en
ポートに接続されます。
2. ルール/メソッド呼び出し単位での合成フロー
2.1 各メソッドのcan_fire
取得
ルールの本文中で呼ぶメソッドが複数ある場合、まずそれぞれの Ready 信号を取り出します。
rule foo when (guard);
let v <- A.method1.can_fire; // ①
w <- B.method2.can_fire; // ②
…
endrule
(実際は can_fire()
はメソッド呼出子の後ろに自動で付く組合せロジックです)
2.2 ルール単位の can_fire_rule
(= 全 Ready の AND)
ルールは「自分のガードが真 & すべての callee が Ready」で発火可能です。
wire can_fire_rule = guard && A_ready && B_ready;
2.3 スケジューラ仲裁 (scheduler_grant
)
同じリソース(レジスタやポート)に対して競合する他ルールとの静的解析結果に基づき、
can_fire_rule
同士の優先度/排他を決める組合せロジックが入ります。
// 競合ルール同士なら priority_bits で排他
wire grant_rule = can_fire_rule & scheduler_logic(…);
2.4 will_fire
(= 発火パルス)
上記 grant_rule
が 1 サイクルだけ 真になる「発火許可パルス」となり、
これが Enable 信号として扱われます。
wire will_fire = grant_rule; // 1-cycle pulse
2.5 Callee への配線 (扇形ファンアウト)
得られた will_fire
を、ルール本文で呼び出したすべてのメソッドの*_en
(Enable)端子に繋ぎます。
assign A_en = will_fire;
assign B_en = will_fire;
2.6 レジスタ更新の en
端子
同じ will_fire
をそのルール内のレジスタ書き込み *_en
にも使うので、
レジスタ更新 も同一パルスでまとめて走ります。
3. 複数ルール/複数メソッドの競合回避
- 同一リソースを書き換える複数ルール は、静的優先度ツリー(priority tree)で排他し、あるルールが
will_fire
すると他は偽になります。 - 同一ルール内の複数メソッド呼び出し は、ひとつの
will_fire
で 同時に 起動。 - 非競合ルール どうしは並列に
will_fire
可能。
合成後、以下のような MUX/OR ネットワークが作られます:
// レジスタ更新データの MUX(優先度付き)
assign reg_din =
(rule2_will_fire) ? data2 :
(rule1_will_fire) ? data1 :
reg_q;
// write-enable の OR
assign reg_en = rule1_will_fire | rule2_will_fire | … ;
4. まとめ:2線ハンドシェークの要点
Ready (
can_fire
)- Callee が 組合せで「今受け入れられるか」を出力。
Enable (
will_fire
)- ルール単位で「ガード & 全 Ready & スケジューラ許可」を合成し、
- 1 サイクルだけ真になるパルスを自動生成。
- ファンアウト
- そのパルスを該当するすべてのメソッド/レジスタの
*_en
に扇形配線。
- そのパルスを該当するすべてのメソッド/レジスタの
- 競合解消
- 複数ルール間の静的優先度/排他はコンパイラが生成。
- ユーザ記述不要
can_fire()
以外はすべて BSC が自動で合成し、- 人手で AND/OR や MUX を書く必要はありません。
これが Bluespec SystemVerilog が内部的に繰り返す 「2 線方式ハンドシェーク」 の実装アルゴリズムです。
まず、ChatGPT に BSV の 2 線ハンドシェークを尋ねてみます。
> bluespec のモジュールでは自動的に 2 線方式のハンドシェークが行われるが、そのアルゴリズムを解説してください。
以下、Bluespec SystemVerilog がモジュール/ルール間に自動挿入する 2 線ハンドシェーク (Ready–Enable) のアルゴリズムを Producer(送り手) / Consumer(受け手) という固定役割だけで解説します。 階層が変わっても物理向きは一切反転しません。
1. ハンドシェークの基本
信号 | 物理方向 | 意味 | 生成元 |
---|---|---|---|
RDY_consumer | Consumer → Producer | 「空きがあります」通知 | Consumer が組合せで出力(!full 等) |
fire | wire | RDY_consumer & Req_producer | ルールガードが組合せで生成 |
EN_producer / EN_consumer | fire → 双方 (入力) | fire 1 クロックストローブ | bsc が扇形配線 |
DATA | Producer → Consumer | ペイロード | Producer が EN=1 サイクルで出力 |
※ RDY は常に Consumer から出る。
EN は fire を入力で受ける
(Producer が単独で EN を駆動することはありません)
2. ルール内での合成フロー
<pre><code class="language-BSV"> rule send_when_ready (prod_req && cons.rdy); // RDY_consumer を参照 cons.en(data); // fire がここで 1 クロック生成 endrule </code></pre>
fire パルス (Verilog 展開例)
<pre><code class="language-verilog"> wire fire = prod_req & cons_rdy; // 組合せ AND assign EN_producer = fire; assign EN_consumer = fire; </code></pre>
3. 複数ルールの場合
bsc が同一リソースへ向かう fire を静的解析し、 優先度ツリー/排他 OR ネットを自動生成。 競合のないルールは並列発火可能。
4. 要点まとめ
- RDY ― Consumer が組合せで出力
- fire = RDY ∧ Req_producer ― ルールガードで生成
- EN ― fire を Producer / Consumer 双方の入力に扇形配線
- 競合解消は bsc ― ユーザは AND・MUX を手書き不要
これが BSV が内部で実装する純ハード視点の 2 線 Ready-Enable ハンドシェークです。
向きは常に
RDY : Consumer → Producer
EN : fire → 双方
で固定され、階層を跨いでも反転しません。