等号と不等号
処理をN回繰り返すとき、ソフトウェアでは
for( i=0; i<N; i++ )
と繰り返し条件をN未満と不等号で書くが、実際にはiはゼロからカウントアップしているので
for( i=0; i!=N; i++ )
と等号の否定で書いても同じである。
ソフトウェアでは、比較処理は両辺の引き算をしてゼロに等しい(equal to zero)か負(less than zero)かをCPU内のALU回路で判定しているので、どちらにしても損得はないのだが、FPGAではその判定回路自身を合成するので、どちらにするか気にした方がよい。
◇
等号eq=(a==b)の回路を考えると、ビットごとのXOR(等しければ0)をとって全ビットが0であることを判定すればよいので
equal = ~|(a[N-1:0] ^ b[N-1:0]) |
と、ビット間のロジックはNORだけですむ。
一方、不等号 lt=(a<b)の回路を考えると、最上位ビットN-1から順に比較していかなければならないから
if(a[N-1]==0 & b[N-1]==1) lt=1; else if(a[N-2]==0 & b[N-2]==1) lt=1; ... else if(a[0]==0 & b[0]==1) lt=1; else lt=0; |
とelse ifが続いて等号よりも深い回路になることがわかる。
したがって、なるべく不等号(<,>,<=,>=)は使わずに等号(==,!=)を使う方がよい。
◇◇
FPGAにはLUTだけでなく加算器やキャリ伝搬回路もついているので、不等号を、両辺を引き算して符号を見る回路にすれば速くなる可能性がある。
実際、次の3とおりの書き方を論理合成してみた。
input[N-1:0] ain,bin; assign eq_cmp = (ain==bin)? 1'b1 : 1'b0; assign lt_cmp = (ain<bin)? 1'b1 : 1'b0; wire[N:0] sub; assign sub = ain - bin; assign lt_sub = sub[N]; // ain-bin<0ならば1 |
(1) Quartusで ArriaVにマッピングした場合
回路要素はLUTとAdder(carry伝搬回路付き)。等号eq_cmpより不等号lt_cmpの方が回路規模は大きくなり最長パスも少し長くなる。引き算による比較lt_subはLUTでなくAdderを使うので単純には比較できないが、キャリ伝搬回路だけを使うのでlt_cmpより速くなる可能性はある。
N |
eq_cmp |
lt_cmp |
lt_sub |
|||
回路規模 |
最長パス |
回路規模 |
最長パス |
回路規模 |
最長パス |
|
8 |
4 LUT |
2 LUT |
5 LUT |
3 LUT |
9 Adder |
9 carry |
16 |
7 LUT |
2 LUT |
15 LUT |
4 LUT |
17 Adder |
17 carry |
32 |
14 LUT |
3 LUT |
40 LUT |
4 LUT |
33 Adder |
33 carry |
(2) Vivadoで 7seriesにマッピングした場合
回路要素はLUTとCARRY4(carry伝搬回路)。等号eq_cmpより不等号lt_cmpの方が回路規模は大きくなり最長パスも少し長くなる。引き算による比較lt_subはlt_cmpよりCARRY4の段数が多くなっているのでかえって遅いかもしれない。
N |
eq_cmp |
lt_cmp |
lt_sub |
|||
回路規模 |
最長パス |
回路規模 |
最長パス |
回路規模 |
最長パス |
|
8 |
3 LUT |
2 LUT |
6 LUT |
1 LUT + 1 CARRY4 |
8 LUT + 3 CARRY4 |
1 LUT + 3 CARRY4 |
16 |
6 LUT + 2 CARRY4 |
1 LUT+ 2 CARRY4 |
16 LUT + 2 CARRY4 |
1 LUT + 2 CARRY4 |
16 LUT + 5 CARRY4 |
1 LUT + 5 CARRY4 |
32 |
11 LUT + 3 CARRY4 |
1 LUT+ 3 CARRY4 |
32 LUT + 4 CARRY4 |
1 LUT + 4 CARRY4 |
32 LUT + 9 CARRY4 |
1 LUT + 9 CARRY4 |