16ビット符号なし整数の加算を行う最も単純なVerilog-2001のコードを以下に示します。
module unsigned_add( input [15:0] ina, inb, output [16:0] result); assign result = ina + inb; endmodule
最初ですので、Verilog-2001のモジュール定義について簡単に説明しておきます。
まず、最初の行の予約語“module”はモジュール定義の開始であることを宣言するもので、この語の後にモジュール名“unsigned_add”とポート定義(次行以下のカッコ内の部分)が続きます。
ポート定義は、入力を表す予約語“input”または出力を表す予約語“output”、ビット幅を指定するベクタ、および信号線名からなります。ここでは3つの信号線名が次のように定義されます。
信号線名 | 入出力の別 | ビット幅 | ビット番号 | |
MSB | LSB | |||
ina | 入力 | 16 | 15 | 0 |
inb | 入力 | 16 | 15 | 0 |
result | 出力 | 17 | 16 | 0 |
Verilogでは、Cなどの高級言語とは異なり、扱う信号線の幅をビット単位で指定します。ビット幅が異なる場合、不足分は上位に0が挿入され、過剰な場合は上位のビットが捨てられます。
算術演算子“+”と“-”は、高級言語と同様に加減算を指定します。高級言語と異なり、Verilogで加減算を行うとビット長が1だけ長くなります。このため、演算結果を伝える信号線のビット幅を1ビットだけ長くすることで符号なし加算によるオーバーフロー・エラーを防止することができます。
符号付整数は2の補数で扱われます。この形式は、値の順序は符号なし整数と同じであり、表現可能範囲の半分、すなわちゼロに近い正の領域では符号付整数と符号なし整数は同じビットパターンが同じ値を意味します。
符号付整数と符号なし整数の相違点は、右の図に示すように、どこでオーバーフロー・エラーが発生するかという点です。ビット長が16の場合、符号付整数は32,767(16'h7FFF)を上回った際にオーバーフロー・エラーが生じ、アンダーフロー・エラーは-32,768(16'h8000)で生じます。これに対して、符号なし整数は65,535(16'hFFFF)以上でオーバーフロー・エラーを生じ、0(16'h0000)以下となったときにアンダーフロー・エラーが生じます。
符号付整数は正負でおおむね対称となっているため、以下にコードを示すように演算結果のビット長を入力信号よりも1ビット長くすることで、加算の場合も減算の場合もオーバーフロー・エラーの発生を防ぐことができます。
module signed_add( input [15:0] ina, inb, output [16:0] result); assign result = {ina[15], ina} + {inb[15], inb}; endmodule module signed_sub( input [15:0] ina, inb, output [16:0] result); assign result = {ina[15], ina} - {inb[15], inb}; endmodule
上のリストにあります“{ina[15], ina}”は「連接演算子」と呼ばれるもので、ina[15]とinaをつないだ信号線を形成します。この演算子は左辺にも置くことができます。Verilogはハードウエア記述言語であり、論理回路の配線を規定しているのですが、この演算子は「配線の束ね方を変えている」ことに相当します。
演算結果のビット数を1ビットだけ増やしておけばオーバーフロー・エラーが発生しないということを前節で述べましたが、複雑な演算処理をする場合に、処理が進むにしたがってどんどん信号を表すためのビット数が増えてしまうというのも困りものです。このため、信号を表すためのビット長には制限を加えることがしばしば行われます。
信号を表すビット長に制限を加えた場合、オーバーフロー・エラーが発生する危険が生じます。オーバーフローが発生した際の信号処理装置の異常動作を防止するため、オーバーフロー・エラーの検出と、オーバーフロー・エラー発生の際の信号のクリッピング処理が行われます。
クリッピング処理を行わない場合、オーバーフロー・エラーが発生すると、上図に青で示したように、出力信号は最大値から最小値へと急変します。クリッピング処理は、オーバーフロー・エラーの発生を検出して、出力信号を最大値または最小値に保つもので、こうすることで信号の急変を防ぎ、装置の異常動作を防止することができます。
符号なし整数のの桁縮小に際しては、捨てられるビット中に1が含まれている場合にオーバーフロー・エラーとなります。符号付整数の場合は、捨てられるビットのすべてが、残る信号の最上位(すなわち符号ビット)と一致していない場合にオーバーフロー・エラーが発生します。
以下のリストは32ビット幅の符号付整数を16ビット幅に縮小する際に、オーバーフロー・エラーを検出して、出力信号を最大値か最小値にクリッピング処理する論理を示します。
module signed_clip( input [31:0] in, output [15:0] result, output error); assign error = ~&in[31:15] & |in[31:15]; assign result = ~error ? in[15:0] : (in[31] ? 32'h8000 : 32'h7FFF); endmodule
上のリストにあります“~&in[31:15]”と“|in[31:15]”という演算子は見慣れない方も多いかと思います。これはリダクション演算子と呼ばれるもので、この右側に書かれた信号線のすべてに対して論理演算を行います。
すなわち“~&in[31:15]”は、“~(in[31} & in[30] & ... & in[15])”という演算を意味し、“~”が否定演算子ですので、「『すべてのビットが1』ではない」ときに1(真)となります。また“|in[31:15]”は、いずれかのビットが1であるとき真となります。
~&in[31:15]は、符号付整数が負であって有効ビットが上位16ビットに含まれないときにゼロとなり、“|in[31:15]”は符号付整数が正であって有効ビットが上位16ビットに含まれないときにゼロとなります。したがって、いずれかがゼロであれば有効ビットの切捨ては発生しないということになります。
この論理の動作を、Qualtus II付属のシミュレータでテストした結果を下図に示します。
入力は上位16ビットがina_pに、下位16ビットがinb_pに入力されます。オーバーフロー・エラーが発生した場合はerror_pが1となり、出力はプラス側にオーバーフローした場合に16'h7FFF、マイナス側にオーバーフローした場合に16'h8000に固定されます。入力が与えられてから出力が現れるまでに多少の時間遅れがあります。入出力の対応関係を赤線で示しておきましたが、論理が正しく動作していることがお分かりいただけますでしょうか。
いろいろな加減算に関するVerilog-2001のソースコードをこのファイルに置きました。