2011-12-01 29 views
16

Tôi đang làm việc trên Cortex-A8 và Cortex-A9 nói riêng. Tôi biết rằng một số kiến ​​trúc không đi kèm với phân chia số nguyên, nhưng cách tốt nhất để làm điều gì khác hơn là chuyển đổi sang phao, chia, chuyển đổi thành số nguyên? Hay đó thực sự là giải pháp tốt nhất?Làm cách nào để thực hiện phân chia số nguyên (đã ký hoặc chưa ký) trên ARM?

Chúc mừng! =)

+0

Tất nhiên trình biên dịch sẽ hỗ trợ phân chia số nguyên trong chế độ phần mềm ngay cả khi không có trong phần cứng. Tôi nghi ngờ những chip spec cao không có phân chia số nguyên. Tôi nghĩ rằng ATMega (như Arduino) thiếu nó. – leppie

+5

Lệnh ghép nối để phân chia số nguyên trên ARM không tồn tại. – Phonon

+1

Hoặc chuyển đổi sang phao hoặc thực hiện phân chia thủ công với mẫu mã opcode 3 chưa được kiểm soát. –

Trả lời

4

Trình biên dịch thường bao gồm một phân chia trong thư viện của mình, gcclib ví dụ tôi đã chiết xuất chúng từ gcc và sử dụng chúng trực tiếp:

https://github.com/dwelch67/stm32vld/ sau đó stm32f4d/phiêu lưu/gcclib

sẽ nổi và trở lại có lẽ không phải là giải pháp tốt nhất. bạn có thể thử nó và xem cách nhanh chóng đó là như thế nào ... Đây là một nhân nhưng có thể dễ dàng làm cho nó một phân chia:

https://github.com/dwelch67/stm32vld/ sau đó stm32f4d/float01/vectors.s

tôi didnt thời gian nó mặc dù để xem nhanh/chậm. Hiểu rằng tôi đang sử dụng vỏ não-m ở trên và bạn đang nói về vỏ não-a, các đầu khác nhau của quang phổ, hướng dẫn phao tương tự và công cụ lib gcc tương tự, cho vỏ não-m tôi phải tạo ngón tay cái nhưng bạn có thể cũng dễ dàng xây dựng cho cánh tay. Trên thực tế với gcc nó nên tất cả chỉ làm việc automagically bạn không cần phải làm điều đó theo cách tôi đã làm nó. Các trình biên dịch khác cũng như bạn không cần phải làm theo cách tôi đã làm trong trò chơi phiêu lưu ở trên.

7

Một số bản sao-mì ống từ nơi khác để chia số nguyên: Về cơ bản, 3 hướng dẫn cho mỗi bit. Từ trang web this, mặc dù tôi đã nhìn thấy nó nhiều nơi khác nữa. This trang web cũng có một phiên bản tốt đẹp có thể nhanh hơn nói chung.


@ 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

Đây là 3 hướng dẫn cho mỗi bit nhưng không phải 3 chu kỳ mỗi bit. Tất cả các hướng dẫn trong mỗi bước phụ thuộc ngay vào cài đặt cờ của bước trước, điều này có nghĩa là độ trễ kết quả là 3-4 chu kỳ tùy thuộc vào lõi. Điều này có thể sẽ mất 9-12 chu kỳ mỗi bước, với tổng số ~ 360 chu kỳ. –

+0

Nghe có vẻ đúng. Đảo ngược nhân điểm cố định luôn luôn là một lựa chọn tốt hơn nếu bạn có thể xoay nó. –

2

Tôi đã viết thói quen của riêng mình để thực hiện một bộ phận không dấu như tôi không thể tìm thấy phiên bản chưa ký trên web. Tôi cần phải phân chia một giá trị 64 bit với một giá trị 32 bit để có được một kết quả 32 bit.

Vòng lặp bên trong không hiệu quả như giải pháp đã ký được cung cấp ở trên, nhưng điều này không hỗ trợ số học chưa ký. Quy trình này thực hiện phân chia 32 bit nếu phần cao của tử số (hi) nhỏ hơn mẫu số (den), nếu không thì phân chia 64 bit đầy đủ (hi: lo/den). Kết quả là trong 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 

Kiểm tra thêm điều kiện biên và công suất 2 có thể được thêm vào. Chi tiết đầy đủ có thể được tìm thấy tại http://www.idwiz.co.za/Tips%20and%20Tricks/Divide.htm

10

Division bởi một giá trị không đổi được thực hiện một cách nhanh chóng bằng cách làm một 64bit-nhân và chuyển sang bên phải, ví dụ, như thế này:

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

đây R1 được chia cho 1625. Việc tính toán được thực hiện như thế này: 64bitreg (R2: R3) = R1 * 0xA151C331, sau đó kết quả là 32bit trên bên phải chuyển bởi 10:

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

Bạn có thể tính toán hằng của riêng bạn từ công thức này:

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

chọn n lớn nhất, mà Một < 2^32

+1

Có lỗi làm tròn ở đây. Đối với phân chia 32 bit không dấu bằng N = 7, chúng ta có n = 2 và A = 2454267026.28 ... Nếu chúng ta làm tròn giá trị của A chúng ta chọn, thì nó cho kết quả quá nhỏ cho "4294967292/7". Nếu chúng ta làm tròn nó lên, thì nó cho kết quả quá lớn cho "4294967291/7". Điều này chỉ có thể xảy ra nếu phần phân số của giá trị chính xác của A nhỏ hơn 0,5, do đó, nó hoạt động tốt cho khoảng một nửa giá trị của N (như 3, 5 hoặc 1625). –

0

tôi đã viết các chức năng sau cho ARM GNU lắp ráp. Nếu bạn không có CPU có hỗ trợ máy udiv/sdiv, chỉ cần cắt một vài dòng đầu tiên lên nhãn "0:" trong một trong hai chức năng.

.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 

Có hai hàm, udiv cho phân chia số nguyên không dấu và sdiv cho phân chia số nguyên đã ký. Cả hai đều mong đợi một cổ tức 64 bit (đã ký hoặc chưa ký) trong r1 (từ cao) và r0 (từ thấp) và một số chia 32 bit trong r2. Chúng trả về thương số trong r0 và số còn lại trong r1, do đó bạn có thể xác định chúng trong một số C headerextern trả lại số nguyên 64 bit và che dấu thương và số dư sau đó. Lỗi (phân chia bởi 0 hoặc tràn) được biểu thị bằng phần còn lại có giá trị tuyệt đối lớn hơn hoặc bằng giá trị tuyệt đối của số chia. Thuật toán phân chia đã ký sử dụng phân biệt chữ hoa với các dấu hiệu của cả cổ tức và số chia; nó không chuyển đổi sang số nguyên dương đầu tiên, vì điều đó sẽ không phát hiện tất cả các điều kiện tràn đúng cách.