Posts Tagged with "BSV"

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

Pongの開発 (8)

posted by sakurai on November 30, 2023 #706

設計段階でVRの操作範囲(開始点、可変範囲)をパラメータ化して回路設計しましたが、現実には実際にゲームをして値を決定します。

そこで例によってChatGPTのBSV Expert君と対話してみます。これは設計開始時点からの会話です。

You

電圧が0.2Vから0.94Vまでの信号を、入力電圧0Vから1Vまでを12bit ADCで測定します。ADCの結果はどの範囲となりますか?

Bluespec Expert
0.2V から 0.94V の電圧範囲は、12ビットのADCでは約819から3850の値に相当します。これは、0Vから1Vの範囲を12ビットで測定するとき、各ビットが約0.244mVを表すことに基づいています。したがって、測定される電圧範囲はADCの値として819から3850の範囲になるでしょう。

You

正解です。その入力電圧はVR(可変抵抗器)のツマミを回すことで発生します。VRの最大回転角度は300°です。ここで、VRのツマミをA°からA+B°まで回すときに先ほどの入力電圧が発生すると、得られるADCの値は何から何までですか?

Bluespec Expert

  1. 角度から電圧への変換:

    • 可変抵抗器のツマミが最大に回されたとき、0.2Vから0.94Vの範囲をカバーします。
    • したがって、可変抵抗器の最大回転角度300°に対応して、電圧範囲は0.74V(0.94V - 0.2V)になります。
  2. 角度 A と A+B に対応する電圧:

    • 角度 A に対応する電圧は 0.2 + (A / 300 * 0.74) となります。
    • 角度 A+B に対応する電圧は 0.2 + ((A+B) / 300 * 0.74) となります。
  3. 電圧からADC値への変換:

    • 12ビットのADCでは、1Vに対して4096の値が割り当てられます。
    • したがって、電圧 V に対応するADC値は V * 4096 です。
  4. ADC値の範囲:

    • 角度 A に対応するADC値は (0.2 + (A / 300 * 0.74)) * 4096 となります。
    • 角度 A+B に対応するADC値は (0.2 + ((A+B) / 300 * 0.74)) * 4096 となります。

これにより、可変抵抗器のツマミを任意の角度 A から A+B まで回したときに得られるADCの値の範囲を計算できます。


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

Cmod A7の利用 (8)

posted by sakurai on November 29, 2023 #705

レベルダイアグラム

前稿のレベルダイアグラムを変数を用いて書き直したものが図705.1です。これに従いデータ変換器をBSVで開発します。ただし、前稿ではVRの開始角と使用角度範囲を105°, 90°と仮決めしましたが、試行錯誤で変更することを見越してパラメータ化したRTLとします。

図%%.1
図705.1 レベルダイアグラム

  • VRの全角度は300°
  • VRの使用角はパラメータ化し、開始角a[°]、範囲b[°]
  • VRの全角度の際のADC入力電圧はLTSpiceの結果より、0.2~0.94[V]

これらより、使用電圧は開始角の値を$V_\text{a}$、終了角の値を$V_\text{a+b}$として、

  • $V_\text{L}=0.2$, $V_\text{H}=0.94$
  • $V_\text{range}=V_\text{H}-V_\text{L}=0.74$
  • $V_\text{a}=\frac{V_\text{range}}{300}a+V_\text{L}$
  • $V_\text{a+b}=\frac{V_\text{range}}{300}(a+b)+V_\text{L}$

次にAD変換後のデータDは入力全範囲0~1[V]を4096分割する。開始角の値を$D_\text{a}$、終了角の値を$D_\text{a+b}$として

  • $D_\text{a}=4096V_\text{a}=\frac{4096V_\text{range}}{300}a+4096V_\text{L}=10.1a+819.2$
  • $D_\text{a+b}=4096V_\text{a+b}=10.1(a+b)+819.2$
  • $D_\text{range}=10.1b$

一方、y座標の制約は以下のとおりであり、上限$y_\text{top}$と下限$y_\text{bottom}$の値でクリッピングが必要。

  • $y_\text{bottom}=44$, $y_\text{top}=186$
  • $y_\text{range}=y_\text{top}-y_\text{bottom}=142$

これらからy座標を求めると、ADCのデータを$D$とすれば、

  • $y=\frac{y_\text{range}}{D_\text{range}}(D-D_\text{a})+y_\text{bottom}=\frac{142}{10.1b}D-\frac{142}{b}a-\frac{142\cdot 819.2}{10.1b}+44\\ =\frac{224.9}{b\ll4}D-\frac{142}{b}a-\frac{11514}{b}+44=\frac{225D-2272a-184216}{b\ll4}+44$

y式中のシフトは固定小数点演算を行うために分母分子を16倍しているものです。さらに最小値$D_\text{a}$、最大値$D_\text{a+b}$で入力ADCデータのクリッピングを行います。

  • $D_\text{a}=10.1a+819.2=(162a+13107)\gg4$
  • $D_\text{a+b}=10.1(a+b)+819.2=(162(a+b)+13107)\gg4$

以上より、完成したBSVコードは以下のとおりです。

package FixedPointConverter;

import Vector::*; // ベクター操作のためのモジュール

interface ConverterIfc;
    method Bit#(12) convert(Bit#(12) adcValue);
endinterface

// ADCの値から座標に変換する演算器(固定小数点演算を使用)
(* synthesize, always_ready, always_enabled, no_default_clock, no_default_reset *)
module mkFixedPointConverter #(
    parameter Bit#(12) a,  // 角度の最小値
    parameter Bit#(12) b   // 角度範囲
) (ConverterIfc);

    method Bit#(12) convert(Bit#(12) adcValue);
        // パラメータの拡張
        Bit#(20) extendedA = zeroExtend(a);
        Bit#(20) extendedB = zeroExtend(b);

        // 座標の下限と上限に対応するADC値の計算
        Bit#(20) adcMinValue = (162 * extendedA + 13107) >>4;     // Min = 10.1A + 819.2
        Bit#(20) adcMaxValue = (162 * (extendedA + extendedB) + 13107) >> 4; // Max = 10.1(A+B) + 819.2
        // クリッピング処理
        Bit#(12) clippedAdcValue = (adcValue < truncate(adcMinValue)) ? truncate(adcMinValue) :
                                  (adcValue > truncate(adcMaxValue)) ? truncate(adcMaxValue) :
                                  adcValue;

          Bit#(24) coordinate = ((zeroExtend(clippedAdcValue) * 225
                               - zeroExtend(extendedA) * 2272 - 184216)
                               / zeroExtend(extendedB) >> 4) + 44;

        return truncate(coordinate);
    endmethod

endmodule
endpackage: FixedPointConverter

これをVivadoに配置した図は以下のとおりで単なる組み合わせ回路です。

図%%.2
図705.2 変換器

このモジュールをダブルクリックすると以下のパラメータ設定画面が表示されます。aとbがすでに設定されているのは、残念ながらbsvにデフォルト値の設定が無いのですが、生成されたverilogのパラメータ文を修正したものです。verilogを10進で修正したため、vivadoでも10進表示となっています。

図%%.3
図705.3 パラメータ設定画面

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

Cmod A7の利用 (7)

posted by sakurai on November 28, 2023 #704

レベルダイアグラム

アナログ回路では用いられる概念でレベルダイアグラムという概念があります。回路の各部分でどれだけのダイナミックレインジがあるかを示す図です。ここではアナログ入力についてダイナミックレインジを調べておきます。

VR角度[°] XADC入力電圧[V] XADC出力
0 0.2 820
300 0.94 3,852

使用VR角度[°] 変換器出力 画面可動域[pix]
105 1598 44
195 2498 186

  • VR回転角は0~300°
  • XADC入力は0~1.0Vであるが可変抵抗器出力のため、0.2~0.94V
  • XADCの変換後の出力はおよそ820~3852
  • 使用VR回転角は中心150°±45°=105~195°⇒調整により変更予定
  • 変換回路の出力は1598~2498⇒調整により変更予定
  • 画面の可動域(y座標)は44~186

読み出しシーケンス

読み出しだけなのでデータバスdi_in及びdwe_in入力は0とします。またアドレスはPin 15に対応するChannel 4(0x14)とし、den_inを1サイクルだけアサートします。すると変換データが準備されデータバスdo_outに出力されるので、それを読み込みます。この回路では変換終了信号eoc_outをden_inに接続しているため、連続的に変換が実行されます。

eoc_outをden_inに接続することにより、特に複雑なシーケンサを組まなくても連続的にADCが実行できるようです。

図%%.1
図704.1 XADC回路図

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

Parameterについて (4)

posted by sakurai on November 27, 2023 #703

IPインテグレータによる変更

まずパラメータを5とします。入力は10進数、16進数、2進数を受け付けますが、表示は2進数となります。最初は5回を設定したので、"0000....0101"と表示されます。ただしこれはverilog記述に依存するようで、verilogが10進であれば10進となります。

図%%.1
図703.1 パラメータ変更画面(count=5)

合成してビットストリームをFPGAにダウンロードしてボタンを押せば、以下の波形が得られます。パルスが5回出力されています。

図%%.2
図703.2 オシロスコープ画面

次にパラメータを7とすれば、"0000....111"と表示されます。

合成してビットストリームをFPGAにダウンロードしてボタンを押せば、以下の波形が得られます。パルスが7回出力されています。

図%%.3
図703.3 オシロスコープ画面
以上より、ソースを修正せずともIPインテグレータ上でパラメータを変更するだけで、合成と配置配線は必要なものの、bsvからFPGAまでパラメータの変更が一貫することが確認できました。ただしbsvはパラメータの箱だけを確保し中身は0です。つまりbsvのソース段階においてはパラメータのデフォルト値は0となり任意の値を設定することはできません。

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

Parameterについて (3)

posted by sakurai on November 24, 2023 #702

IPインテグレータによる変更

モジュールをダブルクリックすると図702.1の画面が現れ、パラメータが2進数で表示されます。これはbscがverilog中に32'b0と書いたからであり、10進で書けば10進で表示されます。

残念ながらbsvではデフォルト値を設定することはできません。モジュールの階層ではなく、その上のモジュールをインスタンスする際にはbsvでパラメータ指定が可能ですが、今回はbsvでモジュールを作成しIPインテグレータで回路を作成するので、bsvソースレベルでは不可能ということになります。

ただし前稿にもあるようにverilogではデフォルトの値が設定できるので、必要であればbscでコンパイルしたverilogを修正します。

図%%.1
図702.1 パラメータ変更画面

実機動作

このパラメータテストモジュールはパラメータで与えられた数だけパルスを出力する回路です。IPインテグレータでパラメータを変更しただけで実際にパルス数が変わるかを確認します。具体的なbsvコードの中心は以下の行です。

    repeat (unpack(pack(count))) seq
       outPulse <= True;
       outPulse <= False;
    endseq 

パラメータcountで指定された数だけパルスのON-OFFを行います。


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

Parameterについて (2)

posted by sakurai on November 23, 2023 #701

verilogにおけるparameter

次にbscを用いてverilogにコンパイルします。

$ bsc -verilog TestParam.bsv
Verilog file created: mkTestParam.v

コンパイルするとmkTestParam.vというverilogファイルが生成されます。パラメータ部分を見ると、

    :
    module mkTestParam(CLK,
                   RST_N,
                   button_flag,
                   lrclk);
     parameter [31 : 0] count = 32'b0;
    :

のように、モジュール定義のすぐ後にparameterとしてcountが定義されています。そのほかにもverilogコード内でcountは複数使用されていますが、レジスタではないので書き込みのコードは存在せず、定数として参照されています。例えば、

    assign n__h31509 = count - 32'd1 ;
    ;
    assign MUX_jj_repeat_count$write_1__VAL_1 =
             (n__h31509[15:0] == jj_repeat_count) ? 16'd0 : x__h31537 ;

のようにcount-1とjj_repeat_countが一致するかのテストを行っている回路が生成されます。repeat_countは文字通りrepeat回数の実行時の変数と思われ、終了条件としてそれがcount-1(定数)と一致するかのテストを行っているようです。

IPインテグレータによる変更

これだけであれば、結局bsvなりverilogのパラメータ部分を修正するので、ソース修正となることからdefineでもあまり変わりません。

完全に動的にはいかずとも、ソースファイルを編集することなくVivaoのIPインテグレータのブロックデザイン画面上で変更する方法があります。

まずVivadoでmkTestParam.vをソースとして読み込み、回路をインスタンシエートします。

図%%.1
図701.1 mkTestParamモジュールを配置

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

Parameterについて

posted by sakurai on November 22, 2023 #700

Parameterについて

Verilogにおいてparameterという機能があります。これは動的に定数を変更することができ便利な機能ですが、ソースを修正することからあまり使用していませんでした。

ところがIPインテグレータを使用すれば、ソースを修正することがなく再合成だけで仕様を変更することができます。今回テスト回路を作成し、BSVからFPGAまでの繋がりを確認します。

BSVにおいてのparameter

BSVリファレンスガイド5.3モジュールにパラメータの文法が書かれています。これを用いたテスト回路を作成します。

図%%.1
図700.1 BSVリファレンスガイド5.3モジュール

文法を見るとわかるようにデフォルト値を設定する機能はありません。'='記号の使用等でそれが可能であればよかったのですが。

以下に具体的なBSVコードを示します。ボタンを押すとパラメータで指定された数だけパルスを出力するFSMです。ポートリストというかメソッドリストの直前にカッコ書きで記述するようです。

import StmtFSM::*;
   
interface FSM_ifc;
   method Action button(Bool flag);
   method Bool lrclk();
endinterface

(* synthesize,always_ready,always_enabled *)
module mkTestParam #(
   parameter Int#(32) count
) (FSM_ifc);
  
   Reg#(Bool) outPulse <- mkReg(False),
              buttonf <- mkReg(True);
   //  Mainloop
   Stmt main = seq
      while(True) seq
         outPulse <= False;
         await(!buttonf);
         await(buttonf);
            repeat (unpack(pack(count))) seq
               outPulse <= True;
               outPulse <= False;
            endseq 
      endseq
   endseq;
   mkAutoFSM(main);

   method Bool lrclk();
      return outPulse;
   endmethod
   method Action button(Bool flag);
      buttonf <= flag;
   endmethod

endmodule: mkTestParam

パルスの出力回数を表すcountをパラメータ化しました。


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

Pongの開発 (4)

posted by sakurai on November 9, 2023 #693

疑似乱数生成器

ChatGPTにLFSRのアルゴリズムを持つ疑似乱数生成器を記述してもらいました。以下の完成したモジュールはそれを手直ししたものです。

interface Randomizer_ifc;
  method ActionValue#(Bit#(1)) random_01();
endinterface

//(* synthesize *) モジュールをインライン化するためコメントアウト
module mkRandomizer(Randomizer_ifc);
    Reg#(Bit#(16)) lfsr <- mkReg(16'hACE1); // 適当な非ゼロの初期値

  method ActionValue#(Bit#(1)) random_01();
       Bit#(1) newBit = lfsr[15] ^ lfsr[13] ^ lfsr[12] ^ lfsr[10];
       lfsr <= {lfsr[14:0], newBit};
       return lfsr[15];
  endmethod

endmodule

ActionValueメソッドの呼び出し方

作成した疑似乱数生成器の呼び出しが少々難しかったのでまとめておきます。BSVにおいてはモジュールインタフェース内に記述されるメソッドの型は

  • Value Method
  • ActionValue Method
  • Action Method

の3種類があります。それぞれ入力、入出力、出力ポートに対応しますが、ActionValueの呼び出し方に少々困難がありました。単純にメソッドを変数に入れることができないためです。

特にFSMを構成するseqブロック内で、あるレジスタwにValueメソッドの戻り値を代入するだけなら、

seq
   :
   w <= random_01();
   :
endseq

等とすれば良いのですが、この場合のrandom関数は内部状態を持ち、それが呼び出しにより更新されるという副作用を持つため、ActionValueメソッドとして呼び出します。この呼び出し法が少々難しく、"<-"を用いてインスタンスした上で、かつ単純にseqの中で呼ぶことはできず、actionブロックを構成してその中でのみ有効な値となります。

実例を挙げると、

import Randomizer::*;
Randomizer_ifc randomizer <- mkRandomizer;
   :
    seq
        action
   :
           Bit#(1) w <- randomizer.random_01();
   :
        endaction
    endseq

のように、actionブロックの中で"<-"を用いて関数を呼び出します。特にactionブロックを構成することになかなか気づきませんでした。

追記:
この場合の変数wはaction - endactionのスコープ内でしか有効でないことが後日判明したので、それを回避する方法を追記します。action - endactionのスコープ内でコピー変数にコピーするだけです。

    Reg#(Bit#(1)) ww <- mkRegU; // wをactionスコープの外で使用するコピー変数
   :
    seq
        action
   :
           let w <- randomizer.random_01();  // Bit#(1)宣言でなくてletでも良い
           ww <= w;
   :
        endaction
    endseq

なお、関数がマルチサイクルの場合はこれでうまく行かない場合もあり、その場合はruleの中に入れます。


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

Pongの開発 (3)

posted by sakurai on November 8, 2023 #692

ブロック図

以下にPongのデモ画面が動作するステートマシンを組み込んだブロック図を示します。

図%%.1
図692.1 Pongブロック図

これらのモジュールのうち、クロック系、デュアルポートRAMを含むグラフィクス系、1チャネルのサウンド系はほぼ流用です。新規設計はGameFSMのみであり、GameFSMとSoundFSMを連結するコマンドバッファもそのまま流用しています。 

完成画面

システムが動作している画面を示します。

図%%.2
図692.2 Pong動作画面

方向制御

パドルの縦位置はボールと同じにしてあるため、必ずボールは打ち返します。パドルでの反射は表692.1のとおり。

表692.1 2種類の乱数とボールの方向
乱数1 bcount 乱数2 dy
0 0 (45°) 0 (dx=1に対して)+1
1 (dx=1に対して)-1
1 3 (18.4°) 0 (dx=3に対して)+1
1 (dx=3に対して)-1

乱数1で傾きの逆数であるbcountを0または3とします。乱数2で方向がプラス+1かマイナスかを決定します。合わせると、bcount=0は右方向の場合速度ベクトルが(+1, +1)または(+1, -1)です。bcount=3の場合速度ベクトルが(+1, +1/3)または(+1, -1/3)です。

オリジナルゲームにはまっすぐ反射する反射もあったのですが、まっすぐ反射してもあまり面白くないので、カットしました。

上下の壁にボールが衝突するとy方向の速度dyの符号を反転させます。一方、左右のパドルに衝突するとx方向の速度であるdxの符号を反転させ、かつ上記の表により方向をランダムに変化させます。


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

Pongの開発 (2)

posted by sakurai on November 7, 2023 #691

サウンドROMデータの作成

以下のコマンドにより、Vivadoの読めるCOEファイルを作成します。

echo 'memory_initialization_radix=16;' > srom.coe
echo -n 'memory_initialization_vector=' >> srom.coe
cat s?o.wav | \od -An -t x1 -v >> srom.coe
echo ';' >> srom.coe

サウンドステートマシン

サウンドステートマシンは以前作成したものを流用します。Space Invadersの開発の際には4多重音のため、4個のサウンドステートマシンを使用しましたが、Pongは単純なので1個で十分です。そのため自分と他人のサウンドコードを見分ける必要がないため、キューへの書き込みを示す!emptyで起動します。またミキサーも無くなるため、従来後段のミキサーに入れていた符号拡張と桁調整を本モジュールに組み込みました。

以下にソースコードを示します。コメントは一部ChatGPTにより作成してもらいました。

// 波形ファイルを読み込み、オーディオDACにサウンドデータを出力するFSMの定義

import StmtFSM.*;  // FSMを生成するためのユーティリティモジュールのインポート

// サウンドイベントと無音を表すマクロ定義
`define SOUND1    1    // 発射音
`define SOUND2    2    // パドルとの衝突音
`define SOUND3    3    // 壁との衝突音
`define SOUND4    4    // アウトの際の音
`define NULL      'h80 // 無音を表す値(8ビットPCMで中間値)

// 必要な型定義
typedef UInt#(13) Addr_t;  // メモリアドレス用の13ビット符号なし整数
typedef UInt#(8) Data_t;   // 8ビットデータ用の符号なし整数
typedef Bit#(16) Sound_t;  // 16ビット符号付PCMサウンドデータ
typedef Bit#(3) Code_t;    // サウンドコード

// FSMのインターフェース定義。外部からアクセスするためのメソッドが定義されています。
interface FSM_ifc;
   method Action sound(Code_t code);              // 音声コードを示す入力メソッド
   method Action rom_data(Data_t indata);         // ROMからのデータ入力メソッド
   method Action sync(Bool lrclk);               // 同期信号を処理するための入力メソッド
   method Action empty(Bool flag);               // FIFOが空を表す入力メソッド
   method Addr_t rom_address();                  // 現在のROMアドレスの出力メソッド
   method Sound_t sdout();                        // 音声出力データの出力メソッド
   method Bool soundon();                         // 音声が再生中かどうかを示す出力メソッド
   method Bool fifo_ren();                        // FIFOの読み出し要求の出力メソッド
endinterface

(* synthesize,always_ready,always_enabled *)
module mkSoundFSM(FSM_ifc);

// 内部ワイヤとレジスタの定義
   
Wire#(Code_t) code <- mkWire, // コードを格納するワイヤと現在のコードを保持するレジスタ
              current <- mkRegU;
Wire#(Bool) lrclk <- mkWire;  // 左右のクロック同期用のワイヤ
Reg#(Data_t) romdata <- mkRegU; // ROMから読み込まれたデータを保持するレジスタ
Reg#(Data_t) dout <- mkReg(`NULL); // データ出力用のレジスタ(初期値は無音)
Reg#(UInt#(32)) workd <- mkRegU;  // 32ビット作業用データレジスタ
Reg#(UInt#(13)) dcount <- mkRegU; // 再生カウント用の13ビットレジスタ
Reg#(Addr_t) worka <- mkRegU, // アドレス計算用の作業用アドレスレジスタ
                         romaddr <- mkRegU, // ROMのアドレスレジスタ
                         addr <- mkRegU; // 出力用アドレスレジスタ
Reg#(UInt#(8)) ii <- mkReg(0); // ループカウンタ用の8ビットレジスタ
Reg#(Bool) son <- mkReg(False), // サウンド再生中フラグ用のレジスタ
                     sonEarly <- mkReg(False), // 早期サウンド開始フラグ用のレジスタ
                     ren <- mkReg(False),  // FIFO読み込み要求フラグ用のレジスタ
                     emptyf <- mkReg(True); // FIFOが空かどうかを示すフラグ用のレジスタ

   // subfunctions
   //   READ MEM  サブ関数:メモリからの読み出し
   //     input:  worka
   //     output: romdata;
   //
   function Stmt readmem;
      return (seq
         addr <= worka;
         delay(2);
      endseq);
   endfunction

   //   READ COUNT    サブ関数:カウント読み出し
   //     input:  romaddr
   //     output: (romaddr,...,romaddr+3) => dcount;
   //             romaddr + 4 => romaddr;
   //
   function Stmt readcount;
      return (seq
         workd <= 0;
         for (ii <= 0; ii <= 3; ii <= ii + 1) seq
            worka <= romaddr + extend(3-ii);
            readmem;
            if (ii == 3) dcount <= truncate(workd<<8) | extend(romdata);
            else workd <= workd<<8 | extend(romdata);
         endseq
         romaddr <= romaddr + 4;
      endseq);
   endfunction
      
   //  Mainloop    メインループの定義
   //
   Stmt main = seq
      while(True) seq
         action
            dout <= `NULL;
            sonEarly <= False;
            son <= False;
            ren <= False;
         endaction
         await(!emptyf);
         action
            ren <= True; // consume 1 entry of Q
            current <= code;
         endaction
         await(emptyf);
         ren <= False;
         // Sync to LRCLK 
         //
         await(lrclk);
         await(!lrclk);
         delay(4);

         // Format decoding
         //
         action    
            case (current)
              `SOUND1:  romaddr <=   0 + 16;
              `SOUND2:  romaddr <=  1610 + 16;
              `SOUND3:  romaddr <=  (1610 + 900) + 16;
              `SOUND4:  romaddr <=  (1610 + 900 + 872) +16;
            endcase
         endaction
         readcount;
         romaddr <= romaddr + extend(dcount) + 4;
      
         readcount;
         romaddr <= romaddr - 1;

         // play loop
         while (dcount != 0) seq
            // Play 0
            if (sonEarly == False) seq
            // 1cycle目
               readmem;
               action
                  sonEarly <= True;
                  son <= False;
                  dout <= `NULL;
               endaction

            endseq else seq
            // 2cycle目以降
               readmem;
               action
                  son <= True;
                  dout <= romdata;
               endaction
            endseq // if

            delay(11);
            action
               romaddr <= romaddr + 1;
               worka <= romaddr + 1;
               dcount <= dcount - 1;
            endaction
         endseq // while(!終了条件)
      endseq // while(True)
   endseq; // Stmt

   mkAutoFSM(main);     // FSMを生成し実行
   
   method Action sound(Code_t incode);
      code <= incode;
   endmethod
   method Action rom_data(Data_t indata);
      romdata <= indata;
   endmethod
   method Addr_t rom_address();
      return addr;
   endmethod
   method Sound_t sdout(); 
      let bdout = pack(dout);     // 現在のオーディオデータ(dout)をパックし、16ビットのデータ(bdout)に変換します。
      let s =  ~bdout[7]; // 8ビット目(MSB)を反転させてサインビット(s)を生成します。
      return {{s,s},bdout[6:0],{7'h0}}; // オーディオデータをsignedに変換します。
   endmethod
   method Bool soundon();
      return son;
   endmethod
   method Action sync(Bool inlrclk);
      lrclk <= inlrclk;
   endmethod
   method Bool fifo_ren();
      return ren;
   endmethod
   method Action empty(Bool flag);
      emptyf <= flag;
   endmethod

endmodule: mkSoundFSM

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


ページ: