저는 특히 Cortex-A8과 Cortex-A9에서 작업하고 있습니다. 일부 아키텍처에서는 정수 나누기가 제공되지 않는다는 것을 알고 있지만 float, divide, integer로 변환하는 것 이외의 다른 작업을 수행하는 가장 좋은 방법은 무엇입니까? 아니면 실제로 최상의 솔루션입니까?ARM에서는 정수 (부호 있거나 부호없는) 나누기를 어떻게합니까?
건배! =)
저는 특히 Cortex-A8과 Cortex-A9에서 작업하고 있습니다. 일부 아키텍처에서는 정수 나누기가 제공되지 않는다는 것을 알고 있지만 float, divide, integer로 변환하는 것 이외의 다른 작업을 수행하는 가장 좋은 방법은 무엇입니까? 아니면 실제로 최상의 솔루션입니까?ARM에서는 정수 (부호 있거나 부호없는) 나누기를 어떻게합니까?
건배! =)
는 컴파일러는 일반적으로 내가 GCC에서 그들을 압축을 푼 예를 들어, 자사의 라이브러리에서 격차, gcclib을 포함하고 직접 사용 : 다시
https://github.com/dwelch67/stm32vld/ 다음 stm32f4d/어드벤처/gcclib 떠가는
및 아마 최선의 해결책이 아닙니다. 당신은 그것을 시도하고 ... 이것은 곱셈이 얼마나 빨리는 볼 수 있지만 쉽게 그것을 나누기 만들 수 있습니다 : 나는 시간을 일부러
https://github.com/dwelch67/stm32vld/ 다음 stm32f4d/float01/vectors.s는하지만 볼을 얼마나 빠르거나 느린 지. 이해하기 위의 대뇌 피질 -m을 사용하고 있습니다. 대뇌 피질 -a, 스펙트럼의 다른 끝, 비슷한 부유물 지침 및 gcc lib 물건은 비슷합니다. 대뇌 피질에 대해서는 엄지 손가락을 만들어야하지만 m 팔을 쉽게 만들 수 있습니다. 실제로 gcc를 사용하면 모든 작업이 자동으로 수행되어야합니다. 그렇게하지 않으면 안됩니다. 다른 컴파일러들도 위 어드벤쳐 게임에서했던 것처럼 그것을 할 필요는 없습니다.
정수 복사를위한 다른 곳의 복사 - 파스타 : 기본적으로 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)
이것은 비트 당 3 명령이지만 비트 당 3 사이클은 아닙니다. 각 단계의 모든 명령어는 이전의 플래그 설정에 즉시 종속되므로 코어에 따라 3-4 사이클의 결과 지연이 발생합니다. 1 단계 당 9-12 사이클이 걸리므로 총 360 사이클이 소요됩니다. –
오른쪽에 대한 소리. 스윙 할 수있는 경우 역 곱하기 고정 점은 항상 더 나은 옵션입니다. –
웹에서 서명되지 않은 버전을 찾을 수 없어서 서명되지 않은 부분을 수행하기 위해 내 자신의 루틴을 작성했습니다. 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
부문에서 찾을 수는 곱셈 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
여기에 반올림 오류가 있습니다. N = 7에 의한 부호없는 32 비트 나눗셈의 경우 n = 2이고 A = 2454267026.28 ... A의 값을 반올림하면 "4294967292/7"의 값이 너무 작아집니다. 반올림하면 "4294967291/7"의 결과가 너무 커집니다. 이것은 A의 정확한 값의 소수 부분이 0.5보다 작은 경우에만 발생할 수 있습니다. 따라서 3, 5 또는 1625와 같이 N 값의 약 절반에 대해 잘 작동합니다. –
나는 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 header
에 extern
으로 정의 할 수 있으며 64 비트 정수를 반환하고 나중에 몫과 나머지를 마스크 아웃합니다. 에러 (0 또는 오버 플로우에 의한 나눗셈)는 절대 값이 제수의 절대 값보다 크거나 같은 나머지에 의해 표시됩니다. 부호 분할 알고리즘은 배당과 제수의 부호로 대소 문자 구분을 사용합니다. 그것은 모든 오버 플로우 조건을 제대로 감지하지 못하기 때문에 먼저 양의 정수로 변환하지 않습니다.
물론 컴파일러는 하드웨어에없는 경우에도 소프트웨어 모드에서 정수 나누기를 지원합니다. 그 높은 사양의 칩에는 정수 나누기가 없다는 것을 나는 의심한다. 나는 ATMega (Arduino와 같은)가 그것을 가지고 있다고 생각한다. – leppie
ARM에서 정수 나누기를위한 어셈블리 명령어가 없습니다. – Phonon
float로 변환하거나 풀린 3 opcode 패턴으로 수동 나누기를 수행하십시오. –