Posts Tagged with "BSV"

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.
posted by sakurai on September 9, 2025 #1018

実際のプログラムでは最上位はConnectableにより直結しています。つまり上位のANDは生成されません。どうしたら良いか聞いてみます。

TopDirect.bsvにおいては、
rule connect; UInt#(4) x <- prod.produce; cons.consume(x); // 直ちに渡す $display("%0t: produce = %h", $time, x); recvCnt <= recvCnt + 1; endrule このようになっている。これはcallerが2つのcalleeを呼び出すので、topにおいて2つのcalleeのrdyのANDがとられ、calleeのenableとなるはず。一方で、本来のGameFSM.bsv, SoundFSM.bsvは独立にコンパイルされ、topではconnectableで直接結線されるので、ANDは出てこない。つまり、上記のrule connectをconnectableに変更しても動作させるにはcalleeをどのようにしたら良いか?

なるほど、rule connectをやめて Connectable で直結したいって話ですね。 その場合は、callee(Producer/Consumer)を標準の Get#/Put#インタフェースで公開するのが王道です。そうすれば mkConnectionが内部で「両方readyなら転送」のルールを自動生成してくれるので、トップで2つの enable を同一ルールでANDする構造から卒業できます。

方針

  • Producer:Get#(T)を出す(getガードで ready 表現)
  • Consumer:Put#(T)を受ける(putガードで ready 表現)
  • Top:mkConnection(prod.out, cons.in)で直結(rule connect不要)

具体例

Consumer 側(今の consumePut化)

import FIFO::*;
import GetPut::*;  // ← これ必要

interface Consumer4Ifc;
  interface Put#(UInt#(4)) in;   // ← 受け口をPutに
  // 必要なら状態確認用のmethodも追加可
endinterface

module mkConsumer4 (Consumer4Ifc);

  Reg#(UInt#(4)) cnt     <- mkReg(0);
  Reg#(Bool)     busy    <- mkReg(False);
  Reg#(Bool)     play    <- mkReg(False);
  Reg#(UInt#(4)) lastVal <- mkRegU;

  // timer は consume 中(=busy)は止める
  rule timer (play && !busy);
    if (cnt == 14) begin
      cnt  <= 0;
      play <= False;
    end
    else
      cnt <= cnt + 1;
  endrule

  rule clear_busy (busy);
    busy <= False;
  endrule

  // これが元の consume 相当
  interface Put in;
    method Action put(UInt#(4) x) if (!busy);
      lastVal <= x;
      $display("%0t: consume = %h", $time, x);
      busy <= True;
      play <= True;
      cnt  <= 0;
    endmethod
  endinterface
endmodule

Producer 側(produceGet化)


import GetPut::*;

interface Producer4Ifc;
  interface Get#(UInt#(4)) out;   // ← 出口をGetに
endinterface

module mkProducer4 (Producer4Ifc);
  // 例:なんらかのソースから値を出す
  Reg#(UInt#(4)) v <- mkReg(0);
  // ready 条件をガードで表現(例:常にOKならガード無しでも可)
  interface Get out;
    method ActionValue#(UInt#(4)) get;
      UInt#(4) x = v;
      v <= v + 1;
      return x;
    endmethod
  endinterface
endmodule

Top(直結)


import GetPut::*;
import Connectable::*;  // ← これ必要

module mkTopDirect(...);
  Producer4Ifc prod <- mkProducer4;
  Consumer4Ifc cons <- mkConsumer4;

  // 直結! これが rule connect の置き換え
  mkConnection(prod.out, cons.in);

  // もし recvCnt を増やしたいなら、Consumer 側でカウンタを増やすか、
  // Consumer の put 内で RWire を叩いて Top で拾うなどの手もあります。
endmodule

補足(recvCnt$displayを Top でやりたい場合)

mkConnectionの中でput/get が呼ばれるので、Top からはその瞬間が見えません。 Top で観測したいなら:

  • Consumer のput内でRWire/PulseWireに書く → Top でwgetを見てカウント・表示
  • あるいは Consumer に「通知用のメソッド」を用意して Top から呼ぶ(設計次第)

これで Top に AND は出ず、Connectableでの疎結合が成立します。 既存のconsumeを崩したくないなら、薄いラッパーで Put#(T)を包んで中で consumeを呼ぶだけでもOKです。


左矢前のブログ 次のブログ右矢

posted by sakurai on September 8, 2025 #1017

Verilogシミュレーション

次にverilogシミュレーションを実行します。

$ make vsim
bsc -verilog -bdir vobj -g mkConsumer4 -vdir rtl Consumer4.bsv
Warning: "Consumer4.bsv", line 7, column 8: (G0020)
  System functions (e.g., $display) called by interface methods execute in an
  unpredictable order during Verilog simulations.
      Top-level interface method `consume' calls a system function (e.g.,
      $display) at "Consumer4.bsv", line 34, column 7
Warning: "Consumer4.bsv", line 7, column 8: (G0117)
  Rule `consume' shadows the effects of `timer' when they execute in the same
  clock cycle. Affected method calls:
    cnt.write, play.write
  To silence this warning, use the `-no-warn-action-shadowing' flag.
Verilog file created: rtl/mkConsumer4.v
bsc -verilog -bdir vobj -u -g mkTopDirect -vdir rtl TopDirect.bsv
checking package dependencies
compiling TopDirect.bsv
code generation for mkTopDirect starts
Verilog file created: rtl/mkTopDirect.v
All packages are up to date.
bsc -verilog -bdir vobj -vsearch rtl -e mkTopDirect -o mkTopDirect.vexe
Verilog binary file created: mkTopDirect.vexe
./mkTopDirect.vexe +bscvcd=vsim.vcd +bscdumpvars
VCD info: dumpfile vsim.vcd opened for output.
5: produce = 1
5: consume = 1
105: produce = 9
105: consume = 9
205: produce = d
205: consume = d
305: produce = f
305: consume = f
405: produce = e
405: consume = e
505: produce = 7
505: consume = 7
605: produce = a
605: consume = a
705: produce = 5
705: consume = 5
805: produce = b
805: consume = b
905: produce = c
905: consume = c
=== simulation finished ===
rtl/mkTopDirect.v:115: $finish(1) called at 915 (1s)

シミュレーション波形を確認します。

$ gtkwave -A vsim.vcd

図%%.1
図1017.1 verilogシミュレーション波形

本来15サイクルかかるConsumeの動作が強制的に10サイクルでリセットされるのが確認できます。


左矢前のブログ 次のブログ右矢

posted by sakurai on September 5, 2025 #1016

Bsimシミュレーション

bsimシミュレーションを実行します。

$ make bsim
bsc -sim -bdir bsim -u -g mkTopDirect TopDirect.bsv
checking package dependencies
All packages are up to date.
cd bsim && \
    bsc -sim -bdir . -e mkTopDirect -o ../mkTopDirect.exe
/home/sakurai/src/bsv/testFSM_preemptive/bsim
Warning: Command line: (S0073)
  Duplicate directories were found in the path specified with the -p flag.
  Only the first occurrence will be used. The duplicates are:
    /home/sakurai/src/bsv/testFSM_preemptive/bsim
  Note that when the -bdir flag is used, that directory is automatically added
  to the head of the path.
Bluesim object reused: mkTopDirect.{h,o}
Bluesim object created: model_mkTopDirect.{h,o}
Simulation shared library created: ../mkTopDirect.exe.so
Simulation executable created: ../mkTopDirect.exe
./mkTopDirect.exe -V bsim.vcd
10: consume = 1
10: produce = 1
110: consume = 9
110: produce = 9
210: consume = d
210: produce = d
310: consume = f
310: produce = f
410: consume = e
410: produce = e
510: consume = 7
510: produce = 7
610: consume = a
610: produce = a
710: consume = 5
710: produce = 5
810: consume = b
810: produce = b
910: consume = c
910: produce = c
=== simulation finished ===

シミュレーション波形を確認します。

$ gtkwave -A bsim.vcd

図%%.1
図1016.1 bsimシミュレーション波形

左矢前のブログ 次のブログ右矢

posted by sakurai on September 4, 2025 #1015

ChatGPTのコードがあまり良くないので、次の考え方で整理してコードを修正します。

元の処理はbusyを内部演奏状態に使用すると同時に外部にbusyを見せていたので、プリエンプティブではなかった。この2つを分離し、playを内部演奏状態とし、外部には受取りの1サイクルのみbusyを立てるようにしてください。

以下に変更したコードを示します。

package Consumer4;

interface Consumer4Ifc;
   method Action consume(UInt#(4) x);
endinterface

module mkConsumer4 (Consumer4Ifc);

   Reg#(UInt#(4)) cnt      <- mkReg(0);     // 0-14 を数える 4bit で十分
   Reg#(Bool)     busy     <- mkReg(False); // ハンドシェーク停止用 1 サイクル
   Reg#(Bool)     play     <- mkReg(False); // 15 サイクル演奏フラグ
   Reg#(UInt#(4)) lastVal  <- mkRegU;       // 取り込んだコード
   
   /* 15 サイクル演奏タイマ */
   rule timer (play);
      if (cnt == 14) begin
         cnt  <= 0;
         play <= False;          // 演奏終了
      end
      else
         cnt <= cnt + 1;
   endrule
   
   /* busy は 1 サイクル後に自動クリア */
   rule clear_busy (busy);
      busy <= False;
   endrule

  /* 受信メソッド
      ガード if (!busy) を付けることで
      can_fire = !busy が自動生成される */
   method Action consume(UInt#(4) x) if (!busy);
      lastVal <= x;                   // EN=1 サイクルでラッチ
      $display("%0t: consume = %h", $time, x);
      busy <= True;              // 1 サイクルだけbusyを上げる
      play <= True;              // 演奏開始
      cnt  <= 0;
   endmethod
endmodule
endpackage

左矢前のブログ 次のブログ右矢

posted by sakurai on September 3, 2025 #1014

前の記事で、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中にいつでも新コードを受理し、即プリエンプション

が両立します。


左矢前のブログ 次のブログ右矢

posted by sakurai on September 2, 2025 #1013

前の記事でChatGPTの回答があたかもalways_ready, always_enable疑似命令を付加すればalways_ready, always_enableとなるように聞こえたので、確認します。

このように、属性だけ変えても中身を正しく書かないとエラーになるのでは?

always_ready / always_enabled を使うときの注意

  1. 属性の意味

    • always_ready : can_fire が常に True と解釈される
    • always_enabled: ルールが発火したら必ずメソッドも実行される
  2. メソッド本体にはガードを書かない

    • if や case で条件分岐するとコンパイルエラーになる
    • 無条件でレジスタへ書き込むだけにする
(* always_ready, always_enabled *)
method Action soundPreempt(Code_t d);
   lastCode  <= d;        // 無条件で最新コードを保持
   hasNewCmd <= True;     // 新コマンド到着フラグ
endmethod
 
  1. 忙しいかどうかの判定は別ルールで行う
rule start_play (!busy && hasNewCmd);
   current    <= lastCode;
   busy       <= True;
   hasNewCmd  <= False;
endrule
  1. 上位モジュールはガード無しで毎サイクル呼んでよい
rule send_cmd;
   sndFsm.soundPreempt(latestCode);   // guard を付けない
endrule

これで busy の間に新しいコードが来ても、旧コードは上書きされて演奏されない。プリエンプションの目的を達成できます。


左矢前のブログ 次のブログ右矢

posted by sakurai on August 29, 2025 #1012

現行のインタフェースでは、SoundFSMはGameFSMから送出されたコマンドを取りこぼすことをできるだけ防ぐためにプリエンプションを実装しています。bscのready/enableハンドシェークでそれを実装できますか?

“プリエンプション(busy 中に新コマンドで上書き)” とBSC の 自動 Ready/Enable がかみ合うか?

モデル 振る舞い 自動 Ready/Enable で実現可?
通常ハンドシェーク vld は busy=0 の時だけ出す → busy 中は転送が発生しない ✅可能(can_fire=0でルールが止まる)
プリエンプション型 busy 中でも新コマンドを“掲げ直し”
旧コマンドは破棄
⚠そのままでは不可

なぜ “そのままでは” 不可?

rule send_cmd;               // GameFSM / Producer
  soundCh.sound(code);       // ← method call
endrule

*BSCはsound.can_fireが0ならこのルールを丸ごと停止 →busy中は呼び出し自体が発生しない →Producerが「新コードを掲げ直す」ことができない

つまり自動ハンドシェークの停止機構が、 プリエンプションで必要な“上書き送出”を妨げてしまう。


解決パターンは2通り

方針 どうするか Pros/Cons
①Readyを無視する経路を別途つくる method Action soundRaw(UInt#(4) d);
always_ready, always_enabled属性を付け、Producerはbusyに関わらず書き込む
+既存自動handshake部を温存可
-衝突検証は手動で要注意
②Ready/Validを全部手書き 先に示したRawインターフェース方式
(Producerはvld保持、Consumerはrdy=0で無視)
+ロジックが明確
-BSCのcan_fire/スケジューラ恩恵を捨てる

① “バイパス” 例(片側だけ属性を変える)

(* always_ready, always_enabled *)
method Action soundPreempt(Code_t d);   // ← Ready 無視で必ず fire
  • Producer は常に soundPreempt(code)を呼ぶ
  • Consumer 側で busy なら 最新だけラッチし直せばプリエンプション完了
  • 他のメソッドは can_fire 付きのまま残せるので 競合解析は 8 割方 BSC に任せつつ 欲しい振る舞いを得られる

まとめ

  • 純粋な自動 Ready/Enable だけ では “busy 中に新コマンドで上書き” は 実現できない (ready=0 が呼び出し停止を引き起こすため)。
  • プリエンプションを行うには

    1. ready を無視する専用メソッドを立てる
    2. ハンドシェークを完全に手書き —— のいずれかで 制御を一段階エスケープ する必要がある。

    要点 BSC の自動機構は「正しく受理されること」を守るため “送り手側を止める” 動きをします。 “捨ててもいいから最新を突っ込む” というプリエンプティブ挙動は、 ready を無視する経路 を自分で設計したときのみ成立します。


左矢前のブログ 次のブログ右矢

posted by sakurai on August 28, 2025 #1011

6. Verilogシミュレーション

次にverilogシミュレーションを実行します。

$ make vsim
bsc -verilog -bdir vobj -u -g mkTopDirect -vdir rtl TopDirect.bsv
checking package dependencies
All packages are up to date.
bsc -verilog -bdir vobj -vsearch rtl -e mkTopDirect -o mkTopDirect.vexe
Verilog binary file created: mkTopDirect.vexe
./mkTopDirect.vexe +bscvcd=vsim.vcd +bscdumpvars
VCD info: dumpfile vsim.vcd opened for output.
5: produce = 1
5: consume = 1
205: produce = d
205: consume = d
405: produce = e
405: consume = e
605: produce = a
605: consume = a
805: produce = b
805: consume = b
1005: produce = 6
1005: consume = 6
1205: produce = 8
1205: consume = 8
1405: produce = 2
1405: consume = 2
1605: produce = 9
1605: consume = 9
1805: produce = f
1805: consume = f
=== simulation finished ===
rtl/mkTopDirect.v:115: $finish(1) called at 1815 (1s)

シミュレーション波形を確認します。

$ gtkwave -A vsim.vcd

図%%.1
図1011.1 verilogシミュレーション波形

verilogシミュレーションではモジュール階層は保たれており、producerが適当なタイミングで値を変更しますが、consumerは自分のタイミングでそれを受け取ります。両方のタイミングが揃った時がtopで見えているWILL_FIRE_RL_connectです。従って、1$\rightarrow$9$\rightarrow$D$\rightarrow$F$\rightarrow$EというProducerの動作に対して9, Fの取りこぼしが起きているのが確認できます。

この取りこぼしの原因は上流のスループット1/10よりも下流のスループット1/15が低い、かつバックプレッシャーをかけてないために、上流から見て3回に1回は取りこぼすことになります。


左矢前のブログ 次のブログ右矢

posted by sakurai on August 27, 2025 #1010

5. Bsimシミュレーション

次にbsimシミュレーションを実行します。

$ make bsim
bsc -sim -bdir bsim -u -g mkTopDirect TopDirect.bsv
checking package dependencies
All packages are up to date.
cd bsim && \
    bsc -sim -bdir . -e mkTopDirect -o ../mkTopDirect.exe
/home/sakurai/src/bsv/testFSM/bsim
Warning: Command line: (S0073)
  Duplicate directories were found in the path specified with the -p flag.
  Only the first occurrence will be used. The duplicates are:
    /home/sakurai/src/bsv/testFSM/bsim
  Note that when the -bdir flag is used, that directory is automatically added
  to the head of the path.
Bluesim object reused: mkTopDirect.{h,o}
Bluesim object created: model_mkTopDirect.{h,o}
Simulation shared library created: ../mkTopDirect.exe.so
Simulation executable created: ../mkTopDirect.exe
./mkTopDirect.exe -V bsim.vcd
10: consume = 1
10: produce = 1
210: consume = d
210: produce = d
410: consume = e
410: produce = e
610: consume = a
610: produce = a
810: consume = b
810: produce = b
1010: consume = 6
1010: produce = 6
1210: consume = 8
1210: produce = 8
1410: consume = 2
1410: produce = 2
1610: consume = 9
1610: produce = 9
1810: consume = f
1810: produce = f
=== simulation finished ===

シミュレーション波形を確認します。

$ gtkwave -A bsim.vcd

図%%.1
図1010.1 bsimシミュレーション波形

bsimシミュレーションではtop/producer/consumerのモジュール階層は存在せず、最適化が図られています。受け渡しする信号xは内部信号となるため、使用されていなければ削除されてしまいます。ここではcons_Lastvalとしてオレンジ色にて波形が表示されています。


左矢前のブログ 次のブログ右矢

posted by sakurai on August 25, 2025 #1008

4. Makefile

次にMakefileのサンプルを提供します。

########################################
#  Makefile(サブ→トップ→リンク:.v→.v 依存で必ず階層化)
########################################

BSC         := bsc
SIMFLAGS    := -sim
VLOG_FLAGS  := -verilog

# -------- トップとソース ---------------------------------
TOP_MOD   := mkTopDirect
TOP_SRC   := TopDirect.bsv
SRCS      := Producer4.bsv Consumer4.bsv OneStage.bsv $(TOP_SRC)

# -------- サブ(mk付き=モジュール名)---------------------
SUB_MODS  := mkProducer4 mkConsumer4

# -------- 生成物/ディレクトリ ---------------------------
RTL_DIR   := rtl
BSIM_DIR  := bsim
VOBJ_DIR  := vobj

BA        := $(TOP_MOD).ba
SIM_EXE   := $(TOP_MOD).exe
V_EXE     := $(TOP_MOD).vexe

TOP_V     := $(RTL_DIR)/$(TOP_MOD).v                       # rtl/mkTopDirect.v
SUB_V     := $(SUB_MODS:%=$(RTL_DIR)/%.v)                  # rtl/mkProducer4.v など

BVCD      := bsim.vcd
VVCD      := vsim.vcd

# -------- デフォルト -------------------------------------
.PHONY: all
all: $(SIM_EXE) $(V_EXE)

############################################################################
# Bluesim(トップ一発)
############################################################################
$(BSIM_DIR)/%.ba : %.bsv
    @mkdir -p $(BSIM_DIR)
    $(BSC) $(SIMFLAGS) -bdir $(BSIM_DIR) -u -g $(basename $<) $<

$(BA): $(SRCS)
    @mkdir -p $(BSIM_DIR)
    $(BSC) $(SIMFLAGS) -bdir $(BSIM_DIR) -u -g $(TOP_MOD) $(TOP_SRC)

$(SIM_EXE): $(BA)
    @mkdir -p $(BSIM_DIR)
    cd $(BSIM_DIR) && \
        $(BSC) $(SIMFLAGS) -bdir . -e $(TOP_MOD) -o ../$@

.PHONY: bsim
bsim: $(SIM_EXE)
    ./$(SIM_EXE) -V $(BVCD)

.PHONY: bwave
bwave: bsim
    gtkwave -A $(BVCD)

############################################################################
# Verilog RTL(.v→.v 依存で順序を強制し、階層を固定)
############################################################################
# 1) サブ .v を個別に生成(必ず mkX.v を出す)。依存解決のため -u を付与
$(RTL_DIR)/mk%.v: %.bsv
    @mkdir -p $(RTL_DIR) $(VOBJ_DIR)
    $(BSC) $(VLOG_FLAGS) -u -bdir $(VOBJ_DIR) -g mk$* -vdir $(RTL_DIR) $<

# 2) トップ .v は **サブ .v に依存**(= サブ→トップの順序を強制)
#    従来挙動どおり -u を付与(同一 bdir の .bo を参照)
$(TOP_V): $(SUB_V) $(TOP_SRC)
    @mkdir -p $(RTL_DIR) $(VOBJ_DIR)
    $(BSC) $(VLOG_FLAGS) -u -bdir $(VOBJ_DIR) -g $(TOP_MOD) -vdir $(RTL_DIR) $(TOP_SRC)

# 3) vexe はトップとサブを兄弟依存でリンク
$(V_EXE): $(TOP_V) $(SUB_V)
    @mkdir -p $(VOBJ_DIR)
    $(BSC) $(VLOG_FLAGS) -bdir $(VOBJ_DIR) -vsearch $(RTL_DIR) -e $(TOP_MOD) -o $@

# 並列実行時の順序ゆらぎを抑止(任意だが推奨)
.NOTPARALLEL: $(TOP_V) $(SUB_V) vsim vwave

.PHONY: vsim
vsim: $(V_EXE)
    ./$(V_EXE) +bscvcd=$(VVCD) +bscdumpvars

.PHONY: vwave
vwave: vsim
    gtkwave -A $(VVCD)

############################################################################
# クリーン
############################################################################
.PHONY: clean
clean:
    rm -f *.ba *.bo *.o *.exe *.vexe *.cxx *.h *.vcd core *~ *.so
    rm -f $(SUB_V) $(TOP_V)
    rm -rf $(RTL_DIR) $(BSIM_DIR) $(VOBJ_DIR)

左矢前のブログ 次のブログ右矢


ページ: