2017-01-02 12 views
-1

분할 연산 (/)은 FPGA의 경우 비용이 많이 드나요? 기본 이동 작업으로 Q15 형식 번호 2 개 (16 비트 고정 소수점 수)를 나눌 수 있습니까?Verilog에서 '/'(나누기) 연산자를 사용하지 않고 Q15 값 두 개를 나누는 방법은 무엇입니까?

누군가가 나를 도와 줄 수 있습니까?

미리 감사드립니다.

+2

Q15 란 무엇입니까? 예를 들어, 시도한 코드를 제공하십시오. –

+0

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).

고정 소수점 부문 정수 나눗셈 연산자를 사용하여, 우리는 분할 스케일 인자, 즉 (a * 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에서 고속 고정 소수점 디바이더 구현". 컴퓨터 과학 저널 & 기술, Vol. 6, No. 1, April 2006, pp. 8-11. 다양한 특수 다루는 여러 가지 방법이 있음을

/* 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; 
} 

주 : 여기

들은 비 복원 방법은 16 비트의 보수 피연산자의 분할에 이용 될 수있는 방법을 도시 C 코드 비 복원 부문에서 발생하는 사례. 예를 들어, 0 부분 부분 나머지 pp을 감지하고이 경우 초기에 몫 비트를 통해 루프를 종료하는 코드를 자주 보게됩니다. 여기에서는 FPGA 구현이 루프를 완전히 풀어 파이프 라인 구현을 작성한다고 가정합니다.이 경우 조기 종료가 도움이되지 않습니다. 대신 최종 보정 값이 부분 나머지 0을 무시하여 영향을받는 몫에 적용됩니다.

위의 내용에서 Q15 나누기를 만들려면 배당의 업 스케일링을 통합하면됩니다.대신에 : (읽기에 사용 Verilog 코드 미안 해요, 내가 제공하지 않습니다) 테스트 프레임 워크를 포함하는 것은

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

결과 C 코드 :

uint32_t pp = dividend; /* partial remainder */ 

우리는 지금 이것을 사용

#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; 
}