PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : WinAvr: 16bit * 16bit ? Wie gehts ?



DerMarkus
26.12.2005, 19:05
Hi Forum,
ich verwende WinAvr ( WinAVR-20050214 ) und habe ein Problem mit der
Mulitplikation zweier 16Bit Variablen. Hier mein Code:

typedef unsigned int us16;
typedef unsigned long us32;

us16 a = 8192;
us16 b = 30000;
us32 c = 0;

c = a * b;

printf("a=%u\r\n", a);
printf("b=%u\r\n", b);
printf("c=%lu\r\n", c);

c = 245760000UL;

printf("c=%lu\r\n", c);

Die Ausgabe ist:
a=8192
b=30000
c=0
c=245760000

Warum ist c = 0 ? Eigentlich erwarte ich 245760000. Da c 32Bit gross ist
sollte auch kein Überlauf auftreten. Wenn ich c = 245760000UL setzte
dann wird der Wert von printf richtig ausgegeben. An der Ausgabe kann es nicht liegen.
Also warum ist c = 0 und wie bekomm ich den AVR dazu den Wert korrekt zu berechnen ?

linux_80
26.12.2005, 19:54
Hallo,
Du musst vor dem rechnen a und b in long umwandeln, damit long rauskommt, kannst mal probieren:

c = (long)a * (long)b;

DerMarkus
27.12.2005, 14:32
hi linux,
ok mit dem long Cast klappt es, dank dir. Allerdings ist mir nicht klar warum ich eine 16Bit Variable erst in 32Bit konvertieren muss ? Nach einem Cast auf long wird für die Multiplikation __mulsi3 aufgerufen, d.h. es wird wirklich 32Bit*32Bit gerechnet obwohl 16Bit*16Bit ausreichen würde. Da geht mir mein kompletter Geschwindigkeitsvorteil flöten. In den App Notes von Atmel sind Assembler Beispiele in den 16Bit*16Bit=32Bit gerechnet wird, die sind erheblich schneller. Kann ich den compiler austricksen oder muss ich die Multiplikation selber implementieren ?

SprinterSB
02.01.2006, 14:40
evtl gibts ne __umulhisi3 oder __mulhisi3.
Am besten schaust das in der Maschinenbeschreibung nach oder in der Quelle der libgcc2 (libgcc2.S)

DerMarkus
02.01.2006, 21:19
Danke für den Tip. Ich habe mir mittlerweile eine Assembler Funktion geschrieben. für eine 16Bit*16Bit=32Bit Multiplikation braucht sie nur 24 Takte :)

michaelb
02.01.2006, 21:31
Hi,
kannst du mal die ASM Funktion posten?
Gruß Michi

SprinterSB
03.01.2006, 14:58
Entsprechende asm-Quellen findest du bei den Compilerquellen, und zwar in der libgcc2.S :-)

DerMarkus
03.01.2006, 21:52
Hier meine Funktion:

#include "avr/io.h"

;------------------------------------------------------------------------------
; Funktion : Unsigned Integer Multiplikation
; Taktzyklen: 24
; Register : r0 r1 r18 r19 r20 r21 r22 r23 r24 r25 r30
;
; 32Bit = 16Bi * 16Bit
; CH:CMH:CML:CL = AH:AL * BH:BL
;------------------------------------------------------------------------------
; Unsigned 16Bit Faktor A
#define AL r24
#define AH r25

; Unsigned 16Bit Faktor B
#define BL r22
#define BH r23

; Unsigned 32Bit Produkt C
#define CL r22
#define CML r23
#define CMH r24
#define CH r25

; 0 Register
#define ZERO r30

; Unsigned 16Bit Produkt aus AL * BL
#define ALBLL r18
#define ALBLH r19

; Unsigned 16bit Produkt aus AH * BH
#define AHBHL r20
#define AHBHH r21

; Unsigned Integer Multiplikation
.global us32Mul16_16_32
.func us32Mul16_16_32
us32Mul16_16_32:
clr ZERO ; r30 = 0 1
mul AH, BH ; AH * BH 2
movw AHBHL, r0 ; (AH * BH) -> (AHBHL:AHBHH) 1
mul AL , BL ; AL * BL 2
movw ALBLL, r0 ; (AL * BL) -> (AHBHL:AHBHH) 1
mul AH, BL ; AH * BL 2
add ALBLH, r0 ; ALBLH + (AH * BL)l 1
adc AHBHL, r1 ; AHBHL + (AH * BL)h 1
adc AHBHH, ZERO; AHBHH + Carry 1
mul BH, AL ; BH * AL 2
add ALBLH, r0 ; ALBLH + (BH * AL)l 1
adc AHBHL, r1 ; AHBHL + (BH * AL)h 1
adc AHBHH, ZERO; AHBHH + Carry 1
movw CL , ALBLL ; ALBLL -> CL 1
movw CMH, AHBHL; AHBHL -> CMH 1
clr r1 ; r1 = 0 1
ret ; return 4
.endfunc
;------------------------------------------------------------------------------

__umulhisi3 und __mulhisi3 aus der libgcc fand ich von der Laufzeit
nicht so berauschend.