プロセッサのタネとしてのFPU
- FAddとFMulを組み合わせてプロセッサのタネを考えてみます。プロセッサと言うからには、μコード(Instruction)による逐次演算とレジスタファイルは少なくとも必要です。ただし、FPUと言うことで分岐は考慮しないことにします。大小比較等のフラグの保持くらいの対応はするべきでしょうがこれも割愛します。
- 応用例の仮定とし、4系統のローパスフィルタの結果(A,B,C,D)に対して、(AxB+C2)xDの計算を必要なとする場合を考えます。ローパスフィルタがフル稼働しても10サイクルで結果を1つしか出せなければ、この後の計算にFAddとFMulの資源を計算式通りに用意するのは効率がよくないことは明らかです。そこで、10サイクル消費してもいいのでFAddとFMulだけの最小資源つまりFPUで結果を得れれば合理的なことが理解して頂けると思います。
- 以下のシンプルな仕様を考えてみます。
- Valid, Stallのパイプライン形式にして、他のモジュールとの接続性を簡単化する
- レジスタファイルはWrite衝突回避の複雑さを避けるため、FAddとFMulそれぞれで用意
- 入力2項は柔軟性を持たせるため、外部入力とレジスタファイルの任意選択を可能にする
- 出力は決まったレジスタを常に出力させ、出力タイミングを命令で与える
- 命令は回路の簡単化のため水平型(FAddとFMulそれぞれ独立)[2]にし、フローに関してレイテンシを考慮したくコードを用意する
- ここからさらに具体化が必要ですが、説明はブロック図とコードを見て頂くと言うことで割愛します。付け加える注意点として、入出力のフローを壊す制御[1]を避けないといけないため、バッファ型は使用できません。また、iVldがネゲートすればパイプ全体を止める必要があります。ここは要注意で、結果が出力しきるまでiVldを与える必要があります。
- 最後にFAddとFMulのパイプラインを基本型に変更する必要があるのと、最終段のラッチは本体のモジュールでレジスタファイルとして管理するので削除します(コードに掲載します)。
命令セットの定義
- 上記仕様を元に命令セットを定めます[3]。具体的には、レジスタ番号等をビットフィールドの位置を決めて並べます。水平型なのでそれぞれのフィールドは直交しており、特に注意することはありません。なるべくなら、タイプごとにまとめます。
- 下記は演算器ごとの命令セットの例です(複雑に見えますが、こう言うものだと思って頂ければいいと思います)。先頭から演算オプション、入力2項のOperand A、Operand B、出力結果のDestinationフィールドです。
- 演算オプションはFMulは影響なしでFAddではA+BかA-Bを指定する場合に使用する
- Operand A/Bのimmは入力信号をオペランドにする場合に使用する(altとreg#はDon't care)
- FMul.Operand A ← iMA
- FMul.Operand B ← iMB
- FAdd.Operand A ← iAA
- FAdd.Operand B ← iAB
- Operand A/Bのaltは異なる演算器のレジスタファイルを参照する場合に使用する
- Operand A/Bのreg #はレジスタファイルの番号を指定する(フィールド長はレジスタ数による)
- DestinationのWEはレジスタファイルへのWrite Enableを指定する('0'であれば他が何であろうがNOPと同じ)
- Destinationのreg #はレジスタファイルの番号を指定する(フィールド長はレジスタ数による)
- 実際の使用方法は次ページです。
本体コード(RTL)
/* ****************************** MODULE PREAMBLE ******************************
Copyright (c) 2012, ArchiTek
This document constitutes confidential and proprietary information
of ArchiTek. All rights reserved.
*/
// ******************************* MODULE HEADER *******************************
module fpu (
iVld,
iStall,
iOut,
iMCode,
iMA,
iMB,
iACode,
iAA,
iAB,
oVld,
oStall,
oMY,
oAY,
reset,
clk
);
// ************************ PARAMETER DECLARATIONS *****************************
// レジスタファイルの数はパラメータで与える
parameter NR = 1; // Radix of register number
parameter N = 1<<NR;
// *************************** I/O DECLARATIONS ********************************
// Pipe Input
input iVld;
output iStall;
// 出力タイミングを示し、これが伝搬してoVldになる
input iOut; // Pipe out timing
// ここがFMulのInstruction
input [3*NR+5:0] iMCode; // Mul Op
// [3*NR+5] Reserved
// [3*NR+4:2*NR+3] Operand0
// [2*NR+2: NR+1] Operand1
// MSB0: Imm Regsiter Sel
// MSB1: Add Regsiter Sel
// [ NR : 0 ] Dst
// MSB : Write Enable (!NOP)
// FMulのOperand A,Bに対する即値
input [15:0] iMA; // Mul Operand0
input [15:0] iMB; // Mul Operand1
// ここがFAddのInstruction
input [3*NR+5:0] iACode; // Add Op
// [3*NR+5] Sub
// [3*NR+4:2*NR+3] Operand0
// [2*NR+2: NR+1] Operand1
// MSB0: Imm Regsiter Sel
// MSB1: Mul Regsiter Sel
// [ NR : 0 ] Dst
// MSB : Write Enable (!NOP)
// FAddのOperand A,Bに対する即値
input [15:0] iAA; // Add Operand0
input [15:0] iAB; // Add Operand1
// Pipe Output
// iVldに対して1対1ではなく、iVldとiOutが同時にアサートするものに対して
output oVld;
input oStall;
// Mul/Addのレジスタ番号0の値をそれぞれ出力
output [15:0] oMY; // Mul Result
output [15:0] oAY; // Add Result
// Utility
input reset;
input clk;
// ************************** LOCAL DECLARATIONS *******************************
// 便利のためInstructionを分解しておく
wire iMType = iMCode[3*NR+5];
wire iMImm0 = iMCode[3*NR+4];
wire iMAlt0 = iMCode[3*NR+3];
wire [NR-1:0] iMOp0 = iMCode[3*NR+2:2*NR+3];
wire iMImm1 = iMCode[2*NR+2];
wire iMAlt1 = iMCode[2*NR+1];
wire [NR-1:0] iMOp1 = iMCode[2*NR:NR+1];
wire iMEn = iMCode[NR];
wire [NR-1:0] iMDst = iMCode[NR-1:0];
wire iAType = iACode[3*NR+5];
wire iAImm0 = iACode[3*NR+4];
wire iAAlt0 = iACode[3*NR+3];
wire [NR-1:0] iAOp0 = iACode[3*NR+2:2*NR+3];
wire iAImm1 = iACode[2*NR+2];
wire iAAlt1 = iACode[2*NR+1];
wire [NR-1:0] iAOp1 = iACode[2*NR:NR+1];
wire iAEn = iACode[NR];
wire [NR-1:0] iADst = iACode[NR-1:0];
// Valid伝搬用のレジスタ(深いパイプ長の演算器を使うなら追加が必要)
reg vld_0, vld_1;
reg out_0;
wire stall; // 共通Stall
// Instruction伝搬用のレジスタ(深いパイプ長の演算器を使うなら追加が必要)
reg mEn_0;
reg [NR-1:0] mDst_0;
reg aEn_0;
reg [NR-1:0] aDst_0;
// それぞれの入力Operand
reg [15:0] iMulOpA;
reg [15:0] iMulOpB;
reg [15:0] iAddOpA;
reg [15:0] iAddOpB;
wire [15:0] iAddNeg;
// それぞれの出力結果
wire [15:0] mulY;
wire [15:0] addY;
// 2次元配列でもいいが、後々alwaysのSensibility Listに入れる可能性を考慮
// 簡単のためFFで構成(SRAMを採用するにはポート数や先行アクセスの問題が生じる)
reg [16*N-1:0] mReg;
reg [16*N-1:0] aReg;
// ****************************** MODULE BODY **********************************
// -----------------------------------------------------------------------------
// Valid & Parameter Path
// vld_1にはiOut(伝搬後のout_0)が織り込まれており、出力時にoStallがない限りiStallはデアサート
assign iStall = vld_1 & oStall;
// ---- Stage 0
always @(posedge clk)
if (reset) begin
vld_0 <= #1 1'b0;
out_0 <= #1 1'b0;
end
else if (!stall) begin
vld_0 <= #1 iVld;
out_0 <= #1 iOut;
end
// 書き込みタイミングまで関係Instructionをパイプに流す(Enは上記説明のWEに相当)
always @(posedge clk)
if (!stall) begin
mEn_0 <= #1 iMEn;
mDst_0 <= #1 iMDst;
aEn_0 <= #1 iAEn;
aDst_0 <= #1 iADst;
end
// ---- Stage 1
always @(posedge clk)
if (reset)
vld_1 <= #1 1'b0;
else if (!stall)
vld_1 <= #1 vld_0 & out_0;
assign oVld = iVld & vld_1;
// このモジュール全体のStall、iVldがなければStallさせる
assign stall = !iVld | iStall;
// -----------------------------------------------------------------------------
// Multiplier Operand Sel
// FMulのOperandをInstructionにより選択
always @(
iMImm0 or
iMAlt0 or
iMOp0 or
iMA or
mReg or
aReg
)
casex ({iMImm0, iMAlt0})
2'b1x: iMulOpA = iMA;
2'b00: iMulOpA = selFunc(mReg, iMOp0);
2'b01: iMulOpA = selFunc(aReg, iMOp0);
endcase
always @(
iMImm1 or
iMAlt1 or
iMOp1 or
iMB or
mReg or
aReg
)
casex ({iMImm1, iMAlt1})
2'b1x: iMulOpB = iMB;
2'b00: iMulOpB = selFunc(mReg, iMOp1);
2'b01: iMulOpB = selFunc(aReg, iMOp1);
endcase
// -----------------------------------------------------------------------------
// Adder Operand Sel
// FAddのOperandをInstructionにより選択
always @(
iAImm0 or
iAAlt0 or
iAOp0 or
iAA or
mReg or
aReg
)
casex ({iAImm0, iAAlt0})
2'b1x: iAddOpA = iAA;
2'b00: iAddOpA = selFunc(aReg, iAOp0);
2'b01: iAddOpA = selFunc(mReg, iAOp0);
endcase
always @(
iAImm1 or
iAAlt1 or
iAOp1 or
iAB or
mReg or
aReg
)
casex ({iAImm1, iAAlt1})
2'b1x: iAddOpB = iAB;
2'b00: iAddOpB = selFunc(aReg, iAOp1);
2'b01: iAddOpB = selFunc(mReg, iAOp1);
endcase
// -----------------------------------------------------------------------------
// Multiplier
// 改変FMullをコール、結果mulYはMul用レジスタファイルに入る前の値
fmul_p mul_0 (
.iA (iMulOpA),
.iB (iMulOpB),
.oStall (stall),
.oY (mulY),
.reset (reset),
.clk (clk)
);
// -----------------------------------------------------------------------------
// Adder
// 改変FAddlをコール、結果addYはAdd用レジスタファイルに入る前の値
fadd_p add_0 (
.iA (iAddOpA),
.iB (iAddOpB ^ iAddNeg),
.oStall (stall),
.oY (addY),
.reset (reset),
.clk (clk)
);
// 演算オプションによりOperand Bの符号を反転(減算)
assign iAddNeg = {iAType, 15'd0};
// -----------------------------------------------------------------------------
// Mul Register
// Mul InstructionでWEが'1'であった場合にのみMul用レジスタファイルに結果を格納
always @(posedge clk)
if (!stall & mEn_0)
mReg <= #1 regFunc(mReg, mulY, mDst_0);
// R0を常に出力
assign oMY = mReg[15:0];
// -----------------------------------------------------------------------------
// Add Register
// Add InstructionでWEが'1'であった場合にのみAdd用レジスタファイルに結果を格納
always @(posedge clk)
if (!stall & aEn_0)
aReg <= #1 regFunc(aReg, addY, aDst_0);
// R0を常に出力
assign oAY = aReg[15:0];
// ************************** FUNCTIONS and TASKS ******************************
// レジスタファイルからReg #で示されるレジスタを抽出
function [15:0] selFunc;
input [16*N-1:0] plane;
input [NR-1:0] ptr;
reg [15:0] result;
integer i;
begin
for (i=0; i<16; i=i+1)
result[i] = plane[{ptr, i[3:0]}];
selFunc = result;
end
endfunction
// レジスタファイルへReg #で示される値を上書き
function [16*N-1:0] regFunc;
input [16*N-1:0] plane;
input [15:0] data;
input [NR-1:0] ptr;
reg [16*N-1:0] result;
integer i;
integer j;
begin
for (i=0; i<N; i=i+1)
for (j=0; j<16; j=j+1)
result[{i[NR-1:0], j[3:0]}]
// 該当レジスタへのみ入力値を代入
= (i[NR-1:0] == ptr)
? data[j]
: plane[{i[NR-1:0], j[3:0]}];
regFunc = result;
end
endfunction
endmodule
// *****************************************************************************
改変FAddコード(RTL)
/* ****************************** MODULE PREAMBLE ******************************
Copyright (c) 2011, ArchiTek
This document constitutes confidential and proprietary information
of ArchiTek. All rights reserved.
faddに比べValid系統と最終段のLatch部を削除しているだけ
*/
// ******************************* MODULE HEADER *******************************
module fadd_p (
iA,
iB,
oStall,
oY,
reset,
clk
);
// *************************** I/O DECLARATIONS ********************************
// Pipe Input
input [15:0] iA; // Operand A
input [15:0] iB; // Operand B
// Pipe Output
input oStall;
output [15:0] oY; // Result
// Utility
input reset;
input clk;
// ************************** LOCAL DECLARATIONS *******************************
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] expSft;
wire [13:0] fracFix;
wire [11:0] fracTmp;
reg ySign_0;
reg ySign_0D;
reg yInv_0;
wire yInv_0D;
reg aInv_0D;
reg bInv_0D;
reg [4:0] yExp_0;
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_1D;
reg [1:0] yFracRound_0;
wire [1:0] yFracRound_0D;
// ****************************** MODULE BODY **********************************
// -----------------------------------------------------------------------------
// Sign Path
// ---- Stage 0 - Result
always @(posedge clk)
if (!oStall) 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;
// ---- Alias
assign oY[15] = ySign_0;
// -----------------------------------------------------------------------------
// Exp Path
// ---- Stage 0 - Put Large Exp
always @(posedge clk)
if (!oStall)
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
assign yExp_1D = |fracTmp
? yExp_0 - {1'd0, expSft} + 5'd1
: 5'h00;
assign expSft = expShiftFunc(fracFix) - {3'd0, fracTmp[11]};
// ---- Alias
assign oY[14:10] = yExp_1D;
// -----------------------------------------------------------------------------
// Frac Path
// ---- Stage0 - Fix Same Column
always @(posedge clk)
if (!oStall) begin
aFrac_0 <= #1 aFrac_0D;
bFrac_0 <= #1 bFrac_0D;
yFracRound_0 <= #1 yFracRound_0D;
end
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
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);
assign aFrac_0D = compFunc(aFracBar, aInv_0D);
assign bFrac_0D = compFunc(bFracBar, bInv_0D);
assign yFracRound_0D = {2{yInv_0D}} ^ roundFunc(pFrac, expAbs);
// ---- Stage1 - 2's Comlemet Add become always plus value
always @(
fracTmp
)
yFrac_1D = fracTmp[11]
? fracTmp[10:1]
: fracTmp[9:0];
assign fracFix = {1'd0, aFrac_0, 2'd0}
+ {1'd0, bFrac_0, 2'd0}
+ {12'd0, yFracRound_0}
+ {yInv_0, 12'd0, yInv_0};
assign fracTmp = fracShiftFunc(fracFix);
// ---- Alias
assign oY[9:0] = yFrac_1D;
// ************************** FUNCTIONS and TASKS ******************************
function [10:0] compFunc;
input [10:0] frac;
input sign;
begin
compFunc = sign ? ~frac : frac;
end
endfunction
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
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
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
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
// *****************************************************************************
改変FMulコード(RTL)
/* ****************************** MODULE PREAMBLE ******************************
Copyright (c) 2012, ArchiTek
This document constitutes confidential and proprietary information
of ArchiTek. All rights reserved.
fmulに比べValid系統と最終段のLatch部を削除しているだけ
*/
// ******************************* MODULE HEADER *******************************
module fmul_p (
iA,
iB,
oStall,
oY,
reset,
clk
);
// *************************** I/O DECLARATIONS ********************************
// Pipe Input
input [15:0] iA; // Operand A
input [15:0] iB; // Operand B
// Pipe Output
input oStall;
output [15:0] oY; // Result
// Utility
input reset;
input clk;
// ************************** LOCAL DECLARATIONS *******************************
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 expMin;
wire [6:0] expTmp;
wire expSft;
wire [11:0] fracSft;
reg ySign_0;
reg [6:0] yExp_0;
reg [4:0] yExp_1D;
reg [12:0] yFrac_0;
wire [21:0] yFrac_0D;
reg [9:0] yFrac_1D;
// ****************************** MODULE BODY **********************************
// -----------------------------------------------------------------------------
// Sign Path
// ---- Stage 0 - Result
always @(posedge clk)
if (!oStall)
ySign_0 <= #1 aSign ^ bSign;
// ---- Alias
assign oY[15] = ySign_0;
// -----------------------------------------------------------------------------
// Exp Path
// ---- Stage 0 - Sum of Exp
always @(posedge clk)
if (!oStall)
yExp_0 <= #1 (aZero | bZero)
? 7'd0
: {2'd0, aExp} + {2'd0, bExp} - 7'h0f;
// ---- Stage 1 - Carry Fix
always @(
expTmp or
expMin or
fracSft
)
casex ({|fracSft, expMin})
2'b0x,
2'b11: yExp_1D = 5'h00;
2'b10: yExp_1D = expTmp[4:0];
endcase
assign expMin = expTmp[6];
assign expTmp = yExp_0 - {6'd0, expSft} + 7'd1;
assign expSft = expShiftFunc(yFrac_0) - fracSft[11];
// ---- Alias
assign oY[14:10] = yExp_1D;
// -----------------------------------------------------------------------------
// Frac Path
// ---- Stage0 - Separate Mul
always @(posedge clk)
if (!oStall)
yFrac_0 <= #1 yFrac_0D[21:9];
assign yFrac_0D = aFrac * bFrac;
// ---- Stage1 - Carry Fix and Remove Hidden MSB
always @(
fracSft
)
yFrac_1D = fracSft[11]
? fracSft[10:1]
: fracSft[9:0];
assign fracSft = fracShiftFunc(yFrac_0);
// ---- Alias
assign oY[9:0] = yFrac_1D;
// ************************** FUNCTIONS and TASKS ******************************
function expShiftFunc;
input [12:0] frac;
expShiftFunc = !frac[12];
endfunction
function [11:0] fracShiftFunc;
input [12:0] frac;
reg carry;
reg [10:0] result;
reg [10:0] adder0;
reg [10:0] adder1;
begin
adder0 = {10'd0, frac[1]};
adder1 = {10'd0, frac[0]};
carry = (frac[12] == 1'b0) & (&frac[11:0]);
casex (frac[12])
1'h1: result = frac[12:2] + adder0;
1'h0: result = frac[11:1] + adder1;
endcase
fracShiftFunc = {carry, result};
end
endfunction
endmodule
// *****************************************************************************
回路デザイン > 設計例 [浮動小数点] > 応用FPUその1 次のページ(応用FPUその2) このページのTOP ▲