2017-01-02 57 views
-1

除算演算(/)はFPGAの場合には高価ですか?基本的なシフト操作で2つのQ15フォーマット番号(16ビット固定小数点数)を分割することは可能ですか?'/'(除算)演算子を使用せずに、Verilogで2つのQ15値の除算を実行する方法は?

例を挙げて助けてもらえますか?

ありがとうございます!

+2

Q15とは何ですか?あなたが試した可能性のあるコードを例として挙げてください。 –

+0

私はOPのためにQ15が16ビットの固定小数点数であると仮定しています。 https://en.wikipedia.org/wiki/Q_%28number_format%29 – wilcroft

+0

はいそれは16ビットの固定小数点数です。 –

答えて

0

固定小数点演算は、スケーリングのビットを入れた整数算術です。Q15は、スケーリング係数が2の符号付き16ビット整数として格納された完全な小数フォーマットです。、 [-1,1)。分母の大きさが被除数の大きさを超えると、分母の大きさが表現可能な範囲を超えてしまうので、除算はQ15で意味をなさないことは明らかです。

固定小数点除算のカスタムVerilog実装に着手する前に、FPGAベンダーのライブラリ製品をパイプライン分割を含む固定小数点ライブラリとしてチェックすることをお勧めします。関連する可能性のあるオープンソースプロジェクト(this oneなど)もあります。

固定小数点除算のための整数除算演算子を使用して、我々は、分割は、スケールファクタ、すなわち、(* 2 スケール)/(b *の2 スケール)=を除去するという事実のために調整する必要があります(a/b)、正しい固定小数点の結果は(a/b * 2 スケール)です。これは簡単に次のCの実装のように、2 スケールによって配当を予め乗算することによって固定されている:

int16_t div_q15 (int16_t dividend, int16_t divisor) 
{ 
    return (int16_t)(((int32_t)dividend << 15)/(int32_t)divisor); 
} 

Wikipediaが使用ビット単位でバイナリ分割を実装する方法に妥当overwiewを与えます加算、減算、およびシフト操作を行います。これらの方法は、小学校で教えられた長さの部分に密接に関連しています。 FPGAの場合は、this paperで指摘されているように、しばしば望ましい場合は非復元方法の使用があります。

"FPGA上の高速固定小数点除算器の実装"などです。 コンピュータサイエンスのジャーナル& Technology、Vol。 6、No. 1、April 2006、pp。8-11。ここ

非回復方法は、16ビットの2の補数オペランドの分割のために使用することができる方法を示すCコード:

/* bit-wise non-restoring two's complement division */ 
void int16_div (int16_t dividend, int16_t divisor, int16_t *quot, int16_t *rem) 
{ 
    const int operand_bits = (int) (sizeof (int16_t) * CHAR_BIT); 
    uint16_t d = (uint16_t)divisor; 
    uint16_t nd = 0 - d; /* -divisor */ 
    uint16_t r, q = 0; /* remainder, quotient */ 
    uint32_t dd = (uint32_t)d << operand_bits; /* expanded divisor */ 
    uint32_t pp = dividend; /* partial remainder */ 
    int i; 

    for (i = operand_bits - 1; i >= 0; i--) { 
     if ((int32_t)(pp^dd) < 0) { 
      q = (q << 1) + 0; /* record quotient bit -1 (as 0) */ 
      pp = (pp << 1) + dd; 
     } else { 
      q = (q << 1) + 1; /* record quotient bit +1 (as 1) */ 
      pp = (pp << 1) - dd; 
     } 
    } 
    /* convert quotient from digit set {-1,1} to plain two's complement */ 
    q = (q << 1) + 1; 

    /* remainder is upper half of partial remainder */ 
    r = (uint16_t)(pp >> operand_bits); 

    /* fix up cases where we worked past a partial remainder of zero */ 
    if (r == d) { /* remainder equal to divisor */ 
     q = q + 1; 
     r = 0; 
    } else if (r == nd) { /* remainder equal to -divisor */ 
     q = q - 1; 
     r = 0; 
    } 

    /* for truncating division, remainder must have same sign as dividend */ 
    if (r && ((int16_t)(dividend^r) < 0)) { 
     if ((int16_t)q < 0) { 
      q = q + 1; 
      r = r - d; 
     } else { 
      q = q - 1; 
      r = r + d; 
     } 
    } 
    *quot = (int16_t)q; 
    *rem = (int16_t)r; 
} 

注様々な特別に対処する複数の方法が存在すること非復旧部門で発生するケース。例えば、ゼロ部分剰余ppを検出し、この場合には商ビット上でループを抜けるコードが頻繁に見られます。ここでは、FPGA実装がループを完全に展開してパイプライン実装を作成すると仮定します。この場合、早期終了は役に立ちません。代わりに、ゼロの部分的な余りを無視することによって影響を受ける商に最終的な補正が適用されます。

上記からQ15ディビジョンを作成するには、配当のアップスケーリングを組み込むだけで1つの変更を加えなければなりません。代わりに:

uint32_t pp = dividend; /* partial remainder */ 

我々は今、これを使用する:

uint32_t pp = dividend << 15; /* partial remainder; incorporate Q15 scaling */ 

テストフレームワークを含めたCコードは(申し訳ありませんが、私は読みに使えるV​​erilogコードを提供することはありません)です。

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <limits.h> 
#include <math.h> 

/* bit-wise non-restoring two's complement division */ 
void q15_div (int16_t dividend, int16_t divisor, int16_t *quot, int16_t *rem) 
{ 
    const int operand_bits = (int) (sizeof (int16_t) * CHAR_BIT); 
    uint16_t d = (uint16_t)divisor; 
    uint16_t nd = 0 - d; /* -divisor */ 
    uint16_t r, q = 0; /* remainder, quotient */ 
    uint32_t dd = (uint32_t)d << operand_bits; /* expanded divisor */ 
    uint32_t pp = dividend << 15; /* partial remainder, incorporate Q15 scaling */ 
    int i; 

    for (i = operand_bits - 1; i >= 0; i--) { 
     if ((int32_t)(pp^dd) < 0) { 
      q = (q << 1) + 0; /* record quotient bit -1 (as 0) */ 
      pp = (pp << 1) + dd; 
     } else { 
      q = (q << 1) + 1; /* record quotient bit +1 (as 1) */ 
      pp = (pp << 1) - dd; 
     } 
    } 
    /* convert quotient from digit set {-1,1} to plain two's complement */ 
    q = (q << 1) + 1; 

    /* remainder is upper half of partial remainder */ 
    r = (uint16_t)(pp >> operand_bits); 

    /* fix up cases where we worked past a partial remainder of zero */ 
    if (r == d) { /* remainder equal to divisor */ 
     q = q + 1; 
     r = 0; 
    } else if (r == nd) { /* remainder equal to -divisor */ 
     q = q - 1; 
     r = 0; 
    } 

    /* for truncating division, remainder must have same sign as dividend */ 
    if (r && ((int16_t)(dividend^r) < 0)) { 
     if ((int16_t)q < 0) { 
      q = q + 1; 
      r = r - d; 
     } else { 
      q = q - 1; 
      r = r + d; 
     } 
    } 
    *quot = (int16_t)q; 
    *rem = (int16_t)r; 
} 

int main (void) 
{ 
    uint16_t dividend, divisor, ref_q, res_q, res_r; 
    double quot, fxscale = (1 << 15); 

    dividend = 0; 
    do { 
     printf ("\r%04x", dividend); 
     divisor = 1; 
     do { 
      quot = trunc (fxscale * (int16_t)dividend/(int16_t)divisor); 
      /* Q15 can only represent numbers in [-1, 1) */ 
      if ((quot >= -1.0) && (quot < 1.0)) { 
       ref_q = (int16_t)((((int32_t)(int16_t)dividend) << 15)/
            ((int32_t)(int16_t)divisor)); 
       q15_div ((int16_t)dividend, (int16_t)divisor, 
         (int16_t *)&res_q, (int16_t *)&res_r); 
       if (res_q != ref_q) { 
        printf ("!r dividend=%04x (%f) divisor=%04x (%f) res=%04x (%f) ref=%04x (%f)\n", 
          dividend, (int16_t)dividend/fxscale, 
          divisor, (int16_t)divisor/fxscale, 
          res_q, (int16_t)res_q/fxscale, 
          ref_q, (int16_t)ref_q/fxscale); 
       } 
      } 
      divisor++; 
     } while (divisor); 
     dividend++; 
    } while (dividend); 

    return EXIT_SUCCESS; 
} 
関連する問題