Tininess のポリシーを After Rounding から Before Rounding に変更する。 そして、バグ取り。
卒論直前にやっただけあって、変更の必要無し。
ここで特殊数とは、SNaN と QNaN と Inf と 0 を指し、入力に対してこれら が排他的にアサートされる 4 ビットで入力の状態を決定する。入力オペラン ドは 2 つなので、この部分は 8 ビットで構成される。加減算の場合は更に有 限数である 2 数の絶対値が等しい有限数かどうかを判断するビット 1 ビット を立てて、計 9 ビットで構成される。各 4 ビットについて、全てのビットが 立っていなければ、それは通常の有限数を意味する。以下の仕様に基づいて作 る。
演算種類 | 結果 | 例外 |
---|---|---|
SNaN ± SNaN SNaN ± QNaN と QNaN ± SNaN SNaN ± 0 と 0 ± SNaN SNaN ± Inf と Inf ± SNaN SNaN ± W と W ± SNaN (+Inf) + (-Inf) と (+Inf) - (+Inf) (-Inf) + (+Inf) と (-Inf) - (-Inf) | QNaN | V |
QNaN ± QNaN QNaN ± 0 と 0 ± QNaN QNaN ± Inf と Inf ± QNaN QNaN ± W と W ± QNaN | QNaN | - |
(+Inf) + (+Inf) と (+Inf) - (-Inf) (+Inf) + W と (+Inf) - W W + (+Inf) と W - (-Inf) (+Inf) + 0 と (+Inf) - 0 0 + (+Inf) と 0 - (-Inf) | +Inf | - |
(-Inf) - (+Inf) と (-Inf) + (-Inf) (-Inf) + W と (-Inf) - W W - (+Inf) と W + (-Inf) (-Inf) + 0 と (-Inf) - 0 0 - (+Inf) と 0 + (-Inf) | -Inf | - |
W + 0 と W - 0 0 + W | ( + ) W | - |
0 - W | - W | - |
(+0) + (+0) と (+0) - (-0) | +0 | - |
(+0) + (-0) と (+0) - (+0) (-0) + (+0) と (-0) - (-0) (+A) - (+A) と (+A) + (-A) (-A) - (-A) と (-A) + (+A) | RN,RZ,RP なら +0 RM なら -0 | - |
(-0) + (-0) と (-0) - (+0) | -0 | - |
(+A) + (+A) と (+A) - (-A) (-A) - (+A) と (-A) + (-A) W + Z と W - Z | ?(W,Inf,0) | ?(O,U,I) |
W , Zは、どちらも非ゼロの有限な、絶対値の異なる ( 符号付きの ) 値とす る。また、非ゼロの有限数値の絶対値を A と書いている。そして、 2 つの有 限数のオペランドの絶対値が等しいときは、W や Z ではなく A を用いて判断 を行い、A を用いるときは必ず符号をつけて書いている。ややこしいので注意。
なお、合成結果は 1707 ゲートの 10.099 ns
浮動小数点加減算のトップ階層はモジュール fp_adder であり、その構成モジュー ルは以下の通り。
モジュール名 | クロック | 下位階層 | ゲート数 | クリティカルパス[ns] |
---|---|---|---|---|
fadd_1 | 要 | 有 | 12,501 | 18.069 |
fadd_2 | 要 | 有 | 5,530 | 17.090 |
fadd_3 | 不要 | 有 | 2,785 | 7.705 |
つまんない。各ステージ毎にモジュールを割ってあるだけ。第3ステージだけ は、このまま上位階層のマルチプレクサを通してからラッチをかけるため、 組み合わせ回路になっている。他はラッチ込みの合成結果。
モジュール名 | クロック | 下位階層 | ゲート数 | クリティカルパス[ns] |
---|---|---|---|---|
fadd_split | 不要 | 無 | 198 | 1.866 |
fadd_exception | 不要 | 無 | 1587 | 10.573 |
fadd_far_1 | 要 | 有 | 3458(嘘) | 14.461(嘘) |
fadd_clo_1 | 要 | 有 | 3541 | 11.135 |
モジュール名 | クロック | 下位階層 | ゲート数 | クリティカルパス[ns] |
---|---|---|---|---|
fadd_split | 不要 | 無 | 198 | 1.866 |
fadd_exception | 不要 | 無 | 1587 | 10.573 |
fadd_far_1 | 要 | 有 | 3458(嘘) | 14.461(嘘) |
fadd_clo_1 | 要 | 有 | 3541 | 11.135 |
モジュール名 | クロック | 下位階層 | ゲート数 | クリティカルパス[ns] |
---|---|---|---|---|
fadd_split | 不要 | 無 | 198 | 1.866 |
fadd_exception | 不要 | 無 | 1587 | 10.573 |
fadd_far_1 | 要 | 有 | 3458(嘘) | 14.461(嘘) |
fadd_clo_1 | 要 | 有 | 3541 | 11.135 |
module add_expdiff(ea,eb,ls,df,be,eq); input [10:0] ea,eb; output ls; // less : ea < eb output [10:0] df; // diff : abs(ea-eb) output [10:0] be; // larger exp of ea,eb output eq; // equal : ea = eb入力オペランドの指数部 ea , eb を入力として
module add_unpack(allorea,alloreb,fa,fb,upfa,upfb); input allorea,alloreb; input [51:0] fa,fb; output [52:0] upfa,upfb; // unpacked fa/fbデノーマルの際、普通にアンパックを行うと、一番左に 0 を 1 ビット挿入す るが、ここでは、一番右に 0 を挿入している。後でシフタにかける際、この 部分がちゃんと調整されて出力される。
まず、デノーマルの時の指数の値は、Emin - 1 ( = 0 ) ではなく、Emin ( = 1 )
として考えることにする。
そして、例外パスはとりあえず無視して、このパスが取り扱うのは
桁落ちビット数 | 指数の差 ( ※ ) | 実効命令 | 担当 |
---|---|---|---|
- | ( don't care ) | ADD ( 1 ) | FAR |
( don't care ) 0 | 2 以上 1 | SUB ( 0 ) | FAR |
1以上 ( don't care ) | 1 0 | SUB ( 0 ) | CLOSE |
ちなみに、この条件の変更によって、Emin の正規化数とデノーマルの減 算で、桁落ちをするものもカバーする必要が生じた。 ( あたりまえ )
条件毎に、問題点を洗ってみる。
今のところ問題無し。
この時点では、アンパック回路を FAR path のそれと分離して、単純な回路を組むことに
メリットがある。 ( ここまで単純だともはやモジュールにする価値がない )
つまり正規化数同士の時と、小数点の位置を変更しない方がいいような気がする。
小数点を固定すれば、問題無いように見える。
この後、判断しなければならないのが、桁落ちがあったかどうかである。もし も桁落ちが起きなければ、丸めの必要性が生じるため、CLOSE path では手に 負えない。
問題なさそう。むしろこれは指数の差が 0 のカテゴリに組み入れるべ
きではないか? ( = 結果に関わらず CLOSE path を採用する )
というのは、この場合、常に丸めの必要が無いからである。
なぜこれまで悩んでいたのだろう?なんなんだいったい
module add_clo_comparexp(ea,eb,eq,og,ol,be); input [10:0] ea,eb; output eq; // equal : ea2 = eb2 output og; // one greater : ea2 = eb2 + 1 output ol; // one lesser : eb2 = ea2 + 1 output [10:0] be; // bigger exp of ea,eb
入力の指数 EA , EB から
module add_clo_adder(upfa,upfb,sa,ndga,ndgb,odga,odgb); input [52:0] upfa,upfb; // unpacked FA/FB input sa; // sign of FA output [54:0] ndga,ndgb,odga,odgb; // No/One-Differnce, Greater-FA/FB with sign ( MSB )
アンパックした仮数 ( upfa , upfb ) を元に、ビットシフトの有無と仮数の 絶対値の大小を予想して 4 通りの減算出力を返す。具体的には
CLOSE において、返す符号は以下の通り。
MSBofNDGA | eq | og | ol | Sign |
---|---|---|---|---|
( don't care ) | 0 | 0 | 1 | ! SA |
( don't care ) | 0 | 1 | 0 | SA |
0 1 | 1 | 0 | 0 | SA ! SA |
MSBofNDGA は、桁が同じで、fa > fb と仮定したときの減算結果 NDGA の MSB で、SA は入力 A の符号である。
なお、減算の結果が 0 になってしまった場合、符号は丸めモード等も考慮し
たものになり、上記の表は適用されない。
( この場合は取り扱いがややこしいので、Exception path で処理することに
している )
ここで問題が一つ発生。
指数が、最低の値とデノーマルの場合、つまりそれは Emin と Emin - 1 ( 修
正して Emin となる ) のときなのだが、そのとき上表ではうまくいかない。
( 最小指数とデノーマルとの減算で、桁落ちが無い場合に正しくない )
いろいろ対応手段は考えられるのだが、どこかで必ず帳尻を合わせなければい けないもののようなので、ここで調節してしまう。
真理値表に、仮数の MSB である MSBofFA と MSBofFB を加えて修正する。
ついでに、並列に計算した 4 通りの減算の結果のどれを選択すればいいかも 書いておく。
MSBofNDGA | MSBofFA | MSBofFB | eq | og | ol | Sign | Select |
---|---|---|---|---|---|---|---|
( don't care ) | ( don't care ) | ( don't care ) | 0 | 0 | 1 | ! SA | ODGB |
( don't care ) | ( don't care ) | ( don't care ) | 0 | 1 | 0 | SA | ODGA |
( don't care ) | 1 | 0 | 1 | 0 | 0 | SA | NDGA |
( don't care ) | 0 | 1 | 1 | 0 | 0 | ! SA | NDGB |
0 1 | 0 | 0 | 1 | 0 | 0 | SA ! SA | NDGA NDGB |
0 1 | 1 | 1 | 1 | 0 | 0 | SA ! SA | NDGA NDGB |
ここで気づく。符号の選択と、4 つの減算結果選択は、独立に行うべきではない。 4 通りの計算をした時点で、符号が決まっているので、4 通りの計算を行った ときに符号ビットを追加して、計算を選択すれば、符号も同時に計算される。
仮定した減算結果と符号の対応は以下の通り。これで結果を選択すれば正しい 符号も一緒に得ることができる。
Subtract | Sign |
---|---|
NDGA | SA |
NDGB | ~SA |
ODGA | SA |
ODGB | ~SA |
つまり、手順としては
module add_clo_mux(ndga,ndgb,odga,odgb,msbfa,msbfb,og,ol,sc,fc); input [54:0] ndga,ndgb,odga,odgb; // MSB is signbit input msbfa,msbfb; // MSBofFA , MSBofFB input og,ol; // equal is not needed by using don't care output sc; // sign of result output [53:0] fc; // mantissa of result
を設計する。入力に指数が同じかどうかという信号 eq が無いが、これは、
og = ol = 0 のときは eq が立っていると仮定して計算しているためである。
( 本当に eq だったかどうかは、次の add_clo_valid で判断する )
module add_clo_valid(msbfc,og,ol,eq,ecmd,valid); input msbfc; // MSB of FC input og,ol,eq; input ecmd; // effective command ( sub : 0 , add : 1 ) output valid; // this calc is valid / invalid
CLOSE path がカバーするオペランドの範囲は前述の通りだが、 add_clo_valid は、具体的にそれを判断する回路。改めてその条件を書けば
桁落ちがあるということは、減算結果の MSB ( MSBofFC ) が 0 ということ。
なお、 MSB が 1 でも LSB が 0 なら、丸めを行う必要がなく、 CLOSE パスでも
カバーは可能である。もし FAR で何かが起きたら使うことにするが、今のところは
LSB は使わないことにする。
( また、eq のときは、MSBofFC = 1 となるときもある ( デノーマル絡み )
が、しかし LSBofFC は必ず 0 になることに注意 )
となる。真理値表は以下の通り。
MSBofFC | og | ol | eq | ecmd | CloseValid |
---|---|---|---|---|
( don't care ) | ( don't care ) | ( don't care ) | 1 | 0 |
( don't care ) | ( don't care ) | 1 | 0 | 1 |
( don't care ) | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 0 | 1 |
1 | 1 | 0 | 0 | 0 |
module add_clo_encode(in,encode); input [53:0] in; output [5:0] encode;
ただのエンコーダ。この出力を用いて、デノーマルに落ちたときも考慮した、 シフト数を計算する。
module add_clo_adjexp(be,encode,ec,shift); input [10:0] be; // Bigger Exp input [5:0] encode; // encoded value output [10:0] ec; // result exp ( denormal -> ec = 0 ) output [5:0] shift; // shift bit
指数 be ( Bigger Exp ) とモジュール add_clo_encode の出力 encode から、
調節した指数 ec と最終段でシフトすべきビット数 shift を計算する。
なお、初段の add_clo_comparexp で、デノーマルのときの指数部の値を Emin
( = 1 ) に修正したが、ここでまたデノーマルの場合は Emin - 1 ( = 0 ) に
戻す。
be と encode の差で判断する
be - encode | ec | shift |
---|---|---|
1 以上 | be - encode | encode |
0 以下 | 0 | be - 1 |
module add_clo_shift(in,shift,out); input [53:0] in; input [5:0] shift; output [53:0] out;
入力 54 ビットのうち実際に使用するのは、 out[52:1] の 52 ビット ( けち
表現 ) で、MSB と LSB は使用しない。何も考えずに切り捨てているだけなの
で、他がきちんとしていないとここでバグる。
( もっとも、MSB を underflow の判断に使っているが )
また、CLOSE path とは直接関係無いが、EXCEPTION path と、CLOSE pathのど ちらの結果を使用するかをこの段階で決定してしまう。この段階で行う根拠は
EXCEPTION path の実行時間 < CLOSE path の実行時間 < FAR path の実行時間
という仮定に基づいている。即ち、この段階では FAR path の計算は 終了していないという仮定である。
module add_clo_excorclo( excvalid,se,ee,fe,ive,ofe,ufe,iee, sc,ec,fc,ufc, so,eo,fo,ivo,ofo,ufo,ieo); input excvalid; input se; input [10:0] ee; input [51:0] fe; // economized expression input ive,ofe,ufe,iee; // invalid , pverflow , underflow , inexact flag input sc; input [10:0] ec; input [51:0] fc; // economized expression input ufc; // invalid , pverflow , underflow , inexact flag output so; output [10:0] eo; output [51:0] fo; // economized expression output ivo,ofo,ufo,ieo; // invalid , pverflow , underflow , inexact flag以下の表に従うように解の選択をする。EXCEPTION path が有効かどうかを示 しているビット ExcValid は、最優先権を持ち、続いて CLOSE path が優先権 を持つ。そのどちらも無効であったときに初めて FAR path が有効と認めるこ とができる。
ExcValid | CloValid | Selected |
---|---|---|
1 | ( don't care ) | Exception path |
0 | 1 | CLOSE path |
0 | 0 | FAR path |
実際には、ExcValid が 1 なら EXCEPTION path の答えを返し。 そうでなければ CLOSE path の値を返す
モジュール名 | cost prim. | 時間 (ns) | 必要数 | 小計 prim. |
---|---|---|---|---|
add_clo_comparexp | 481 | 5.935 | 1 | 481 |
add_clo_adder | 2848 | 8.888 | 1 | 2848 |
add_clo_mux | 370 | 2.304 | 1 | 370 |
(ここまでの 組み合わせ回路 ) | 3518 | 10.714 | 1 | 3518 |
add_clo_valid | 4 | 0.988 | 1 | 4 |
add_clo_encode | 371 | 7.253 | 1 | 371 |
add_clo_adjexp | 247 | 5.911 | 1 | 247 |
add_clo_shift | 804 | 4.782 | 1 | 804 |
(ここまでの 組み合わせ回路 ) | 5122 | 27.915 | 1 | 5122 |
add_clo_excorclo | 209 | 1.993 | 1 | 209 |
ここでも、デノーマルの時の指数は Emin ( = 1 ) として考える。
カバーする範囲は、CLOSE path 以外のもの全て ( 例外パスも当然除く )。 具体的には
である。
module add_far_comparexp(ea,eb,be,df,ls); input [10:0] ea,eb; output [10:0] be; // bigger exp output [10:0] df; // difference : abs( ea - eb ) output ls; // less : ea < eb
入力である 2 つの指数部の値 ea , eb から
デノーマルを完全に Emin として扱うと、仮数の大小がはっきりしているにも
かかわらずわからなくなってしまうケースが発生する。
FAR path の減算は、仮数の絶対値の大小
が完全に保証されている必要があるが、入力に Emin と Emin - 1 が入ってき
たときに、次で説明する仮数のスワッパで正しさが保証されなくなる。
( そもそも、Emin - 1 -> Emin に写像変換しているため、持っている情報量
が落ちている )
これに対処するため、比較作業 ( ls の計算 ) だけについては、デノーマル でも変換することなく比較をして、 be と df に関しては、Emin - 1 を Emin に変換したとみなして計算することにする。
まずい部分は実は全部 CLOSE path がカバーしている範囲だった。でも速度的 に問題ないのでこのままにしておく。( というか戻したらむしろ遅くなった )
CLOSE path の add_clo_comparexp と、全く同じ働きをする同名の信号がある が、今のところ別々に作っている。あまりメリットは無いが、めんどくさいの で分けてある。楽。
module add_far_swapper(upfa,upfb,ls,sa,ecmd,fx,fy,sc); input [52:0] upfa,upfb; input ls; // less : ea < eb input sa; // sign of input A input ecmd; // effective command = sa^sb^cmd ( sub : 0 , add : 1 ) output [52:0] fx,fy; output sc; // sign of result
モジュール add_far_comparexp の出力 ls を用いて、アンパックした指数を スワップする。スワップされた値は fx , fy となる。
この fx と fy は、シフト後は、絶対値において fx > fy を保証するもので ある。但し 1 つ例外があって、指数フィールドの値が同じ場合はどちらが大 きいのかわからないので fx > fy は保証できない。しかし正確な大小がわか らないまま計算を続けると困るのは減算のみで、加算については、桁合わせさ えちゃんとやっておけばよい。そして減算については、そのような事態 ( 指 数が同じで fx > fy が保証されない : ハマる ) になるのは、実は全て CLOSE path の仕事で、こちらの担当ではないため、どうでもよい。
また、結果の符号 sc もついでに出力し、以下の表のように動作する。
ls | ecmd | sc |
---|---|---|
( don't care ) | 1 | sa |
0 | 0 | sa |
1 | 0 | ~sa |
module add_far_shift(fx,fy,df,ecmd,sfx,sfy,rob,stb); input [52:0] fx,fy; // fx , fy input [10:0] df; // exp difference input ecmd; // effective command ( sub : 0 ) output [53:0] sfx,sfy; // shifted fx , fy output rob; output stb;
ただのシフタ + スティッキービットの検出。次の段で、この結果を加算器に 投入するのだが、加算減算に関わらずビット操作をしないでそのままつっこめ るようにここで工夫してみる。そのためには大きい方の仮数も手を加えなけれ ばいけないのだが、どうせクリティカルパスの裏での作業なので、めんどくさ いだけでとりたてて問題は無い。
FAR path においては
が正しくできればよい。これを全て固定小数点で計算させるのは、生理的に抵 抗がある。ので、場合分けをして、加減算器に投入する際、少し加工する。
FAR path では、桁落ちが最大 1 ビットのものまで考えている。下位の桁に保 護ビットを 1 ビット付加して 54 ビットで計算を行う。これで 1 ビット桁落 ちしたときにも丸めのための情報が十分得られる
5 4 3 2 1 0 321098765432109876543210987654321098765432109876543210 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0 - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbG R S ------------------------------------------------------ ccccccccccccccccccccccccccccccccccccccccccccccccccccc R S
5 4 3 2 1 0 321098765432109876543210987654321098765432109876543210 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0 - bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbG R S ------------------------------------------------------ cccccccccccccccccccccccccccccccccccccccccccccccccccccR' S'
なお、ボロー ( 後述 ) については後で考える。
桁上がりこそすれ、桁落ちすることがないために保護ビットをつける必要がな い。そのため、 53 ビットでいいのだが、どうせ減算が 54 ビット使用するの で、アンパックした仮数の MSB に 0 を付加して 54 ビットにしてしまう。
5 4 3 2 1 0 321098765432109876543210987654321098765432109876543210 0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 0bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb R S ------------------------------------------------------ ccccccccccccccccccccccccccccccccccccccccccccccccccccc R S
5 4 3 2 1 0 321098765432109876543210987654321098765432109876543210 0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + 0bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb R S ------------------------------------------------------ cccccccccccccccccccccccccccccccccccccccccccccccccccccR' S'
結局、有効演算が加算である場合には、右シフトを 1 ビット余分に入れれば よい。このときの問題は、指数の差が Emax + 1 ( ビット列がオール 1 ) の ときの加算で、 Emax + 1 + 1 ビットシフトさせるようにシフタに指示するの だが、ビット列が全て 1 のときに 1 を加えると結果は 0 になり、シフタに は 0 ビットシフトをするような指令がでてしまう。しかしこれは、差が Emax + 1 のときは、∞か NaN なので、EXCEPTION path に任せてしまっているので、 影響は無いはずである。
module add_far_adder(x,y,roi,sti,ecmd,c0,c1,c2,roo,sto,br); input [53:0] x,y; input roi,sti; // roundbit in , stickybit in input ecmd; output [53:0] c0,c1,c2; output roo,sto; // roundbit out , stickybit out output br; // borrow bit
加減算モジュール。このとき並列に 3 つの計算を行う。加算なら
丸めという操作は、絶対値的には増えることはあっても減ることはないのに、 減算で ( X - Y - 1 ) という 1 だけ少ない値を計算させているのは、減算に ボロー ( borrow ) という概念を導入しているからである。
ボローとは、 54 ビットの減算器に引っかかっていない丸めビットとスティッ キービットを考慮したときに表われるものである。
丸めビットとスティッキービットのうち、少なくともどちらかのビットが立っ
ている場合、被減算数から 1 を借りてきて、結果を調整する必要がある。
( どちらのビットも立っていない場合は、被演算数からは、何も借りなくてよい )
例えば、54 ビットの減算 ( A - B = C ) を考える。丸めビットとスティッキー ビットを R , S と書き、今、 R と S を無視して上位ビットの減算を行い、R , S をスルーさせて結果を出したとすると
aaa.....aa0 - bbb.....bbb R S ----------- ccc.....ccc R S
となる。 R = S = 0 ならば、演算結果である c のビット列にはなんら影響は ないが、もし R = 0 , S = 1 だったときは、実際に 56 ビットに拡張して計 算すると、上位 54 ビットは、もはや C とは違う値のはずである。( その値 を仮に D とする )
aaa.....aa0 0 0 - bbb.....bbb 0 1 ----------- --- ddd.....ddd 1 1
このとき、54 ビット減算器の LSB の桁で「ボロー」が発生していて、その借 りのせいで、ビット列 d は、ビット列 c よりも 1 だけ小さい値になってい る。そして丸めビットとスティッキービットも ( 0 , 1 ) から ( 1 , 1 ) に 変化している。つまり A - B - 1 が D の答えである。 これが、 3 通りの 減算で、 X - Y - 1 を計算している理由である。
結局、ボローに関する操作は
丸めビットとスティッキービットの変換は
R | S | R' | S' |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 1 | 1 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 1 |
となる。ここまでを加減算器 add_far_adder の内部で、54 ビット加減算と並 列に行う。当然、ボローの処理は、有効演算が減算のときのみ行い、加算のときは、 そのままスルーさせる。
module add_far_round(fz_53,fz_1,fz_0,roin,stin,sib,rmode,pone,ie); input fz_53,fz_1,fz_0; // result of ADD/SUB [53],[1],[0] input roin,stin; // roundbit , stickybit input sib; // signbit of result input [1:0] rmode; // roundmode 00:RN , 01:RZ , 10:RP , 11:RM output pone; // plus one : ROUND or NOT ROUND output ie; // inexact flagこのモジュールは、 X + Y と X + Y + 1 の 2 通りの両方について、投機的 に丸めを行うために用意されている。即ちこのモジュールは 2 つ用意する。
また、このモジュールは、丸めを実際に行う ( = 加算器で +1 する ) わけで はなく、 あくまで +1 するかどうか ( 出力 pone : Plus ONE )を判定する回 路である。 +1 するかどうかがわかったら、適切に切上げ ( 実は +1 のとき と、 +2 のときがある ) された解を選択するだけでよい。
また、不正確の例外フラグ InExact も出力する ( これも後のモジュール add_far_mux1 で選択される )
ところで、ここまでのプロセスを行うことで、A + B , A + B + 1 , A + B + 2 の 3 通りを計算するだけで本当に、ボローと桁落ち ( 桁上がり ) と丸め を考慮した全ての組み合わせを満たしているかどうかについて考える。
加算の場合と減算の場合の 2 つに分けて考えるが、共通の方針は以下の 2 点 である。
例えば、加算であるならば、 A + B について丸め操作を行うべきであるし、 減算であるならば、 A - B = A + ~B + 1 について丸め操作を行うべきである
丸めを行う際、上位 53 ビットが結果となるように考えなければならない。上 位 53 ビットがどの 53 ビットになるのかというのは、演算結果の桁上がりも しくは桁落ちに依存する
減算については、ボローの有無と桁落ちの有無,、そして丸めによる切上げの 有無が選択の基準となる。
ボローが無い場合は、丸めの判断を行う回路は A - B = A + ~B + 1 である。 反対にボローがあるときの丸めは A - B - 1 = A + ~B で行う必要がある。そ して更に、丸めによる切上げがあるかどうかで最終的な解を選択する。
( 例 )
aaa.....aa0 - bbb.....bbb 0 0 ----------- (or) ccc.....ccR' 0 ^ LSB'
ボローが無いので、丸めの判断は A + ~B + 1 で行う。桁落ちがないので、54
ビット減算結果の LSB を新たに丸めビット R' とし、従来の丸めビットとス
ティッキービットの or をとったものを新たなスティッキービット S' とする。
( ちなみに、ボローが無いということは、 S' = 0 と等価である )
そして、減算結果の LSB を新たに R' とするのだから、LSB の 1 つ上位のビッ
トが丸めを行う際の新たな LSB' となる。
丸めによる切上げがなければ、解は A + ~B + 1 である。
ところで、丸めによる切上げが発生する状況について考えてみる。
丸めによる切上げは、丸めモードの種類に関わらず、丸めビットかスティッキー
ビットの少なくともどちらかが 1 でないと発生しない。
( どちらも 0 であるということは、演算の結果が正確で丸めの必要がないと
いうことである。あたりまえ )
そこで、今回のケースを考えてみると、S' = 0 なのだから、少なくとも R' =
1 でないと、丸めによる切上げは発生しない。そして丸めの切上げが起きたと
きは A - B + 2 = A + ~B + 3 を計算して LSB を切捨てることに相当する。
つまりこの操作は、LSB' ( 旧 LSB の 1 つ上位のビット ) に 1 を足せばい
い。しかし、既に書いたように、 R` ( 旧 LSB ) が 1 でなければ丸めによる
切上げは起こらないため、 A - B + 1 = A + ~B + 2 に 1 を足して LSB を切
捨てても全く同じ結果を得ることができる。
結局、このケースでは丸めによる切上げが無いときは、A + ~B + 1 を選択し、 切上げがあるときには、 A + ~B + 2 を選択すればよい。
( 例 )
aaa.....aa0 - bbb.....bbb 0 0 ----------- cc.....ccc R'S' ^ LSB' ( = LSB )
ボローがないので、A - B = A + ~B + 1 を丸めの判断に用いる。そして桁落 ちがあることから、そのまま LSB' = LSB , R' = R ( = 0 ) , S' = S ( = 0 ) が成り立つ。しかし既に述べたように、R' = S' = 0 ということは、丸めによ る切上げが発生しないことを示している。
つまりこのケースでは、丸めは一切考慮せずに、無条件に A - B = A + ~B + 1 を選択すればよい。
( 例 )
aaa.....aa0 - bbb.....bbb 0 1 ----------- (or) ccc.....ccR' S' ^ LSB'
ボローがあるので、丸めの判断には、 A - B - 1 = A + ~B を用いる。 また、桁落ちがないので今まで同様、R' = LSB , S' = R or S とする。
ここで丸めによる切上げが起きたときには、LSB' に 1 を足す必要がある。 これは、旧 LSB から見れば、2 を足すことに相当し、 A - B - 1 + 2 = A - B + 1 = A + ~B + 2 で計算が可能である。
結局、丸めによる切上げがなければ A - B - 1 = A + ~B を選択して、 切上げがあれば、 A - B -1 + 2 = A + ~B + 2 を選択すればよい。
( 例 )
aaa.....aa0 - bbb.....bbb 0 1 ----------- cc.....ccc R'S' ^ LSB' ( = LSB )
ボローがあるので、丸めの判断には、 A - B - 1 = A + ~B を用いる。 また、桁落ちがあるので、LSB' = LSB , R' = R , S' = S とする。
丸めによる切上げが起きたときには、R' を丸めて LSB' の桁で +1 を行えばよいから、 A - B - 1 + 1 = A + ~B + 1 が解となる。
つまり、この場合は丸めの切上げが無ければ A - B - 1 = A + ~B を、 切上げがあるならば A - B - 1 + 1 = A + ~B + 1 を選択すればよい。
加算は減算の場合と異なり、ボローのような丸めを判断すべき選択を行う必要 がなく、桁上がりの有無と丸めによる切上げの有無の 2 つのみがパラメータ となる。
丸めの判断は、全て A + B の結果を用いる。
( 例 )
0aa.....aaa - 0bb.....bbb R S ----------- 0cc.....ccc R'S' ^ LSB'
桁上がりがないので、LSB' = LSB , R' = R , S' = S とする。
丸めによる切上げが起きたときは、LSB' の桁 ( = 旧 LSB の桁 ) で +1 すれ ばよいから、結果は A + B + 1 となる。
即ち、丸めによる切上げが起きなかったら、A + B を、 丸めによる切上げが起きたなら、A + B + 1 を選択すればよい。
( 例 )
0aa.....aaa - 0bb.....bbb R S ----------- (or) ccc.....ccR' S' ^ LSB'
桁上がりがあるので、 R' = LSB , S' = R or S とする。
丸めによる切上げが起きたときは、LSB' の桁 ( = 旧 LSB の 1 つ上位の桁 ) で +1 すればよいから、結果は A + B + 2 となる。
即ち、丸めによる切上げが起きなかったら、A + B を、 丸めによる切上げが起きたなら、A + B + 2 を選択すればよい。
以上により、A + B , A + B + 1 , A + B + 2 で全ての場合を包括しているこ とが説明できた。この選択作業を実現したのが、モジュール add_far_mux1 で ある。
module add_far_mux1(msbc0,msbc1,pone0,pone1,ie0,ie1,br,ecmd,c0e,c1e,c2e,ief); input msbc0,msbc1; // MSB of C0 , C1 input pone0,pone1; // PlusOne of C0 , C1 ( result of Roundlogic ) input ie0,ie1; // inexact0,1 input br; // borrow input ecmd; // sub : 0 , add : 1 output c0e,c1e,c2e; // C0,1,2 Enable output ief; // inexact FAR path
3 つの加減算結果 C0 , C1 , C2 の中から以下の表に基づいて 1 つを選ぶた めの ( 冗長な ) 信号を吐く。各々の信号 C0E , C1E , C2E は、排他的にア サートされる。
MSBofC0 | PlusOne0 | MSBofC1 | PlusOne1 | Borrow | ecmd | Select | 備考 |
---|---|---|---|---|---|---|---|
d | 0 | d | d | d | 1 | C0 | 加算 切捨て |
0 | 1 | d | d | d | 1 | C1 | 加算 切上げ 桁上無 |
1 | 1 | d | d | d | 1 | C2 | 加算 切上げ 桁上有 |
d | d | d | 0 | 0 | 0 | C1 | 減算 借り無 切捨て |
d | d | 0 | 1 | 0 | 0 | ( 不可能 ) | 減算 借り無 切上 げ 桁落有 |
d | d | 1 | 1 | 0 | 0 | C2 | 減算 借り無 切上げ 桁落無 |
d | 0 | d | d | 1 | 0 | C0 | 減算 借り有 切捨て |
0 | 1 | d | d | 1 | 0 | C1 | 減算 借り有 切上げ 桁落有 |
1 | 1 | d | d | 1 | 0 | C2 | 減算 借り有 切上げ 桁落無 |
また、各々 add_far_round で計算した不正確のフラグも選択する。
Borrow | ECmd | Selected |
---|---|---|
x | 1 | InExact_0 |
1 0 | 0 | InExaqct_0 InExact_1 |
オーバーフローやアンダーフロー等も考慮した上での指数の調節を行うには、 まずオーバーフローの際の返り値を、丸めモードと結果の符号から判定しなけ ればならない。
module add_far_resultinfty(rmode,sc,ovi); input [1:0] rmode; // roundmode input sc; // sign of result output ovi;
乗算器でも同じ動作をするモジュールを用いている。結果がオーバフローした ら即 INF を返すというわけではなく、丸めモードによっては表現できる有限 数の中で最大値を返す場合もある。
加減算器に投入する前のスワップの出力である結果の符号 sc と丸めモードを 用いて、オーバーフロー時に、INF を結果とするかどうかを判定するビット OVI を返す。
この操作は、加減算と並列に行うことができる。
丸めモード | SC | OVI |
---|---|---|
RN ( 00 ) | ( don't care ) | 1 |
RZ ( 01 ) | ( don't care ) | 0 |
RP ( 10 ) | 0 1 | 1 0 |
RM( 11 ) | 0 1 | 0 1 |
module add_far_adjexp(be,ecmd,fc,ovi,ae,of,uf); input [10:0] be; // Bigger Exp ( from input ecmd; // effective cmd. SUB:0 , ADD:1 input [53:0] fc; // FC = FA + FB input ovi; // output infinity as a result if overflow output [10:0] ae; // adjusted exponent output [52:0] af; // uneconomized 53bit out output of,uf;
基本的には、浮動小数点加減算演算結果の指数は 2 つの入力オペランドのう ち大きい方の指数であるが、桁落ち等により、指数の調節が必要となる。
指数を調節しなければならない要因は 5 つある。
更にオーバーフロー時には、Emax + 1 に指数を固定するのか Emax に固定するのかを モジュール add_far_resultinfty の出力 OVI から判断する必要がある。
なお、最後の要因であるデノーマルに落ちるやつは、実は CLOSE path の担当 であり、FAR path では無視しても構わないはずである。 しかし万が一を考えて、とりあえず実装してある。
3 番目の条件は、すっかり忘れていたもので、指数の修正には、加算結果の MSB だけではなく、もう 1 ビットだけ下位のビットが更に必要となる。こい つのおかげでだいぶ表が汚くなった。
OVI | MSBsFC | ECmd | BE | AdjustExp | 例外 |
---|---|---|---|---|---|
1 0 | 1x | 1 | Emax | Emax + 1 ( =
BE + 1 ) Emax ( = BE ) | OFlow |
x | 0x | 1 | Emax | Emax ( = BE ) | - |
x | 1x 0x | 1 | Emin + 1 〜 Emax - 1 | BE + 1 BE | - |
x | 11 10 | 1 | Emin | Emin + 1 ( = BE + 1 ) | - |
x | 01 | 1 | Emin | Emin ( BE ) | - |
x | 00 | 1 | Emin | Emin - 1 ( BE - 1 ) | UFlow |
x | 1x 0x | 0 | Emin + 1 〜 Emax | BE BE - 1 | - |
x | 1x | 0 | Emin | Emin ( = BE ) | - |
x | 0x | 0 | Emin | Emin
- 1 ( BE - 1 ) [CLOSE path] | UFlow |
CLOSE path の担当、INF を返すところ、加算のデノーマルを除いて加減算の 動作は、対称になっている。
更にこの時点で、オーバーフロー等も含めた、特殊な仮数の値を採用するかどうかも 決まってしまうので、ついでに選択してしまう。 ( AF とする )
上表と違って、MSBofFC の 1 ビットだけあればよいことに注意。
OVI | MSBofFC | ECmd | BE | AdjustedF |
---|---|---|---|---|
1 0 | 1 | 1 | Emax | ALL 0 ALL 1 |
x | 1 | 1 | Emin 〜 Emax -1 | FC[53:1] |
x | 0 | 1 | x | FC[52:0] |
x | 1 0 | 0 | Emin + 1 〜 Emax | FC[53:1] FC[52:0] |
x | x | 0 | Emin | FC[53:1] |
module add_far_mux2( sc,ec,fc,ivc,ofc,ufc,iec,valec, sf,ef_0,ef_1,ef_2,c0,c1,c2,c0e,c1e,c2e,off_0,off_1,off_2,uff_0,uff_1,uff_2,ief, so,eo,fo,ivo,ofo,ufo,ieo); input sc; // sign of CLOSE input [10:0] ec; // exponenent of CLOSE path input [51:0] fc; // mantissa of CLOSE path input ivc,ofc,ufc,iec; // invalid , overflow , underflow , inexact input valec; // VALid EXCEPTION / CLOSE path input sf; // sign of FAR path input [10:0] ef_0,ef_1,ef_2; // exponenent of FAR path input [51:0] c0,c1,c2; // mantissa of FAR path ( economize expression ) input c0e,c1e,c2e; input off_0,off_1,off_2,uff_0,uff_1,uff_2,ief; // invalid , overflow , underflow and inexact(already determined) output so; output [10:0] eo; output [51:0] fo; output ivo,ofo,ufo,ieo;
最終の仮数、指数、フラグを選択する。add_far_mux1 はイネーブルの信号だ けだったが、add_far_mux2 では実際に選択を行う。 実際の回路は、複数段のマルチプレクサになるはず。
例外パスの信号もここで選択してしまうことにする。選択する信号の種類は、
ExValid | CloValid | Selected |
---|---|---|
1 | ( don't care ) | Exception path |
0 | 1 | CLOSE path |
0 | 0 | FAR path |
EXCEPTIO path か CLOSE path かのどちらかということについては、既に選択 されているものとする。
その前の段階において、更に FAR path の内部の 3 通りに結着をつけるため の、仮数の選択を行う。
C0E | C1E | C2E | FarF |
---|---|---|---|
1 | 0 | 0 | C0 |
0 | 1 | 0 | C1 |
0 | 0 | 1 | C2 |
又、各パスで起こり得る例外は以下の通り。
パス | 例外 |
---|---|
Exception | IV , OF , OF with IEX , UF? |
CLOSE | UF |
FAR | OF with IEX , UF , IEX |
FAR path のオーバーフローは、必ず InExact になるように設定してある。
モジュール名 | cost prim. | 時間 (ns) | 必要 数 | 小計 prim. |
---|---|---|---|---|
add_far_comparexp | 460 | 6.192 | 1 | 460 |
add_far_swapper | 336 | 2.940 | 1 | 336 |
add_far_shift | 3223 | 6.428 | 1 | 3223 |
(ここまでの 組み合わせ回路) | 3852 | 12.626 | 1 | 3852 |
add_far_adder | 2358 | 12.396 | 1 | 2358 |
add_far_round | 20 | 1.331 | 2 | 40 |
add_far_mux1 | 16 | 1.209 | 1 | 16 |
add_far_resultinfty | 4 | 0.733 | 1 | 4 |
add_far_adjexp | 512 | 4.388 | 3 | 1536 |
add_far_mux2 | 439 | 3.027 | 1 | 439 |
加減算をする際、
A の符号 | B の符号 | 命令 | 実効命令 |
---|---|---|---|
+ | + | ADD | ADD |
+ | - | ADD | SUB |
- | + | ADD | SUB |
- | - | ADD | ADD |
+ | + | SUB | SUB |
+ | - | SUB | ADD |
- | + | SUB | ADD |
- | - | SUB | SUB |
演算 | 結果の符号 |
A + B | Sign A |
A - B | Sign A |
B + A | Sign B |
B - A | Sign ~B |
if(exp == 0) return( { 1'b0, significand } ); ~~~~~~~~~~~~~~~~~ else return( { 1'b1, significand } );なのだが、実際は、このようにしている。
if(exp == 0) return( { significand, 1'b0 } ); ~~~~~~~~~~~~~~~~~ else return( { 1'b1, significand } );デノーマルがオペランドに存在するとき、その指数フィールドは 0 にもかか らわず、実際に示しているのは、exp = 1 と同じ指数である。( エクセス 127 なので、本当に表現しているのは -126 ) そのため、正規化数とデノーマルとの間で、指数の差を計算すると、実際の指 数の差よりも、必ず 1 だけ大きな値になってしまう。この 1 のずれは、桁合 わせの際に 1 ビット余分に右シフトしてしまうことになる。それを防止する ために、あらかじめ 1 ビット左にシフトさせてあるのが、上記のルーチンで ある。これによって、余分な 1 ビット右シフトをそのままにしておいても、 結果的に相殺することができる。(それ以外にも、ロジックが簡単になるとい う理由から、この方法を採用している。確か、デノーマル同士の加算の時に、威力を発揮したはず。)
X op Y | Y = 0 | Y = W | Y = ∞ | Y = NaN |
---|---|---|---|---|
X = 0 | (iii) | (ii) | (i) | |
X = W | (ii) | (i) | ||
X = ∞ | (ii) | (ii) | (ii) | (i) |
X = NaN | (i) | (i) | (i) | (i) |
X = NaN ? | Y = NaN ? | 結果 |
0 | 0 | d (担当外) |
0 | 1 | Y |
1 | 0 | X |
1 | 1 | X |
X = ∞ ? | Y = ∞ ? | 実効命令 = ADD ? | 結果 |
0 | 0 | d | d (担当外) |
0 | 1 | 0 | {Sign ~X,∞} |
0 | 1 | 1 | {Sign X,∞} |
1 | 0 | d | {Sign X,∞} |
1 | 1 | 0 | NaN |
1 | 1 | 1 | {Sign X,∞} |
| X | = | Y | ? | 実効命令 = ADD ? | X = 0 ? | Rmode = RM ? | 結果 |
1 | 1 | 1 | d | X |
1 | 1 | 0 | d | d (担当外) |
1 | 0 | d | 0 | +0 |
1 | 0 | d | 1 | -0 |
0 | d | d | d | d (担当外) |