9 |
BSVの設計トライアル (3) |
サウンドFSMの作成
waveフォーマット解析とデータ供給を実行する、メインのFSMの設計を行います。このような小さいFSMであっても階層設計を行います。その理由はソフトウェアと同じで、構造化のためです。最上位ステートは図236.1のようにINIT, SOUNDの2ステートとします。基本的にINITでは外部からサウンドコードが送信されてくるのを待ち、自分のコードであればSOUNDに遷移します。サウンド出力が終了すると、またINITに戻ります。
フラットなFSMではステート変数を整数で持ちますが、階層FSMでは構造体で持つことにします。階層は3階層で、上位2階層が意味のある機能を示し、最下層はステップとします。次に構造体の記述を示します。
typedef struct {
Category_t cat;
Function_t func;
Step_t step;
} State_t deriving(Bits,Eq);
上位からカテゴリー、ファンクション、ステップと名付けました。最上位のカテゴリーは先の図のように、 INIT, SOUNDの2ステートからなります。
typedef enum { INIT, SOUND } Category_t deriving(Bits,Eq);
INITでは中間階層である機能は特になく、2ステートを持ち、LRCLKと同期させます。これはFSMクロックがLRCLKよりも速いため、LRCLKと確率的に位相が合わなくなるためです。
SOUNDでは機能としてFORMAT, DATA, PLAY, READCOUNT, READMEMの5種を持ちます。それぞれの機能は、フォーマット解析、データ長取得、演奏です。さらにそこから呼ばれる共通シーケンス(サブルーチン)として、データ長読み出し、データ1バイト読み出しが存在します。
typedef enum { FORMAT, DATA, PLAY, READCOUNT, READMEM } Function_t deriving(Bits,Eq);
同階層内にコール関係があり、DATAからREADCOUNTをコールします。さらにREADCOUNTからREADMEMをコールするので、リターンスタックは2段必要です。
最下層のステップの名前には特に意味が無く、シーケンシャルにステートを進めるだけです。シーケンシャルになる理由は、フォーマット解析やROMのデータ待ちなど様々です。
typedef enum { S0, S1, S2, S3, S4, S5, S6 } Step_t deriving(Bits,Eq);
以上は宣言であり、実際のインスタンシエーションは以下のようにモジュール内部で行います。
Reg#(State_t) state <- mkReg(State_t{cat:INIT,func:?,step:S0}),
ret <- mkRegU,
ret2 <- mkRegU;
ステート構造体stateと、さらに同じ構造を持つ2本のリターンレジスタret, ret2をインスタンスしています。