/* ****************************** MODULE PREAMBLE ******************************
Copyright (c) 2011, ArchiTek
This document constitutes confidential and proprietary information
of ArchiTek. All rights reserved.
// 指数シフトが2以上あれば正規化が簡単になり、1以下であれば最初の桁合わせが簡単になる
// 前者は最終段の正規化を前段に入れ込むことができ、後者は初段で桁合わせと加算を実行できる
// それぞれ専用パスで回路を作れば、演算器は倍になるがレイテンシは2から1にすることができる
// ここではその手法を採用していない
*/
// ******************************* MODULE HEADER *******************************
module fadd (
iVld,
iStall,
iA,
iB,
oVld,
oStall,
oY,
reset,
clk
);
// *************************** I/O DECLARATIONS ********************************
// Pipe Input
input iVld;
output iStall;
input [15:0] iA; // Operand A
input [15:0] iB; // Operand B
// Pipe Output
output oVld;
input oStall;
output [15:0] oY; // Result
// Utility
input reset;
input clk;
// ************************** LOCAL DECLARATIONS *******************************
// 便利信号の定義と同時に値を代入
// allDirはオペランドの大きい方から小さい方を引くための判断に使う
// expDirはオペランドどうしの指数を合わせるため、指数の大小の判断に使う
wire aSign = iA[15];
wire [4:0] aExpo = iA[14:10];
wire [4:0] aExp = |aExpo ? aExpo : 5'h0f;
wire [10:0] aFrac = {|aExpo, iA[9:0]};
wire aZero = ~|iA[14:0];
wire bSign = iB[15];
wire [4:0] bExpo = iB[14:10];
wire [4:0] bExp = |bExpo ? bExpo : 5'h0f;
wire [10:0] bFrac = {|bExpo, iB[9:0]};
wire bZero = ~|iB[14:0];
wire allDir = ({aExp, aFrac} < {bExp, bFrac});
wire expDir = (aExp < bExp);
wire [4:0] expAbs = expDir ? bExp - aExp : aExp - bExp;
wire [3:0] expShift;
// 2ステージ分の信号を定義、末尾DはFFのD端子信号(確定ではない)
reg vld_0;
reg vld_1;
wire stall_0;
wire stall_1;
reg ySign_0;
reg ySign_0D;
reg ySign_1;
reg yInv_0;
wire yInv_0D;
reg aInv_0D;
reg bInv_0D;
reg [4:0] yExp_0;
reg [4:0] yExp_1;
reg [4:0] yExp_0D;
wire [4:0] yExp_1D;
reg [10:0] aFrac_0;
reg [10:0] bFrac_0;
wire [10:0] aFrac_0D;
wire [10:0] bFrac_0D;
reg [10:0] aFracBar;
reg [10:0] bFracBar;
reg [10:0] pFrac;
wire [10:0] qFrac;
reg [9:0] yFrac_1;
wire [11:0] yFrac_1D;
wire [13:0] yFracTmp;
reg [1:0] yFracRound_0;
wire [1:0] yFracRound_0D;
// ****************************** MODULE BODY **********************************
// -----------------------------------------------------------------------------
// Valid Path
// ---- Input
// 後方のStallを論理に加えバッファ型のステージに(以下同じ)
// 添え字の番号は、0をスタートに番号付けしたステージ後方の境界を示す
assign iStall = vld_0 & stall_0;
// ---- Stage 0
always @(posedge clk)
if (reset)
vld_0 <= #1 1'b0;
else if (!iStall)
vld_0 <= #1 iVld;
assign stall_0 = vld_1 & stall_1;
// ---- Stage 1
always @(posedge clk)
if (reset)
vld_1 <= #1 1'b0;
else if (!stall_0)
vld_1 <= #1 vld_0;
assign stall_1 = oStall;
// ---- Alias
// ステージの結果を出力信号にマッピング
assign oVld = vld_1;
// -----------------------------------------------------------------------------
// Sign Path
// ---- Stage 0 - Result
// オペランドの符号、0、大小関係から結果の符号を判断
// 特に減算の場合(符号が排他的)、いずれのオペランドの補数をとるかも決定
always @(posedge clk)
if (!iStall) begin
ySign_0 <= #1 ySign_0D;
yInv_0 <= #1 yInv_0D;
end
always @(
allDir or
aSign or
bSign or
aZero or
bZero
)
casex ({aZero, bZero, aSign, bSign, allDir})
5'b1xx0x: // B's Sign
{ySign_0D, bInv_0D, aInv_0D} = 3'b000;
5'b1xx1x: // B's Sign
{ySign_0D, bInv_0D, aInv_0D} = 3'b100;
5'b010xx: // A's Sign
{ySign_0D, bInv_0D, aInv_0D} = 3'b000;
5'b011xx: // A's Sign
{ySign_0D, bInv_0D, aInv_0D} = 3'b100;
5'b0000x: // + (|A|+|B|)
{ySign_0D, bInv_0D, aInv_0D} = 3'b000;
5'b0011x: // - (|A|+|B|)
{ySign_0D, bInv_0D, aInv_0D} = 3'b100;
5'b00010: // + (|A|-|B|)
{ySign_0D, bInv_0D, aInv_0D} = 3'b010;
5'b00011: // - (|B|-|A|)
{ySign_0D, bInv_0D, aInv_0D} = 3'b101;
5'b00100: // - (|A|-|B|)
{ySign_0D, bInv_0D, aInv_0D} = 3'b110;
5'b00101: // + (|B|-|A|)
{ySign_0D, bInv_0D, aInv_0D} = 3'b001;
endcase
assign yInv_0D = aInv_0D | bInv_0D;
// ---- Stage 1 - Delay
always @(posedge clk)
if (!stall_0)
ySign_1 <= #1 ySign_0;
// ---- Alias
// ステージの結果を出力信号にマッピング
assign oY[15] = ySign_1;
// -----------------------------------------------------------------------------
// Exp Path
// ---- Stage 0 - Put Large Exp
// 指数の大小関係から大きい方をラッチ、0の場合は他方をラッチ
always @(posedge clk)
if (!iStall)
yExp_0 <= #1 yExp_0D;
always @(
expDir or
aExp or
bExp or
aZero or
bZero
)
casex ({aZero, bZero, expDir})
3'b1xx: yExp_0D = bExp; // B's Exp
3'b01x: yExp_0D = aExp; // A's Exp
3'b000: yExp_0D = aExp; // A's Exp
3'b001: yExp_0D = bExp; // B's Exp
endcase
// ---- Stage 1 - Normalize
always @(posedge clk)
if (!stall_0)
yExp_1 <= #1 yExp_1D;
// 仮数部どうしの1.xxxと1.xxxを足すと指数はデフォルトで1加算(結果のMSBは2の位なので)
// 仮数部の左寄せ数はFunctionでチェック、その分の桁上がり分を補正
// ただし、仮数部のMSBが0でもまるめで桁上がりの可能性を仮数部の左寄せの結果のMSBを見て判断
assign yExp_1D = |yFrac_1D
? yExp_0 - {1'd0, expShift} + 5'd1
: 5'h00;
assign expShift = expShiftFunc(yFracTmp) - {3'd0, yFrac_1D[11]};
// ---- Alias
// ステージの結果を出力信号にマッピング
assign oY[14:10] = yExp_1;
// -----------------------------------------------------------------------------
// Frac Path
// ---- Stage0 - Fix Same Column
// 指数部の桁合わせをした仮数部とまるめ情報をラッチ
always @(posedge clk)
if (!iStall) begin
aFrac_0 <= #1 aFrac_0D;
bFrac_0 <= #1 bFrac_0D;
yFracRound_0 <= #1 yFracRound_0D;
end
// 指数の大小関係からオペランドAが小さいなら下記の右寄せした結果を代入
always @(
expDir or
aZero or
bZero or
aFrac or
qFrac
)
casex ({bZero, aZero, expDir})
3'bx1x: aFracBar = 11'h000;
3'b001: aFracBar = qFrac;
default:
aFracBar = aFrac;
endcase
// 指数の大小関係からオペランドBが小さいなら下記の右寄せした結果を代入
always @(
expDir or
aZero or
bZero or
bFrac or
qFrac
)
casex ({bZero, aZero, expDir})
3'b1xx: bFracBar = 11'h000;
3'b000: bFracBar = qFrac;
default:
bFracBar = bFrac;
endcase
// 指数の小さい方を大きい方に合わせるため、仮数部を右寄せ
always @(
expDir or
aZero or
bZero or
aFrac or
bFrac
)
casex ({bZero, aZero, expDir})
3'bx1x: pFrac = aFrac;
3'b10x: pFrac = bFrac;
3'b001: pFrac = aFrac;
3'b000: pFrac = bFrac;
endcase
assign qFrac = bsftFunc(pFrac, expAbs);
// 符号のところで求めた仮数部の補数の必要性から、先ずは1の補数を計算
// 補数を取るのはどちらか一方で、2つ同時はない
assign aFrac_0D = compFunc(aFracBar, aInv_0D);
assign bFrac_0D = compFunc(bFracBar, bInv_0D);
// 下記の右寄せで捨てられるビットのMSBの2ビットをまるめ情報として取得
// 同時に補数の必要性を見て1の補数を実行(減算時は引く方が小さくなっている)
assign yFracRound_0D = {2{yInv_0D}} ^ roundFunc(pFrac, expAbs);
// ---- Stage1 - 2's Comlemet Add become always plus value
// ラッチ時は最終的な左寄せを行う(左寄せ2回目)
always @(posedge clk)
if (!stall_1)
yFrac_1 <= #1 yFrac_1D[11]
? yFrac_1D[10:1]
: yFrac_1D[9:0];
// 左寄せを行う(左寄せ1回目)
// MSBは指数の補正に利用する(これがなければFunction内で左寄せの処理は閉じれる)
assign yFrac_1D = fracShiftFunc(yFracTmp);
// 加算の本体、まるめと2の補数の+1をまとめて計算
assign yFracTmp = {1'd0, aFrac_0, 2'd0}
+ {1'd0, bFrac_0, 2'd0}
+ {12'd0, yFracRound_0}
+ {yInv_0, 12'd0, yInv_0};
// ---- Alias
// ステージの結果を出力信号にマッピング
assign oY[9:0] = yFrac_1;
// ************************** FUNCTIONS and TASKS ******************************
// 1の補数
function [10:0] compFunc;
input [10:0] frac;
input sign;
begin
compFunc = sign ? ~frac : frac;
end
endfunction
// 指数に従ったバレルシフト、指数はオペランドの指数の差分の絶対値(Priority encoder)
function [10:0] bsftFunc;
input [10:0] frac;
input [4:0] exp;
reg [5:0] idx;
reg [10:0] result;
integer i;
begin
for (i=0; i<11; i=i+1) begin
idx = {1'b0, exp} + i[5:0];
if (idx > 6'd10)
result[i] = 1'b0;
else
result[i] = frac[idx];
end
bsftFunc = result;
end
endfunction
// 上記バレルシフトで捨てるビットの上位2ビットを選択(Priority encoder)
function [1:0] roundFunc;
input [10:0] frac;
input [4:0] exp;
reg [5:0] idx;
reg [10:0] result;
integer i;
begin
for (i=0; i<11; i=i+1) begin
idx = {1'b0, exp} + i[5:0] - 6'd11;
if (idx[5] | (idx[4:0] > 5'd10))
result[i] = 1'b0;
else
result[i] = frac[idx];
end
roundFunc = result[10:9];
end
endfunction
// 仮数部の有効桁をチェック、全ビットのチェックを実施(Priority encoder)
function [3:0] expShiftFunc;
input [13:0] frac;
reg [3:0] idx;
integer i;
begin
idx = 4'd10;
for (i=0; i<12; i=i+1)
if (frac[i+2])
idx = i[3:0];
expShiftFunc = 4'd11 - idx;
end
endfunction
// 左寄せの1回目とまるめ、まるめは左寄せ数により参照ビットが異なる
// 入力MSBが1ならまるめてもオーバーフローしない(carry信号は0)
// 入力MSBが0ならまるめるとオーバーフローする可能性がある(その場合carry信号は1)
// 減算の結果、上位のビットの多くが0に連なる場合があるので、全ビットのチェックが必要
// このあたりを工夫すれば偶数丸め可能
function [11:0] fracShiftFunc;
input [13:0] frac;
reg [11:0] result;
reg [11:0] adder0;
reg [11:0] adder1;
reg [11:0] adder2;
begin
adder0 = {11'd0, frac[2]};
adder1 = {11'd0, frac[1]};
adder2 = {11'd0, frac[0]};
casex (frac)
{ 1'h1, 13'hxxxx}: result = {1'd0, frac[13:3]} + adder0;
{ 2'h1, 12'hxxx}: result = {1'd0, frac[12:2]} + adder1;
{ 3'h1, 11'hxxx}: result = {1'd0, frac[11:1]} + adder2;
{ 4'h1, 10'hxxx}: result = {1'd0, frac[10:0] };
{ 5'h01, 9'hxxx}: result = {1'd0, frac[9:0], 1'd0};
{ 6'h01, 8'hxx}: result = {1'd0, frac[8:0], 2'd0};
{ 7'h01, 7'hxx}: result = {1'd0, frac[7:0], 3'd0};
{ 8'h01, 6'hxx}: result = {1'd0, frac[6:0], 4'd0};
{ 9'h001, 5'hxx}: result = {1'd0, frac[5:0], 5'd0};
{10'h001, 4'hx}: result = {1'd0, frac[4:0], 6'd0};
{11'h001, 3'hx}: result = {1'd0, frac[3:0], 7'd0};
{12'h001, 2'hx}: result = {1'd0, frac[2:0], 8'd0};
{13'h001, 1'hx}: result = {1'd0, frac[1:0], 9'd0};
default: result = {1'd0, 11'd0};
endcase
fracShiftFunc = result;
end
endfunction
endmodule
// *****************************************************************************