Posts Tagged with "AI"

既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.
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)

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

posted by sakurai on August 8, 2025 #1007

3. トップモジュール

次にトップモジュールのサンプルを提供します。

package TopDirect;
import Producer4::*;
import Consumer4::*;

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

   /* 10 回受信したらシミュレーション終了 */
   Reg#(UInt#(5)) recvCnt <- mkReg(0);

   rule connect;
      UInt#(4) x <- prod.produce;
      cons.consume(x);                  // 直ちに渡す
      $display("%0t: produce = %h", $time, x);
      recvCnt <= recvCnt + 1;
   endrule

   /* 受信 10 回で $finish */
   rule finish (recvCnt == 10);
      $display("=== simulation finished ===");
      $finish;
   endrule
endmodule
endpackage

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

posted by sakurai on August 7, 2025 #1006

2. Consumer

次にコンシューマのサンプルを提供します。

package Consumer4;

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

module mkConsumer4 (Consumer4Ifc);

   Reg#(UInt#(8)) cnt  <- mkReg(0);
   Reg#(Bool)     busy <- mkReg(False);
   Reg#(UInt#(4)) lastVal <- mkRegU;   // 取り込み結果を保持

   rule timer (busy);
      if (cnt == 14) begin
         cnt  <= 0;
         busy <= False;
      end
      else
         cnt <= cnt + 1;
   endrule

   method Action consume(UInt#(4) x) if (!busy);
      lastVal <= x;                   // EN=1 サイクルでラッチ
      $display("%0t: consume = %h", $time, x);
      busy <= True;
   endmethod
endmodule
endpackage


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

posted by sakurai on August 5, 2025 #1004

それではダイレクト制御(途中にバッファを置かずに直接2線ハンドシェイクを行う)のサンプルを作成してください。

1. Producer

それではプロデューサのサンプルを提供します。

/* Producer4.bsv : 4bit データを 10 サイクルごとに出すだけ */
package Producer4;
import LFSR :: *;

interface Producer4Ifc;
   method ActionValue#(UInt#(4)) produce();
endinterface

module mkProducer4 (Producer4Ifc);
   Reg#(UInt#(7)) cnt <- mkReg(0);   // 0–
   LFSR#(Bit#(4)) lfsr <- mkLFSR_4;
   
   rule tick;
      cnt <= (cnt==9)?0:cnt+1;
   endrule
   rule random (cnt == 4);
      lfsr.next();              // ② LFSR を 1-step 進める
   endrule

   method ActionValue#(UInt#(4)) produce() if (cnt==0);
      Bit#(4) v = lfsr.value;   // ① 今の値を読む
      return unpack(v);         // ③ 読み取った値を返す
   endmethod
endmodule
endpackage


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

posted by sakurai on August 4, 2025 #1003

それでは実際のソース、GameFSM.bsv, OneStage.bsv, SoundFSM.bsvを提供します。これらはそれぞれゲームFSM、中間バッファ、サウンドFSMのソースです。両FSMを直結できるかどうか調査して、できるのであれば、ソースを修正してください。


目的

現在の設計では、GameFSMとSoundFSMの間にOneStageという中間モジュールが存在しています。これはFIFOではなく、コマンド受け渡しのbusyフラグを持つ調停回路です。 これを 完全に排除し、BSV の Ready/Enable による自動ハンドシェークだけで同期をとる構成に書き換えます。

Before / After 構成

現在(Before)

GameFSM ──► OneStage ──► SoundFSM

  • 実データは GameFSM 側の inscode レジスタに保持
  • OneStage は empty/wr_en/rd_en による制御信号だけを調停している

変更後(After)

GameFSM ──► SoundFSM × 4ch

  • 余計な 1 cycle が削減
  • BSC が各 sound() 呼び出しに Ready/Enable を自動合成

修正方針(概要)

修正対象ファイル 修正内容
OneStage.bsv 完全に削除 (または mkTop から除外)
GameFSM.bsv OneStage 接続を削除し、Vector#(4, FSM_ifc) を受けるよう改修
SoundFSM.bsv 変更不要(既にmethod sound(Code_t)をエクスポート)
mkTop.bsv Vector を生成し、そのまま GameFSM へ 1引数で渡す

修正内容の詳細

1. OneStage.bsv を削除

このファイルは完全に不要になるため

2. GameFSM.bsv の修正

(A) インターフェースを Vector 版へ

interface GameFSM_ifc;
  method Action step();
  interface Vector#(4, FSM_ifc#(Code_t)) soundCh; // ★ 追加
endinterface
-module invader_move(GameFSM_ifc /*AUTOARG*/);
+module invader_move #(Vector#(4, FSM_ifc#(Code_t)) ch) (GameFSM_ifc);

(B) 不要レジスタ削除

- Wire#(Bool)       fempty <- mkWire;
- Reg#(Bool)        fwen   <- mkReg(False);
- Reg#(UInt#(4))    inscode<- mkRegU;

(C) sound() ではなく 1 行呼び出し

rule send_sound;
  UInt#(2) idx = truncate(nextCode);   // ch[0]~[3] を選択
  ch[idx].sound(nextCode);            // BSC が ready/enable を自動合成
  nextCode <= nextCode + 1;
endrule

3. SoundFSM.bsv の変更は不要

既に以下のようにインターフェースが定義されています:

interface FSM_ifc;
   method Action sound(Code_t code);
endinterface

module mkSoundFSM0(FSM_ifc);

4. トップモジュール(mkTop.bsv)の修正

module mkTop ();
  Vector#(4, FSM_ifc#(Code_t)) sc <- replicateM(mkSoundFSM0); // または個別生成
  sc[1] <- mkSoundFSM1();
  sc[2] <- mkSoundFSM2();
  sc[3] <- mkSoundFSM3();

  GameFSM_ifc gfsm <- mkGameFSM(sc);  // ← 1 引数で OK
endmodule

メリット

  • OneStage 削除 → 余計な 1 cycle 遅延を解消
  • 自前ハンドシェーク (fempty, fwen) ロジック消失 → 記述量削減
  • 音チャンネルが Vector に集約 → 拡張時もTopと呼び出し構文は不変
  • Ready/Enable は BSC が自動生成 → 衝突・競合を静的解析で保証

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


ページ: