FSMのエンコーディング
ステートマシンFinite State Machine(FSM)は、特に禁止しないかぎり論理合成でステートレジスタ値の割り当て(エンコーディング)が変更されてしまう。
制約 |
Quartus |
Vivado |
FSMの エンコーディング |
syn_encoding="value" value= {default|sequential |one-hot|gray|user} |
fsm_encoding="value" value= {auto|sequential |one-hot|gray|johnson} ユーザ定義どおりにするには dont_touch="true"にする |
ここで
(1) sequential: 'd0→'d1→'d2→'d3→'d0→... (カウントアップ)
(2) one-hot: 'b0001→''b0010→'b0100→'b1000→'b0001→... (常に1ビットだけが1)
(3) gray: 'b000→''b001→'b011→'b010→'b110→'b111→'b101→'b100→'b000→... (常に1ビットづつ変化)
ユーザ定義(user)の場合はRTLに書いたとおりの割り当てを保持する。おまかせ(auto,default)の場合は状態数によって選択され、数個以下ならばsequential、数10個以下ならばone-hot、それ以上はgrayとなるのが一般的のようである。
◇
このようにエンコーディングが変更されると不都合なのは、非同期リセット解除後すぐ遷移するような設計でone-hotになった場合である。
例えば図のようなone-hotの回路は、'b100→'b010→・・・と遷移するが、rstのスキューにより過渡値の'b000または'b110に遷移する可能性がある。特に'b000になった場合はFSMがフリーズしてしまう。
本来の設計としては、非同期リセット後に何か入力を待って遷移するようにするか、非同期リセットを同期リセットに変えるのがよいが、どうしてもこうしたいならば、リセット解除後の遷移が過渡値をとらないようなエンコーディングにして、reg変数に合成制約(user)を指定する。
/* FSM */ (* syn_encoding="user" *) reg[2:0] state_r; localparam IDLE=3'b000; localparam SA1 =3'b100; localparam SA2 =3'b101; localparam SA2 =3'b010; always @(posedge clk or posedge rst) begin if(rst) state_r <= IDLE; else case(state_r) IDLE: state_r <= SA1; // 非同期リセット解除後すぐ遷移する SA1: if(start_a) state_r <= SA2; SA2: state_r <= SA3; SA3: if(done_a) state_r <= IDLE; endcase end |
◇◇
もうひとつエンコーディングが変更されると不都合なのは、state_rのビットをそのままFF出力の制御信号として使っている場合である。
上記のstate_rの割り当てのとき、
if((state_r==SA1)|(state_r==SA2)) |
というロジックを、非同期転送やハザードフリーの出力として使うためFF出力であることを意図して
if(state_r[2]) // state_rのビットをそのままFF出力の制御信号として使っている |
と記述することもできるが、もしエンコーディングがone-hotにされてしまうと、信号state_r[2]はハザードの出る組合せ回路になってしまい不都合である。したがってこの場合もreg変数に合成制約(user)を指定する必要がある。
◇◇◇
FSMではステートレジスタのとりうる値が決まっているので、case文のdefault項は書いても実行されないデッドコードになり、合成ツールでも無視される。もし、α線とかでFFが反転して未定義値になったときのためにdefault項を書きたいのであれば、合成ツールのオプションで指定する必要がある。
Quartusの場合は制約ファイル(.qsf)に書く。
set_global_assignment -name safe_state_machine on set_instance_assignment -name safe_state_machine on -to (instance_name) |
Vivadoの場合は次のように書く。
(* fsm_safe_state = "default_state" *) reg[2:0] state_r; |