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 |