2012年7月7日土曜日

固定小数点演算信号処理の極意シリーズ (その7) 符号付き2進数加算法

前回は加算回路でbit幅が増えてゆくのをどう設計すればよいかを説明しました. ------乗算回路はまたこんど------

今回は、
 1)符号付き2進数の加算方法
 2)verilog code
 3)膨らんだbit幅を削減する方法
を説明します.

1)符号付き2進数の加算方法
符号なしの加算はたとえばこんなです.そんなに困ったことはないです.(0.6.0)+(0.4.0)=(0.7.0)となりますのは前回で解説したとおりです.
  36+13 = 49     100100 + 1101 = 0110001

ところが、つぎのような符号付きの加算は注意が必要です.
  (-36)+(-13)=(-49)
やってみます.
まず、-36を符号付き2進数に変換します.関数電卓で変換すりゃ簡単ですが、ここでは手でやってみます.
①+36の2進数100100に符号bitを追加する.0100100    ←(1.6.0)になった
②各bitを反転させた1011011に+1して、1011100 を得る     ←これが-36
つぎに、-13を符号付き2進数に変換します.
①+13の2進数1101に符号bitを追加する.01101    ←(1.4.0)になった
②各bitを反転させた10010に+1して、10011 を得る     ←これが-13

では加算します.
      1011100 + 10011 = 1101111
この右辺が-49になっているのかを確かめます.
③1101111の各bitを反転して、 0010000を得る
④0010000に+1して、0010001 を得る
⑤0010001を10進数に変換すると17です.つまり、上記の計算は-17という間違った答えを導き出してしまっています.
ムムッこれはイカン!   どこで間違ったんだ?
その答えは、
符号付き2進数を加算するときには、符号拡張をしなくちゃいけない
という法則があります.

符号拡張の例を示します.上で説明したマイナス数値の加算の式とbit構成はこうでした.
  式: (-36)+(-13)=(-49)            bit構成: (1.6.0) + (1.4.0) = (1.7.0)
符号付き加算回路を構成するには、出力のbit幅で入力を統一しなくちゃいけません.そういう操作を符号拡張と呼びます.つまりこうゆうbit構成にて計算します.
  式: (-36)+(-13)=(-49)            bit構成: (1.7.0) + (1.7.0) = (1.7.0)
-36の符号拡張のしかたはこうです.
     -36=1011100=(1.6.0)     →   -36=11011100=(1.7.0)
-13の符号拡張のしかたはこうです.
     -13=10011=(1.4.0)     →   -13=11110011=(1.7.0)
符号拡張でやることは、符号bitを必要なだけMSB側に並べることです.憶えましょう.
そして計算します.
11011100 + 11110011 = 11001111              bit構成: (1.7.0) + (1.7.0) = (1.7.0)
この右辺が-49になっているのかを確かめます.
③ 11001111 の各bitを反転して、 00110000を得る
④ 00110000 に+1して、 00110001 を得る
⑤ 00110001  を10進数に変換すると49です.    やったー正答を得た!

ここまでの知見を元に verilog codeを書きますと、実は意外に簡単です.
変数にS36+S13=A49と名付けしますと、
// S36 (1.6.0) + S13 (1.4.0) = A49 (1.7.0)
wire [6:0] S36;  // (1.6.0)    -36
wire [4:0] S13;  // (1.4.0)    -13
wire [7:0] A49 = {S36[6],S36[6:0]} + {S13[4],S13[4],S13[4],S13[4:0]}; // (1.7.0)  -49

赤いところが符号拡張です.符号付き加算にはこの操作が必ず必要です.かったるいですねー.
符号付き数値であっても、必ず正数だと判っているなら符号拡張は不要です.ゼロを符号拡張してもゼロですから.

 2)verilog code
前回の例題であったこの回路をverilog codeで書いてみます.赤が符号拡張です.
wire [13:0] A1;  // (1.7.6)
wire [14:0] A2;  // (1.7.7)
wire [15:0] A = {A1[13],A1[13],A1} + {A2[14],A2}; // (1.8.7)
reg [15:0] B1;   // (1.8.7)
always @(posedge clk or negedge xrst)   if(!xrst) B1<=0;   else B1<=A;
wire [14:0] B2;  // (1.7.7)
wire [16:0] B = {B1[15],B1[15:0]} + {B2[14],B2[14],B2[14:0]}; // (1.9.7)
reg [16:0] C1;   // (1.9.7)
always @(posedge clk or negedge xrst)   if(!xrst) C1<=0;   else C1<=B;
wire [14:0] C2;  // (1.7.7)
wire [16:0] C = {C1[15],C1[15:0]} + {C2[14],C2[14],C2[14:0]}; // (1.9.7)
reg [16:0] D1;   // (1.9.7)
always @(posedge clk or negedge xrst)   if(!xrst) D1<=0;   else D1<=C;
wire [13:0] D2;  // (1.7.6)
wire [17:0] D = {D1[16],D1[16:0]} + {D2[13],D2[13],D2[13],D2[13],D2[13:0]}; // (1.10.7)

↑どうですか? 意外に簡単なcodeでホッとしたんじゃないかと思います.でも、目がチカチカして間違いやすいです.固定小数点演算回路はこのようにかったるい設計なんです.

 3)膨らんだbit幅を削減する方法

すまんが長くなってしまったのでこれは次回にさしてくれ.     つぎへ       前へ

人気ブログランキングへ

0 件のコメント:

コメントを投稿