論理回路デザイン |
|
/* ****************************** 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 // *****************************************************************************
/* ****************************** 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 // *****************************************************************************
/* ****************************** 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 ▲
○早すぎる参照 ・0番の演算結果R2を2番の演算で使用 ・結果が後段Stallで書き込めない ・2番の命令で未書き込みR2を参照 ○遅すぎる参照 ・逆に、先ほどの書き込みは終了 ・2番の命令が別の理由で発行できず ・発行済みの1番の命令でR2が上書き 0: R0+R1->R2 1: R2+R1->R2 2: R2+R1->R3 3: R2+R1->R4