社内FPGA派遣業のお話の続き。今回はステートマシンです
ソースコードを見てたら、各設計者が一生懸命ステートの値を決めていて、モジュールによってはワンホットだったり、バイナリだったりグレイだったりとそれぞれのようです。
書いている人によるポリシーが見え隠れします。
で、このエンコードって
頑張って考えてもツール側で勝手に変えられるんじゃなかったっけ?って思って調べてみたら、
Xilinx(Vivado)はFSM_ENCODINGっていう属性があり、デフォルトでautoになっており、最適なエンコードになるそうです。多分ワンホット。
Quartusの場合は、 syn_encoding という属性、もしくはツール内のAdvancedAnalysis&SynthesisSettingのところで、State Machine Processing という項目がありそこで設定できるようでデフォルトはこちらもAutoとなっており、Alteraの場合もFPGAでAutoだとワンホットになるようです。
当然ながらそういう機能を知ってなかったようで、そのオプションを設定しているわけはなく、Autoになってました。
事実、いくつかコンパイルした結果を見てみましたが、バイナリ指定がワンホットになっている箇所がありました。
ワンホットの場合は、ビット化けとかした場合にロックするのでそうならないように!って
言われますが、これもSafe State Machineというオプションで設定できるようです。
同様な設定はXilinxにもあったはず。
ただ、QuartusではこれはデフォルトOffになってました。
自分はこのSafeStateMachineという機能は使ったこと無いので、Onにした時にどういった回路にしてくれるのか?までは把握してません。
ただ、常にOnしているとかなり余計な回路が発生するという話もあるようで、必要な箇所だけ設定するってする方が良いのかなとも思います。
で、ちょっと気になったので、勝手に作られるってことならレジスタ設定とか適当でもいいんじゃね?ってことで試してみました。
単純なパラレル・シリアル変換の回路を4つのステートで実現するステートマシン書いてみました。
RTL Viewerでみてもステートマシンって認識されると回路が出てこないんですね。
クリックしてもState Machine Viewerでの状態遷移図しかみれません。
合成後のTechnologymapViewerでようやく回路が見れますが、あれって見にくいんですよねー
そのState Machine Viewerの図ですが、シンプルな状態遷移図です
S_ADDでアドレスを出力
S_DATAでデータを出力
S_ENDはシミュレーションで見やすいようにあえてシリアル化データ出し終わりましたって出す信号用。
って感じです。
まぁこんな回路だとわざわざステートマシンで書く必要はなくただのカウンタで良いんですけどね。
某有名な製品を作ってた若かれし頃、ステートマシンが楽ね~って書いてたら、こんなんカウンタとフラグだけで書けるよ。全然ゲート量が違うでしょ。って偉い人に指摘された過去を思い出します。
で、ステートは4つなのでバイナリカウンタなら2bitですみます。
reg [1:0] STATE;
parameter S_INIT = 0,
S_ADD = 1,
S_DATA = 2,
S_END = 3;
これで合成させるとワンホットにされてしまいました。
この表の見方としては1行目のNameより右のS_*が信号名で、Nameの列にある4行のS_*がステート名のようです。
ぱっと見ワンホットじゃなく見えますが、S_INIT信号を反転したらワンホットなのでこれでいいんだと思います。
たぶんリセットで0にしたいんじゃないかなーって思います。
このあたりに書いてある内容ですかね。
これを先のオプションでUser-Encodedにすると指定した通りのバイナリになりますしレジスタも2bitになります。
ただ、リセット時はレジスタの値は0ってのは譲れないようで、
parameter S_INIT = 1,
S_ADD = 0,
S_DATA = 2,
S_END = 3;
ってあえてS_INITを0以外にしてみても
Warning (284006): Flipped 1 bits in user-encoded state machine |state|STATE
っていうWarningを吐いて意地でもS_INITを0にしてくれました。頑固ものー
で,オプションをAutoに戻して、
今度は過剰なビット数、適当なSTATE値にしてみて合成させてみると
reg [31:0] STATE;
parameter S_INIT = 5,
S_ADD = 10,
S_DATA = 20,
S_END = 30;
Warningすら吐かずまったく無視されて同じように作られました。
Auto様がそんなに偉いんなら
何も考えず適当にしちゃって!ってことで
逆にビット減らしてみました。
reg STATE;
parameter S_INIT = 1,
S_ADD = 0,
S_DATA = 2,
S_END = 3;
さすがにそれは怒られちゃうようです。
Warning (10230): Verilog HDL assignment warning at state.v(21): truncated value with size 32 to match size of target (1)
Warning (10230): Verilog HDL assignment warning at state.v(29): truncated value with size 32 to match size of target (1)
Warning (10230): Verilog HDL assignment warning at state.v(31): truncated value with size 32 to match size of target (1)
Warning (10230): Verilog HDL assignment warning at state.v(40): truncated value with size 32 to match size of target (1)
Warning (10230): Verilog HDL assignment warning at state.v(43): truncated value with size 32 to match size of target (1)
Warning (10199): Verilog HDL Case Statement warning at state.v(50): case item expression never matches the case expression
Warning (10199): Verilog HDL Case Statement warning at state.v(62): case item expression never matches the case expression
回路も1bitしか作ってくれません。
当然ちゃんと動きません。
なんや中途半端やなー
意図を組んでレジスタ増やしてくれてもえーのになぁ。
まぁステートマシンに限らず使ってないレジスタは削除する機能はあるんでその一環なんでしょうね。
なので、大きめに書いておけばいいってことでしょうか?
実際はレジスタは生成されないのでいいんでしょうが、Warningも出ないので気づかないまま終わりそうですから、ソースコードとしてはあまりよろしくは無いのかもしれませんね。
ちゃんときっちり書きましょう。
また、ワンホットで記述してあるステートの使用方法で、
(STATE==S_END)
っていう判断方法と
(STATE[3]==1’b1)
ってあるかと思います。
後者の方の書き方をするとQuartusはStateMachineとみなさなくなりました。
回路はちゃんとワンホットで合成されます。
前者の場合は、ワンホットだと同じような回路になるんですが、例えばグレイコードとかエンコード方式を変えてもStateMashineとみなされ、ちゃんとデコード回路が入って合成されます。
自分的には前者のほうがたとえワンホットで使うんであっても自由度や見た目が良いかと思うんですが、どうなんでしょうね。
これはさすがに波風たちそうですし、私もどれが最適なのかわかんないので、現部署の人たちには指摘していません。
でも、全体で何十個もあるステートマシンなのに、Quartusが認識していてStateMashineViewer出でてくるのは6個くらいなんですよね。
そんなんでいいのかなぁ。
ほんと、どういう記述が正しいのか、難しいなぁって
Verilog書きはじめて30年くらいという年数だけは長い自分ですが、よくわかんないことはまだまだあるみたいです。
あと、Quartusのオプションだけじゃなくソースコードに埋め込む合成属性についても調べたんですが、長くなったので次回にします。
コメント