2011-12-01 9 views
16

저는 특히 Cortex-A8과 Cortex-A9에서 작업하고 있습니다. 일부 아키텍처에서는 정수 나누기가 제공되지 않는다는 것을 알고 있지만 float, divide, integer로 변환하는 것 이외의 다른 작업을 수행하는 가장 좋은 방법은 무엇입니까? 아니면 실제로 최상의 솔루션입니까?ARM에서는 정수 (부호 있거나 부호없는) 나누기를 어떻게합니까?

건배! =)

+0

물론 컴파일러는 하드웨어에없는 경우에도 소프트웨어 모드에서 정수 나누기를 지원합니다. 그 높은 사양의 칩에는 정수 나누기가 없다는 것을 나는 의심한다. 나는 ATMega (Arduino와 같은)가 그것을 가지고 있다고 생각한다. – leppie

+5

ARM에서 정수 나누기를위한 어셈블리 명령어가 없습니다. – Phonon

+1

float로 변환하거나 풀린 3 opcode 패턴으로 수동 나누기를 수행하십시오. –

답변

4

는 컴파일러는 일반적으로 내가 GCC에서 그들을 압축을 푼 예를 들어, 자사의 라이브러리에서 격차, gcclib을 포함하고 직접 사용 : 다시

https://github.com/dwelch67/stm32vld/ 다음 stm32f4d/어드벤처/gcclib 떠가는

및 아마 최선의 해결책이 아닙니다. 당신은 그것을 시도하고 ... 이것은 곱셈이 얼마나 빨리는 볼 수 있지만 쉽게 그것을 나누기 만들 수 있습니다 : 나는 시간을 일부러

https://github.com/dwelch67/stm32vld/ 다음 stm32f4d/float01/vectors.s는하지만 볼을 얼마나 빠르거나 느린 지. 이해하기 위의 대뇌 피질 -m을 사용하고 있습니다. 대뇌 피질 -a, 스펙트럼의 다른 끝, 비슷한 부유물 지침 및 gcc lib 물건은 비슷합니다. 대뇌 피질에 대해서는 엄지 손가락을 만들어야하지만 m 팔을 쉽게 만들 수 있습니다. 실제로 gcc를 사용하면 모든 작업이 자동으로 수행되어야합니다. 그렇게하지 않으면 안됩니다. 다른 컴파일러들도 위 어드벤쳐 게임에서했던 것처럼 그것을 할 필요는 없습니다.

7

정수 복사를위한 다른 곳의 복사 - 파스타 : 기본적으로 3 비트 당 명령입니다. this 웹 사이트에서 다른 곳에서도 많이 보았습니다. This 사이트에는 또한 일반적으로 더 빠른 좋은 버전이 있습니다.


@ Entry r0: numerator (lo) must be signed positive 
@  r2: deniminator (den) must be non-zero and signed negative 
idiv: 
     lo .req r0; hi .req r1; den .req r2 
     mov hi, #0 @ hi = 0 
     adds lo, lo, lo 
     .rept 32 @ repeat 32 times 
      adcs hi, den, hi, lsl #1 
      subcc hi, hi, den 
      adcs lo, lo, lo 
     .endr 
     mov pc, lr @ return 
@ Exit r0: quotient (lo) 
@  r1: remainder (hi) 
+4

이것은 비트 당 3 명령이지만 비트 당 3 사이클은 아닙니다. 각 단계의 모든 명령어는 이전의 플래그 설정에 즉시 종속되므로 코어에 따라 3-4 사이클의 결과 지연이 발생합니다. 1 단계 당 9-12 사이클이 걸리므로 총 360 사이클이 소요됩니다. –

+0

오른쪽에 대한 소리. 스윙 할 수있는 경우 역 곱하기 고정 점은 항상 더 나은 옵션입니다. –

2

웹에서 서명되지 않은 버전을 찾을 수 없어서 서명되지 않은 부분을 수행하기 위해 내 자신의 루틴을 작성했습니다. 32 비트 결과를 얻으려면 64 비트 값을 32 비트 값으로 나눌 필요가있었습니다.

내부 루프는 위에 제공된 서명 된 솔루션만큼 효율적이지 않지만 서명되지 않은 산술을 지원합니다. 이 루틴은 분자 (hi)의 상위 부분이 분모 (den)보다 작은 경우 32 비트 나누기를 수행하고, 그렇지 않으면 전체 64 비트 나누기가 수행됩니다 (hi : lo/den). 결과는 lo입니다.

cmp  hi, den     // if hi < den do 32 bits, else 64 bits 
    bpl  do64bits 
    REPT 32 
    adds lo, lo, lo    // shift numerator through carry 
    adcs hi, hi, hi 
    subscc work, hi, den   // if carry not set, compare   
    subcs hi, hi, den    // if carry set, subtract 
    addcs lo, lo, #1    // if carry set, and 1 to quotient 
    ENDR 

    mov  r0, lo     // move result into R0 
    mov  pc, lr     // return 

do64bits: 
    mov  top, #0 
    REPT 64 
    adds lo, lo, lo    // shift numerator through carry 
    adcs hi, hi, hi 
    adcs top, top, top 
    subscc work, top, den   // if carry not set, compare   
    subcs top, top, den   // if carry set, subtract 
    addcs lo, lo, #1    // if carry set, and 1 to quotient 
    ENDR 
    mov  r0, lo     // move result into R0 
    mov  pc, lr     // return 

경계 조건과 2의 거듭 제곱을 추가 검사 할 수 있습니다. 자세한 내용은 상수 값으로 http://www.idwiz.co.za/Tips%20and%20Tricks/Divide.htm

10

부문에서 찾을 수는 곱셈 64 비트-A을하고 이동 오른쪽을, 예를 들어, 다음과 같이 신속하게 이루어집니다 : 여기

LDR  R3, =0xA151C331 
UMULL R3, R2, R1, R3 
MOV  R0, R2,LSR#10 

는 R1은 1625에 의해 구분됩니다.64bitreg (R2 : R3) = R1 * 0xA151C331는, 결과가 오른쪽 (10)에 의해 시프트 된 상위 32 비트이다 계산은 다음과 같이 수행하여이 식으로부터 고유 상수를 계산할 수

R1*0xA151C331/2^(32+10) = R1*0.00061538461545751488 = R1/1624.99999980 

:

x/N == (x*A)/2^(32+n) -->  A = 2^(32+n)/N 

는 A가 < 2^32

+1

여기에 반올림 오류가 있습니다. N = 7에 의한 부호없는 32 비트 나눗셈의 경우 n = 2이고 A = 2454267026.28 ... A의 값을 반올림하면 "4294967292/7"의 값이 너무 작아집니다. 반올림하면 "4294967291/7"의 결과가 너무 커집니다. 이것은 A의 정확한 값의 소수 부분이 0.5보다 작은 경우에만 발생할 수 있습니다. 따라서 3, 5 또는 1625와 같이 N 값의 약 절반에 대해 잘 작동합니다. –

0

나는 ARM GNU 어셈블러에 대한 다음과 같은 기능을 작성하는 가장 큰 N을 선택합니다. 컴퓨터에 udiv/sdiv 기계 지원이없는 CPU가있는 경우 두 가지 기능 모두에서 처음 몇 줄을 "0 :"레이블까지 잘라냅니다.

.arm 
.cpu cortex-a7 
.syntax unified 

.type udiv,%function 
.globl udiv 
udiv: tst  r1,r1 
     bne  0f 
     udiv r3,r0,r2 
     mls  r1,r2,r3,r0 
     mov  r0,r3 
     bx  lr 
0:  cmp  r1,r2 
     movhs r1,r2 
     bxhs lr 
     mvn  r3,0 
1:  adds r0,r0 
     adcs r1,r1 
     cmpcc r1,r2 
     subcs r1,r2 
     orrcs r0,1 
     lsls r3,1 
     bne  1b 
     bx  lr 
.size udiv,.-udiv 

.type sdiv,%function 
.globl sdiv 
sdiv: teq  r1,r0,ASR 31 
     bne  0f 
     sdiv r3,r0,r2 
     mls  r1,r2,r3,r0 
     mov  r0,r3 
     bx  lr 
0:  mov  r3,2 
     adds r0,r0 
     and  r3,r3,r1,LSR 30 
     adcs r1,r1 
     orr  r3,r3,r2,LSR 31 
     movvs r1,r2 
     ldrvc pc,[pc,r3,LSL 2] 
     bx  lr 
     .int 1f 
     .int 3f 
     .int 5f 
     .int 11f 
1:  cmp  r1,r2 
     movge r1,r2 
     bxge lr 
     mvn  r3,1 
2:  adds r0,r0 
     adcs r1,r1 
     cmpvc r1,r2 
     subge r1,r2 
     orrge r0,1 
     lsls r3,1 
     bne  2b 
     bx  lr 
3:  cmn  r1,r2 
     movge r1,r2 
     bxge lr 
     mvn  r3,1 
4:  adds r0,r0 
     adcs r1,r1 
     cmnvc r1,r2 
     addge r1,r2 
     orrge r0,1 
     lsls r3,1 
     bne  4b 
     rsb  r0,0 
     bx  lr 
5:  cmn  r1,r2 
     blt  6f 
     tsteq r0,r0 
     bne  7f 
6:  mov  r1,r2 
     bx  lr 
7:  mvn  r3,1 
8:  adds r0,r0 
     adcs r1,r1 
     cmnvc r1,r2 
     blt  9f 
     tsteq r0,r3 
     bne  10f 
9:  add  r1,r2 
     orr  r0,1 
10:  lsls r3,1 
     bne  8b 
     rsb  r0,0 
     bx  lr 
11:  cmp  r1,r2 
     blt  12f 
     tsteq r0,r0 
     bne  13f 
12:  mov  r1,r2 
     bx  lr 
13:  mvn  r3,1 
14:  adds r0,r0 
     adcs r1,r1 
     cmpvc r1,r2 
     blt  15f 
     tsteq r0,r3 
     bne  16f 
15:  sub  r1,r2 
     orr  r0,1 
16:  lsls r3,1 
     bne  14b 
     bx  lr 

두 가지 기능, 부호없는 정수 나누기에 대한 udiv 서명 정수 나누기에 대한 sdiv있다. 이들은 모두 r1 (상위 워드) 및 r0 (하위 워드)의 64 비트 배당 (부호 또는 부호 없음)과 r2의 32 비트 제수를 예상합니다. 이들은 r0의 나머지와 나머지를 r1으로 반환하므로 C headerextern으로 정의 할 수 있으며 64 비트 정수를 반환하고 나중에 몫과 나머지를 마스크 아웃합니다. 에러 (0 또는 오버 플로우에 의한 나눗셈)는 절대 값이 제수의 절대 값보다 크거나 같은 나머지에 의해 표시됩니다. 부호 분할 알고리즘은 배당과 제수의 부호로 대소 문자 구분을 사용합니다. 그것은 모든 오버 플로우 조건을 제대로 감지하지 못하기 때문에 먼저 양의 정수로 변환하지 않습니다.