case文の制御
一口に合成制約といってもいくつかのレベルがある。
(1) RTL記述と合成結果とでロジック(シミュレーションの動作)が変わってしまう合成制約
(2) ロジックは等価だがFFの個数や位置や構成を変える合成制約
(3) ロジックもFFも変えないが、FPGAの専用ハードウェアへの割り当てを変える合成制約
このうち(3)はFPGAのアーキテクチャに依存する合成制約で、次章で説明する。(2)は論理合成ツールに任せておけばよいように思えるが、クロック非同期な部分だけは論理合成ツールにおまかせにすると不都合があるので、合成制約をRTL記述に明示的に書いた方がよい。これは次項以降で説明する。
◇
(1)は本来RTL記述で表現すべきものであり使用してはいけない。といってもこのような合成制約はcase文の制御をするparallel_case,full_caseぐらいで、旧いコードにこれが含まれていたら修正すればよい。
例えば、条件信号sel[2:0]のどれか1ビットしか1にならず、1になったビットに対応したデータを出力するセレクタはparallel_case,full_caseを使うと次のように書ける。
× assign dout = selector(.sel(sel),.d0(d0),.d1(d1),.d2(d2)); function[7:0] selector (input[2:0] sel ,input[7:0] d0 ,input[7:0] d1 ,input[7:0] d2 ); casez(sel) // synthesis parallel_case,full_case 3'b??1: dout = d0; 3'b?1?: dout = d1; 3'b1??: dout = d2; endcase endfunction |
合成制約のparallel_caseは条件がどれか1つしか成り立たない(sel=3'b101などはありえない)としてよいという意味、full_caseはどの条件も成り立たないとき(sel=3'b000)は出力はdon't careとしてよいという意味である。しかしシミュレーションでは最初に成り立った条件だけが実行されるので、この記述は優先度つきと同じ動作をする。またどの条件も成り立たないときは現在の値が保持されるので単なる組合せ回路でなくラッチと同じ動作をする。したがって正しくは次のように書かなければならない。
○ 優先度つきの場合 assign dout = selector(.sel(sel),.d0(d0),.d1(d1),.d2(d2)); function[7:0] selector (input[2:0] sel ,input[7:0] d0 ,input[7:0] d1 ,input[7:0] d2 ); casez(sel) 3'b??1: dout = d0; 3'b?10: dout = d1; 3'b100: dout = d2; default:dout = 8'bx; endcase endfunction |
優先度を付けたくなければ明示的にセレクタを書く。
○ selのどれか1ビットしか1にならない場合 assign dout = {8{sel[0]}} & d0[7:0] | {8{sel[1]}} & d1[7:0] | {8{sel[2]}} & d2[7:0]; |