Posts Tagged with "Design"
既に発行済みのブログであっても適宜修正・追加することがあります。
We may make changes and additions to blogs already published.
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 を使うときの注意
属性の意味
- always_ready : can_fire が常に True と解釈される
- always_enabled: ルールが発火したら必ずメソッドも実行される
メソッド本体にはガードを書かない
- if や case で条件分岐するとコンパイルエラーになる
- 無条件でレジスタへ書き込むだけにする
(* always_ready, always_enabled *)
method Action soundPreempt(Code_t d);
lastCode <= d; // 無条件で最新コードを保持
hasNewCmd <= True; // 新コマンド到着フラグ
endmethod
- 忙しいかどうかの判定は別ルールで行う
rule start_play (!busy && hasNewCmd);
current <= lastCode;
busy <= True;
hasNewCmd <= False;
endrule
- 上位モジュールはガード無しで毎サイクル呼んでよい
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 に任せつつ 欲しい振る舞いを得られる
まとめ
前のブログ
次のブログ
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
図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
図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
前のブログ
次のブログ
ページ: