1 ビットずつ商を計算する符号無/有の 32 ビット除算。 現段階ではひとまず完了(19971213)
簡単な仕様を以下に示す。
整数と浮動小数点の除算は、丸めの問題等で少し芸風が違うので、やはり整数 に絞って考えることにする。
まずは符号なしから。
module div_encodea(b,enca);
input [31:0] b;
output [4:0] enca; // 0 ~ 31
module div_encode(b,encb);
input [31:0] b;
output [4:0] encb; // 0 ~ 31
当初は一つのモジュールだったが、一つにすると何故か倍くらい遅い回路が生 成されてしまうので、別々にすることにした。 除数 b を見て、何ビット左シフトすれば MSB が 1 になるかというビット encb を返す。この 32 ビットに対して、更に MSB に 1 ビット 0 を付加す ることで、0.5 以上 1 未満の固定小数点を作り、除算のお膳立てが揃うこと になる。
また、被除数 a も 31 - encb ビット右シフトさせる (予定)もしかしたら 32 - encb とかになるかも。 この値を enca として出力する。
除数 b を 0.5 以上 1 未満になるようにシフトする。
module div_lsft(b,encb,sfb);
input [31:0] b;
input [4:0] encb; // 0 ~ 63 ( 32 )
output [31:0] sfb;
被除数 a を encodea ビット右にシフトする。出力は 63 ビットになる。
module div_rsft(a,enca,sfa);
input [31:0] a;
input [4:0] enca; // 0 ~ 31
output [62:0] sfa;
| モジュール名 | cost prim. | 時間 (ns) | 必要 数 | 小計 prim. |
|---|---|---|---|---|
| div_encodea | 137 | 4.062 | 1 | 137 |
| div_encodeb | 129 | 3.268 | 1 | 129 |
| div_lsft | 493 | 3.350 | 1 | 493 |
| div_rsft | 586 | 3.283 | 1 | 586 |
| div_qselect_top | 163 | 0.739 | 1 | 163 |
| div_qselect | 1016 | 3.873 | 31 ? | 31496 ? |
| div_adjust | - | - | 1 | - |
| div_postsft | 491 | 3.732 | 1 | 491 |
module div_complement(in,signed,out);
input [31:0] in;
input signed;
output [31:0] out;
今回実装している除算アルゴリズムは、正数同士しか受けつけないため、 被除数を正の数に変換しておく必要がある。 除数 / 被除数について、2 の補数を取る条件は
の 2 つが同時に成立していることである。
module div_encode(in,enc);
input [31:0] in;
output [5:0] enc;
被除数 a と除数 b の両方を正規化する。整数の場合は除数のみ正規化すれば いいと思うが、浮動小数点除算のお流れで設計しているのでこのままいく。
module div_presft(in,sft,out);
input [31:0] in;
input [5:0] sft; // 0 ~ 64
output [31:0] out; // including round bit & sticky bit
module div_qselect_top(b,a,po,mo,zpo,zno);
input [32:0] b; // divisor
input [32:0] a; // plusone , minusone
output [32:0] po,mo; // sum/carry of partial remainder , output
output [31:0] zpo,zno;
module div_qselect(b,pi,mi,zpi,zni,po,mo,zpo,zno);
input [32:0] b; // divisor
input [32:0] pi,mi; // plusone , minusone
input [30:0] zpi,zni; // on-the-fly conversion
output [32:0] po,mo; // sum/carry of partial remainder , output
output [31:0] zpo,zno;
モジュール div_qselect_top では、最初の商ビットを 1 として計算している。 それ以降の商の判定は div_qselect で行う。
キャリーセーブ表現の部分剰余 pi , mi から商を判定して
を返す。
冗長表現されている部分剰余の上位 3 ビットから商を選択する。
| 部分剰余 | 対応ビット列 | 商 |
|---|---|---|
| 0.25 以上 | 0.11 0.10 0.01 | +1 |
| 0 以上 0.25 未満 | 0.00 | 0 |
| 0 未満 | 1.11 1.10 1.01 1.00 |
-1 |
部分剰余の CSA のアルゴリズムは、商が 1 、即ち減算演算を行うとき、各ビッ トの出力は以下の通り。
| 被減数 | 減数 | Borrow , Sum |
|---|---|---|
| 1 | 1 0 | 0 , 0 0 , 1 |
| 0 | 1 0 | -1 , 1 0 , 0 |
| -1 | 1 0 | -1 , 0 -1 , 1 |
商が -1 のとき、即ち加算演算のときは以下の通り
| 被加数 | 加数 | Carry , Sum |
|---|---|---|
| 1 | 1 0 | 1 , 0 1 , -1 |
| 0 | 1 0 | 1 , -1 0 , 0 |
| -1 | 1 0 | 0 , 0 0 , -1 |
また、on - the - fly 変換以下の表に基づく。
| qi | ZPi | ZNi |
|---|---|---|
| +1 | { ZPi - 1 , 1 } | { ZPi - 1 , 0 } |
| 0 | { ZPi - 1 , 0 } | { ZNi - 1 , 1 } |
| -1 | { ZNi - 1 , 1 } | { ZNi - 1 , 0 } |
本来なら名前を div_postmux にすべきだが、div_premux の登場を考慮してい なかったためこのようなややこしい名前になってしまった。 整数除算に固有か?
この操作は、この除算アルゴリズムを浮動小数点にも適用させようともくろん でいるために必要となっているステップ。
異符号同士の除算についても、まず正に変換してから実際に除算を行っているため この最後の段階で 2 の補数を取らなくてはならない。 この 2 回補数を取る操作は、オーバーヘッドが大きいように見えるが、これ をしないで、全て商選択ルーチンに押し込めてしまうと、1 回に商を選択する 時間が大きくなってしまい、かえって遅くなりそうな感じがする。なんといっ ても商を選択する回数が 32 回もあるのだから。でも頑張れば、この補数操作 は不要になって、高速な動作を行うことができるようになるはずである。
| モジュール名 | cost prim. | 時間 (ns) | 必要 数 | 小計 prim. |
|---|---|---|---|---|
| div_complement | 392 | 4.456 | 2 | 784 |
| div_encode | 146 | 3.265 | 2 | 292 |
| div_presft | 493 | 3.510 | 2 | 986 |
| ( ここまでの 組み合わせ回路 ) | 2020 | 12.238 | 1 | 2020 |
| div_qselect_top | 163 | 0.739 | 1 | 163 |
| div_qselect | 1016 | 3.873 | 31 | 31496 |
| div_premux | 535 | 3.152 | 1 | 535 |
| div_mux | 452 | 7.271 | 1 | 452 |
| div_postsft | 491 | 3.732 | 1 | 491 |
| div_signconv | 392 | 4.456 | 1 | 392 |
除算は、加減算・乗算と違って、毎サイクルオペランドを投入できないので、 ステージの割り方を考える必要がある。
こんな感じでステージを割ると、10 サイクル毎に投入が可能となる。
第 1 ステージはこんな感じ
module div_1(CLK,a,b,signed,start,
fstart,fp,fm,fzp,fzn,fconv,fdf,fnb);
input CLK;
input [31:0] a,b;
input signed;
// signed operation or unsigned operation
input start;
output fstart;
output [32:0] fp,fm;
output [31:0] fzp,fzn;
output fconv;
output [5:0] fdf;
output [32:0] fnb;
第 2 ・第 3 ステージはこんな感じ
module div_2(CLK,
fstart,fnb,fp,fm,fzp,fzn,fconv,fdf,
snb,sp,sm,szp,szn,sconv,sdf,
dnb,dp,dm,dzp,dzn,dconv,ddf);
input CLK;
input fstart;
input [32:0] fnb;
input [32:0] fp,fm;
input [31:0] fzp,fzn;
input fconv;
input [5:0] fdf;
input [32:0] snb;
input [32:0] sp,sm;
input [31:0] szp,szn;
input sconv;
input [5:0] sdf;
output [32:0] dnb;
output [32:0] dp,dm;
output [31:0] dzp,dzn;
output dconv;
output [5:0] ddf;
module div_3(CLK,nb,p31,m31,zp31,zn31,conv,df,fq,fzerod);
input CLK;
input [32:0] nb;
input [32:0] p31,m31;
input [31:0] zp31,zn31;
input conv;
input [5:0] df;
output [31:0] fq;
output fzerod; // zero divide
描いていないが、第 2 ステージの出力は、そのまま第 2 ステージの入力 ( マルチプレクサの入力 ) へフィードバックもさせている。この配線は、一つ 上の階層のモジュールで行うことにする。
| モジュール名 | cost prim. | 時間 (ns) | 必要 数 | 小計 prim. |
|---|---|---|---|---|
| div_1 | 4232 | 16.979 | 1 | 4232 |
| div_2 | 5318 | 15.021 | 1 | 5318 |
| div_3 | 1632 | 14.839 | 1 | 1632 |