tinyAVR:如何快速乘以203,171或173?

gre*_*ard 5 algorithm avr multiplication

着眼于最坏情况周期计数,我为Atmel的AVR架构编码了整数乘法例程.
在一个特定的实现中,我遇到了2 + 1个最坏的情况,我为此寻求更快的实现.这些乘法乘法器具有偶数个字节,乘法器的8位部分的已知值:
* 11001011(203 10)
* 10101011(171 10)
* 10101101(173 10)
GCC(4.8.1)将这些计算为*29*7,*19*9和*(43*4 + 1) - 非常适合3地址机器,tinyAVR不是(相当:大多数寄存器对的移动速度是添加速度的两倍).对于双字节被乘数和乘积,它使用9 + 2,10 + 2和11 + 2加法(和减法)并分别移动20,22和24个周期.Radix-4 Booth将使用11 + 1次添加(在不完全可比的条件下)和23次循环.
由于超出这个问题的原因,我有16*被乘数预先计算(a5:a47个循环,包括移动); 原始和移位的被乘数都可以在以后使用(但对于MSByte).并且产品初始化为以下汇编程序代码片段的被乘数(其中我使用Booth样式的重新编码表示法:.对于NOP +,和-.owing是标签"之前的一条指令done",执行单周期修复) :

locb:; .-..+.++     ..--.-.- ++..++.-   ++.+.-.-    ..--.-.- 29*7
; WC?!? 11001011                                            s   18
    add p0, p0;15   n   16   a4 15      s   16      n   15  s0  17
    adc p1, p1
    sub p0, a4;13   d   13   z  13      s0  15      s4  12  d   15
    sbc p1, a5
    add p0, p0;11   s4  11   d  12      z   13      z   10  s0  13
    adc p1, p1
    add p0, a0; 9   d   9    aZ 10      a4  12      s0  9   z   11
    adc p1, a1
    add p0, p0; 7   s4  7    d  8       a4  10      d   7   d   10
    adc p1, p1
    add p0, a0; 5   s0  5    d  6       d   8       az  5   aZ  8
    adc p1, a1
    rjmp owing; 3   owi 3    s0 4       d   6       owi 3   d   6
              ;                         aZ  4               aZ  4
Run Code Online (Sandbox Code Playgroud)

(注释从左到右,循环计数("到达后退done"),以及使用"标签行"中相同列中的重新编码的其他代码序列,使用n否定的简写,d为double(部分) )产物,a0/ a4/ s0/ s4用于添加或减去被乘数移位0或4比特的左边,z用于存储在ZL的产品:ZH,和aZ/ sZ用于使用).
使用宏(另一个最坏的情况下和上述速记):

loab:; .-.-.-.- +.++.-.-    +.+.+.++    .-.-+.++    +.+.++.-
; WC    10101011
    negP    ;15 set4    ;16 add4    ;15 d   16      a4  16
    sub4    ;12 doubleP ;15 pp2Z    ;13 s4  14      d   14
    pp2Z    ;10 subA    ;13 doubleP ;12 z   12      a0  12
    doubleP ; 9 pp2Z    ;11 doubleP ;10 d   11      d   10
    doubleP ; 7 doubleP ;10 addZ    ; 8 d   9       a4  8
    addZ    ; 5 doubleP ; 8 doubleP ; 6 aZ  7       d   6
    owing   ; 3 addZ    ; 6 addA    ; 4 a0  5       s0  4
            ;   add4    ; 4
loac:; +.+.++.. ++-.++.. .-.-.-..
load: ; .-.-..--    .-.-.-.+    .--.++.+    0.9 1.8 0.8 (avg)
; WC    10101101
    negP    ;15 -1      negP    ;16 a4  a0  a0  17
    sub4    ;12 -16-1   sub4    ;13 d   s4  a0
    pp2Z    ;10 -16-1   doubleP ;11 a0  Z   s4
    sub4    ; 9 -32-1   doubleP ; 9 d   d   d
    doubleP ; 7 -64-2   sub4    ; 7 a4  aZ  d
    addZ    ; 5 -80-3   addA    ; 5 d   d   a0
    owing   ; 3         owing   ; 3 a0  a0  s4
Run Code Online (Sandbox Code Playgroud)

(我不会在任何一次调用中找到多个结果/作为任何单个调用的结果 - 但是如果你有办法在少于23个周期内获得两个或者在少于26个周期中获得全部三个,请让我知道.为了证实我对CSE的了解,(重新)使用vlad_tepesch引入的符号[rl] sh16和add16:

movw        C, x    ; 1
lsh16(C)            ; 3 C=2x
movw        A, C    ; 4
swap        Al      ; 5 
swap        Ah      ; 6
cbr         Ah, 15  ; 7
add         Ah, Al  ; 8
cbr         Al, 15  ; 9
sub         Ah, Al  ;10 A=32x
movw        X, A    ;11
add16(X, C)         ;13 X=34x
movw        B, X    ;14
lsh16(X)            ;16
lsh16(X)            ;18 X=136X
add16(B, X)         ;20 B=170X
add16(B, x)         ;22 B=171X
add16(A, B)         ;24 A=203X
add16(C, B)         ;26 C=173X
Run Code Online (Sandbox Code Playgroud)

- 注意到第一个结果的22个周期是相同的旧20个周期,加上两个寄存器对移动.除此之外的动作序列是在loab上面的标签之后的第三列/替代的行动.)
虽然20个周期(15-2(rjmp)+7(*16)看起来不那么糟糕,但这些最坏的情况.一个没有mul指令的AVR CPU,
如何通过203,171和173中的每一个快速乘以实数
(前一个移动一个案例,done或者owing让其他两个更快,将缩短关键路径/改善最坏情况周期数.)

vla*_*sch 1

我对 AVR-asm 不是很熟悉,但我很了解 AVR,所以我尝试一下

如果您在同一位置需要这些产品,您可以使用常见的中间结果并尝试添加 2 的倍数。

(a) 203: +128 +64 +8 +2 +1 =  +128 +32 +8 +2 +1   +32
(b) 171:                      +128 +32 +8 +2 +1
(c) 173: +128 +32 +8 +4 +1 =  +128 +32 +8 +2 +1   +2
Run Code Online (Sandbox Code Playgroud)

关键是16位右移和16位加法必须高效。我不知道我是否遗漏了什么,但是:

rsh16 (X): 
  LSR Xh
  ROR Xl
Run Code Online (Sandbox Code Playgroud)

add16 (Y,X)
  ADD  Yl, Xl
  ADDC Yh, Xh
Run Code Online (Sandbox Code Playgroud)

都是2个周期。

一对寄存器保存当前的 x*2^n 值 (Xh, Xl)。其他 3 对(Ah、Ab、Bh、Bl、Ch、Cl)保存结果。

 1. Xh <- x;  Xl <- 0    (X = 256*x)
 2. rsh16(X)             (X = 128*x) 
 3. B = X                (B = 128*x)
 4. rsh16(X); rsh16(X)   (X =  32*x)
 5. add16(B, X)          (B = 128*x + 32*x)
 6. A = X                (A =  32*X)
 7. rsh16(X); rsh16(X)   (X =   8*x)
 8. add16(B, X)          (B = 128*x + 32*x+ 8*x)
 9. rsh16(X); rsh16(X)   (X =   2*x)
10. add16(B, X)          (B = 128*x + 32*x + 8*x + 2*x)
11. C = X                (C =   2*X)
12. CLR  Xh              (clear Xh so we only add the carry below)
    add  Bl, x           
    addc Bh, Xh          (B = 128*x + 32*x + 8*x + 2*x + x) 
13. add16(A, B)          (A = 32*X + B)
14. add16(C, B)          (C =  2*X + B)
Run Code Online (Sandbox Code Playgroud)

如果我是正确的,那么所有三个乘法总共需要 32 个周期,并且需要 9 个寄存器(1 个输入、6 个输出、2 个临时寄存器)