Forum: Mikrocontroller und Digitale Elektronik AVR C/ASM Verständnisfrage


von pic u. (pic_user)


Lesenswert?

Ich habe hier eine C Zeile mit einem Code, und verstehe nicht, wieso
da mulhi3 aufgerufen werden soll. Kann mir das bitte jemand erklären 
oder
ist es ein bekannter BUG ?

Danke für die Antworten.

CC Arguments sind folgende:
bin\avr-gcc %* -Wa,-almshd=a.lst   -mcall-prologues -Os -mmcu=attiny24 
-Wl,-Map -Wl,a.map

for(EEAR = (unsigned int)&cfg_ee,flag=;EEARL<(sizeof(cfg)+(unsigned 
int)&cfg_ee);CAST(char*,cfg-(unsigned int)&cfg_ee)[EEARL++]=~EEDR) 
EECR|=(1<<EERE); // copy EEprom to data
 6f4:  82 e0         ldi  r24, 0x02  ; 2
 6f6:  90 e0         ldi  r25, 0x00  ; 0
 6f8:  9f bb         out  0x1f, r25  ; 31
 6fa:  8e bb         out  0x1e, r24  ; 30
 6fc:  2e b3         in  r18, 0x1e  ; 30
 6fe:  2e 5e         subi  r18, 0xEE  ; 238
 700:  23 bb         out  0x13, r18  ; 19
 702:  62 e1         ldi  r22, 0x12  ; 18
 704:  70 e0         ldi  r23, 0x00  ; 0
 706:  50 d0         rcall  .+160      ; 0x7a8 <__mulhi3>
 708:  aa 27         eor  r26, r26
 70a:  bb 27         eor  r27, r27
 70c:  a8 1b         sub  r26, r24
 70e:  b9 0b         sbc  r27, r25
 710:  a7 53         subi  r26, 0x37  ; 55
 712:  bf 4f         sbci  r27, 0xFF  ; 255
 714:  0c c0         rjmp  .+24       ; 0x72e <main+0x82>
 716:  e0 9a         sbi  0x1c, 0  ; 28
 718:  9e b3         in  r25, 0x1e  ; 30
 71a:  8d b3         in  r24, 0x1d  ; 29
 71c:  ed 91         ld  r30, X+
 71e:  fc 91         ld  r31, X
 720:  11 97         sbiw  r26, 0x01  ; 1
 722:  e9 0f         add  r30, r25
 724:  f1 1d         adc  r31, r1
 726:  80 95         com  r24
 728:  80 83         st  Z, r24
 72a:  9f 5f         subi  r25, 0xFF  ; 255
 72c:  9e bb         out  0x1e, r25  ; 30
 72e:  9e b3         in  r25, 0x1e  ; 30
 730:  83 b3         in  r24, 0x13  ; 19
 732:  98 17         cp  r25, r24
 734:  80 f3         brcs  .-32       ; 0x716 <main+0x6a>



// cut //

000007a8 <__mulhi3>:
 7a8:  55 27         eor  r21, r21
 7aa:  00 24         eor  r0, r0

000007ac <__mulhi3_loop>:
 7ac:  80 ff         sbrs  r24, 0
 7ae:  02 c0         rjmp  .+4        ; 0x7b4 <__mulhi3_skip1>
 7b0:  06 0e         add  r0, r22
 7b2:  57 1f         adc  r21, r23

000007b4 <__mulhi3_skip1>:
 7b4:  66 0f         add  r22, r22
 7b6:  77 1f         adc  r23, r23
 7b8:  61 15         cp  r22, r1
 7ba:  71 05         cpc  r23, r1
 7bc:  21 f0         breq  .+8        ; 0x7c6 <__mulhi3_exit>
 7be:  96 95         lsr  r25
 7c0:  87 95         ror  r24
 7c2:  00 97         sbiw  r24, 0x00  ; 0
 7c4:  99 f7         brne  .-26       ; 0x7ac <__mulhi3_loop>

000007c6 <__mulhi3_exit>:
 7c6:  95 2f         mov  r25, r21
 7c8:  80 2d         mov  r24, r0
 7ca:  08 95         ret

von Sven P. (Gast)


Lesenswert?

Wie lange hast du denn gebraucht, um den Code so unlesbar hinzukriegen? 
Der ist zudem noch fehlerhaft.
1
for (
2
  EEAR = (unsigned int) &cfg_ee, flag=;
3
  EEARL < (sizeof(cfg) + (unsigned int) &cfg_ee);
4
  CAST(char *, cfg - (unsigned int) &cfg_ee)[EEARL++] = ~EEDR
5
) 
6
    EECR |= (1 << EERE); // copy EEprom to data

Es gibt übrigens auch fertige Routinen in der avr-libc für sowas. Die 
sollte man auch tunlichst verwenden, wegen der Zeigerproblematik.

von pic u. (pic_user)


Lesenswert?

sorry, dies ist der korrekte Code.
cfg_ee ist ein Strukt im EEprom, cfg ist dasselbe Strukt im Ram.
CAST ist einfach ein Cast    *(char*)(&cfg-(unsigned int)&cfg_ee)
Der Grund wieso ich die vorgefertigten Funktionen nicht verwenden will
ist die Codegröße. Trotzdem ist die Verwendung von mulhi3 für mich
merkwürdig.

  for(EEAR = (unsigned int)&cfg_ee;EEARL<(char)(sizeof(cfg)+(unsigned 
int)&cfg_ee);CAST(char*,cfg-(unsigned int)&cfg_ee)[EEARL++]=~EEDR) 
EECR|=(1<<EERE); // copy EEprom to data
 6f4:  22 e0         ldi  r18, 0x02  ; 2
 6f6:  30 e0         ldi  r19, 0x00  ; 0
 6f8:  3f bb         out  0x1f, r19  ; 31
 6fa:  2e bb         out  0x1e, r18  ; 30
 6fc:  c9 01         movw  r24, r18
 6fe:  62 e1         ldi  r22, 0x12  ; 18
 700:  70 e0         ldi  r23, 0x00  ; 0
 702:  55 d0         rcall  .+170      ; 0x7ae <__mulhi3>
 704:  aa 27         eor  r26, r26
 706:  bb 27         eor  r27, r27
 708:  a8 1b         sub  r26, r24
 70a:  b9 0b         sbc  r27, r25
 70c:  a7 53         subi  r26, 0x37  ; 55
 70e:  bf 4f         sbci  r27, 0xFF  ; 255
 710:  2e 5e         subi  r18, 0xEE  ; 238
 712:  33 27         eor  r19, r19
 714:  27 fd         sbrc  r18, 7
 716:  30 95         com  r19
 718:  0c c0         rjmp  .+24       ; 0x732 <main+0x86>
 71a:  e0 9a         sbi  0x1c, 0  ; 28
 71c:  9e b3         in  r25, 0x1e  ; 30
 71e:  8d b3         in  r24, 0x1d  ; 29
 720:  ed 91         ld  r30, X+
 722:  fc 91         ld  r31, X
 724:  11 97         sbiw  r26, 0x01  ; 1
 726:  e9 0f         add  r30, r25
 728:  f1 1d         adc  r31, r1
 72a:  80 95         com  r24
 72c:  80 83         st  Z, r24
 72e:  9f 5f         subi  r25, 0xFF  ; 255
 730:  9e bb         out  0x1e, r25  ; 30
 732:  8e b3         in  r24, 0x1e  ; 30
 734:  90 e0         ldi  r25, 0x00  ; 0
 736:  82 17         cp  r24, r18
 738:  93 07         cpc  r25, r19
 73a:  7c f3         brlt  .-34       ; 0x71a <main+0x6e>

von Stefan E. (sternst)


Lesenswert?

pic user schrieb:
> Trotzdem ist die Verwendung von mulhi3 für mich
> merkwürdig.

Das dürfte einfach das Resultat des verqueren Codes sein.
1
*(char*)(&cfg-(unsigned int)&cfg_ee)
Wenn du von einem Pointer auf ein Struct einen Integer-Wert abziehst, 
muss ja von der Adresse das Ergebnis von (Integer-Wert * sizeof(Struct)) 
abgezogen werden. Da ist deine Multiplikation.

Für mich macht deine ganze Pointer-Arithmetik keinen Sinn.
> cfg_ee ist ein Strukt im EEprom, cfg ist dasselbe Strukt im Ram.
Und warum sollte dann überhaupt irgendein Bedarf bestehen, die Adressen 
der beiden irgendwie miteinander zu verrechnen?

pic user schrieb:
> Der Grund wieso ich die vorgefertigten Funktionen nicht verwenden will
> ist die Codegröße.

Und auch das klingt für mich ziemlich unsinnig.

von pic u. (pic_user)


Lesenswert?

> Das dürfte einfach das Resultat des verqueren Codes sein.
schein so zu sein.

> Wenn du von einem Pointer auf ein Struct einen Integer-Wert abziehst,
> muss ja von der Adresse das Ergebnis von (Integer-Wert * sizeof(Struct))
> abgezogen werden. Da ist deine Multiplikation.

Wobei, ich habe mulh3 nicht nachfollzogen scheint aber ein *3 zu sein.
Aber das Strukt ist 18 bytes groß und es bekommt 18 als input.

> Für mich macht deine ganze Pointer-Arithmetik keinen Sinn.
>> cfg_ee ist ein Strukt im EEprom, cfg ist dasselbe Strukt im Ram.
> Und warum sollte dann überhaupt irgendein Bedarf bestehen, die Adressen
> der beiden irgendwie miteinander zu verrechnen?
Weil mit dem Array im Ram gearbeitet wird welches vom EEprom 
initialisiert
wird. Das Array sind eigentlich statische Werte, welche im 
Konfigurationsmodus geändert sowie dann als ganzes ins EEprom 
gespeichert werden.
>
> pic user schrieb:
>> Der Grund wieso ich die vorgefertigten Funktionen nicht verwenden will
>> ist die Codegröße.
>
> Und auch das klingt für mich ziemlich unsinnig.

Code hat jemand anders geschrieben, ich wurde gebeten die Softwäre zu 
ändern, mehr Funktionalität reinzubringen, und von 1.5kbyte sind es 
derzeit
2150byte welche ich nocht auf die 2k Grenze runterbringen muss.

Danke vielmals für deinen Support.

Zum Scluss noch der Code:

  for(EEAR = (unsigned 
int)&cfg_ee;EEARL-(uchar)((uint)&cfg_ee+sizeof(cfg));CAST(char*,cfg)[EEA 
RL++-(uchar)(uint)&cfg_ee]=~EEDR)  EECR|=(1<<EERE); // copy EEprom to 
data
 6f4:  82 e0         ldi  r24, 0x02  ; 2
 6f6:  90 e0         ldi  r25, 0x00  ; 0
 6f8:  9f bb         out  0x1f, r25  ; 31
 6fa:  8e bb         out  0x1e, r24  ; 30
 6fc:  68 2f         mov  r22, r24
 6fe:  6e 5e         subi  r22, 0xEE  ; 238
 700:  48 2f         mov  r20, r24
 702:  50 e0         ldi  r21, 0x00  ; 0
 704:  11 c0         rjmp  .+34       ; 0x728 <main+0x7c>
 706:  e0 9a         sbi  0x1c, 0  ; 28
 708:  3e b3         in  r19, 0x1e  ; 30
 70a:  2d b3         in  r18, 0x1d  ; 29
 70c:  83 2f         mov  r24, r19
 70e:  90 e0         ldi  r25, 0x00  ; 0
 710:  84 1b         sub  r24, r20
 712:  95 0b         sbc  r25, r21
 714:  e0 91 c9 00   lds  r30, 0x00C9
 718:  f0 91 ca 00   lds  r31, 0x00CA
 71c:  e8 0f         add  r30, r24
 71e:  f9 1f         adc  r31, r25
 720:  20 95         com  r18
 722:  20 83         st  Z, r18
 724:  3f 5f         subi  r19, 0xFF  ; 255
 726:  3e bb         out  0x1e, r19  ; 30
 728:  8e b3         in  r24, 0x1e  ; 30
 72a:  86 17         cp  r24, r22
 72c:  61 f7         brne  .-40       ; 0x706 <main+0x5a>

von pic u. (pic_user)


Lesenswert?

Im Vergleich zur stdlib spart dieser unleserliche Code 62 bytes flash,
getestet wie folgt:

#if 1
#define EEREAD   (1<<EERE)
#define EEWRITE  (1<<EEPE)|(1<<EEMPE)
void ee(char x) { char i; EECR=0;
  for(i=sizeof(cfg)-1,EEAR = (unsigned 
int)&cfg_ee;EEDR=CAST(char*,cfg)[i++],i--;EECR = 
(1<<EEMPE),EECR=x,CAST(char*,cfg)[i]=EEDR,--EEARL)  while(EECR & 
(1<<EEPE));
}

#else
#include <avr/eeprom.h>
#if 1
void ee(char x) { if (x==EEREAD) // +62 bytes flash
   eeprom_read_block((void*)&cfg, (const void*)&cfg_ee, sizeof(cfg));
   else
   eeprom_write_block((const void*)&cfg_ee,(void*)&cfg,  sizeof(cfg));

}
#else
#define EEREAD 0
#define EEWRITE 1
void ee(char x) { char i;  // +78 bytes flash
  for(i=sizeof(cfg);i--;CAST(char*,cfg)[i]=eeprom_read_byte(&cfg_ee+i)) 
if(x) eeprom_write_byte(&cfg_ee+i,CAST(char*,cfg)[i]);
}
#endif
#endif

und hier der ASM code.

#if 1
#define EEREAD   (1<<EERE)
#define EEWRITE  (1<<EEPE)|(1<<EEMPE)
void ee(char x) { char i; EECR=0;
 6ac:  28 2f         mov  r18, r24
 6ae:  1c ba         out  0x1c, r1  ; 28
  for(i=sizeof(cfg)-1,EEAR = (unsigned 
int)&cfg_ee;EEDR=CAST(char*,cfg)[i++],i--;EECR = 
(1<<EEMPE),EECR=x,CAST(char*,cfg)[i]=EEDR,--EEARL)  while(EECR & 
(1<<EEPE));
 6b0:  82 e0         ldi  r24, 0x02  ; 2
 6b2:  90 e0         ldi  r25, 0x00  ; 0
 6b4:  9f bb         out  0x1f, r25  ; 31
 6b6:  8e bb         out  0x1e, r24  ; 30
 6b8:  94 e0         ldi  r25, 0x04  ; 4
 6ba:  09 c0         rjmp  .+18       ; 0x6ce <ee+0x22>
 6bc:  e1 99         sbic  0x1c, 1  ; 28
 6be:  fe cf         rjmp  .-4        ; 0x6bc <ee+0x10>
 6c0:  9c bb         out  0x1c, r25  ; 28
 6c2:  2c bb         out  0x1c, r18  ; 28
 6c4:  8d b3         in  r24, 0x1d  ; 29
 6c6:  81 8b         std  Z+17, r24  ; 0x11
 6c8:  8e b3         in  r24, 0x1e  ; 30
 6ca:  81 50         subi  r24, 0x01  ; 1
 6cc:  8e bb         out  0x1e, r24  ; 30
 6ce:  e0 91 c9 00   lds  r30, 0x00C9
 6d2:  f0 91 ca 00   lds  r31, 0x00CA
 6d6:  81 89         ldd  r24, Z+17  ; 0x11
 6d8:  8d bb         out  0x1d, r24  ; 29
 6da:  f0 cf         rjmp  .-32       ; 0x6bc <ee+0x10>

von Rolf M. (rmagnus)


Lesenswert?

Das großflächige Entfernen von Leerzeichen und Zeilenumbrüchen im 
Quellcode wird aber keinen Flash sparen. Das macht den Code nur noch 
unsleserlicher, als er eh schon ist. Und ich kann mir auch nicht so 
recht vorstellen, daß du mehr sparst, wenn du die gesamte Funktionalität 
in den Schleifenkopf steckst.

Mir scheint da aber auch ein logischer Fehler drin zu sein. Bei jedem 
Schleifendurchlauf inkrementierst du i und dekrementierst es wieder. Da 
du es als Abbruchkriterium nutzt, müßte das eine Endlosschleife ergeben. 
Ich sehe im Assemblercode auch keine Stelle, an der er wieder aus der 
Schleife rauskommt.

von Sven P. (Gast)


Lesenswert?

Der Quelltext wäre ein super Kündigungsgrund, ehrlich.

von pic u. (pic_user)


Lesenswert?

@Rolf Magnus

Danke, ich schlage mich mit dem Code schon zu lange rum, damit mir 
solche
idiotische Fehler unterlaufen. Dies reduziert leider auch die Anzahl
der gewonnen bytes, leider.

Ich hoffe ich bin fertig geworden, das wird sich morgen zeigen.


Mit folgendem Code sind es:
#define CFG_GET  (1<<EERE)
#define CFG_SET  (1<<EEPE)|(1<<EEMPE)

static void inline config_update(char type) {
  char i,j;
  for(j=cfg.ee_limit,i=0,EECR=0,EEAR=(unsigned int)&cfg_ee;j--;EEARL++)
    { while(EECR & (1<<EEPE));EEDR=CAST(char*,cfg)[i]; EECR = 
(1<<EEMPE),EECR=type,CAST(char*,cfg)[i++]=EEDR;}
}

AVR Memory Usage
----------------
Device: attiny24

Program:    2048 bytes (100.0% Full)
(.text + .data + .bootloader)

Data:         96 bytes (75.0% Full)
(.data + .bss + .noinit)

EEPROM:      127 bytes (99.2% Full)
(.eeprom)

und mit diesem:

#include <avr/eeprom.h>
static void inline config_update(char type) { if (type==EEREAD) // +62 
bytes flash
   eeprom_read_block((void*)&cfg, (const void*)&cfg_ee, cfg.ee_limit);
   else
   eeprom_write_block((const void*)&cfg_ee,(void*)&cfg, cfg.ee_limit);

}
AVR Memory Usage
----------------
Device: attiny24

Program:    2094 bytes (102.2% Full)
(.text + .data + .bootloader)

Data:         96 bytes (75.0% Full)
(.data + .bss + .noinit)

EEPROM:      127 bytes (99.2% Full)
(.eeprom)

von Peter D. (peda)


Lesenswert?

Sven P. schrieb:
> Es gibt übrigens auch fertige Routinen in der avr-libc für sowas. Die
> sollte man auch tunlichst verwenden, wegen der Zeigerproblematik.

Irgendwann wurde die EEPROM-Lib auf indirekten Funktionscall umgestellt 
und hat riesig Flash verbraucht.
Ich hab daher meine eigene Funktion geschrieben.
Da Schreiben und Lesen viel gemeinsam hat, habe ich beides in eine 
Funktion gepackt. Das spart nochmals Code. Obendrein wird unnützes 
Schreiben vermieden. Man kann also immer die gesamte Struct sichern, 
auch wenn sich nur ein Element geändert hat.
1
#include <util\atomic.h>
2
3
#define  EESTART    0x0001
4
5
#define  EE_WRITE  1
6
#define  EE_READ    0
7
8
void eeprom( uint8_t *sram, uint16_t eep, uint16_t len, uint8_t write )
9
{
10
  uint8_t edat;
11
12
  do{
13
    while( EECR & 1<<EEPE );    // wait until previous write done
14
    EEAR = eep;
15
    EECR |= 1<<EERE;            // read pulse
16
    edat = EEDR;
17
    if( write ){                // if write flag
18
      if( edat != *sram ){      // and not equal
19
        EEDR = *sram;           // write EEPROM
20
        ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
21
          EECR |= 1<<EEMPE;
22
          EECR |= 1<<EEPE;      // write pulse
23
        }
24
      }
25
    }else{
26
      *sram = edat;             // read EEPROM
27
    }
28
    sram++;
29
    eep++;                      // next address
30
  }while( --len );
31
}
32
33
void save_eedata()
34
{
35
  eeprom( (void*)&eedata, EESTART, sizeof( eedata ), EE_WRITE );
36
}
37
38
void read_eedata()
39
{
40
  eeprom( (void*)&eedata, EESTART, sizeof( eedata ), EE_READ );
41
}

eedata ist die Struct im SRAM.


Peter

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

pic user schrieb:

> Dies reduziert leider auch die Anzahl
> der gewonnen bytes, leider.

Lass einfach die Hacks. Das ist Käse, nur um ein paar Bytes zu sparen!

Verursachen diese paar Bytes mehr denn wirklick Probleme ?

Oder ist's von der Sorte "Ich weis da was besser als der Compiler und 
will auf Biegen nd Brechen das Byte sparen"?

Wenn dich die Multiplikation sooo nervt oder wirklich absolut 
inakzeptabel ist, dann verabschiede dich von deiner alten Toolchain! Und 
verwende stattdessen was zeitgemässes wie zB avr-gcc 4.7.1.

Der macht die Multiplikation nicht mehr per libgcc-Aufruf, zumindest 
falls der µC über MUL verfügt.

von pic u. (pic_user)


Lesenswert?

@Peter Dannegger
Danke, ein wirklich netter code, und er zeigt mir auch wie man das
mit den   ATOMIC_BLOCK machen sollte. Nach ein paar defines konnte ich 
es mit -std=c99 compilieren. Beides werde ich in Zukunft nutzen.

Gegenüber der AVR Lib erspart er 20 byte code (version 4.3.3 von GCC)

Inzwischen ist der Code auch mit der realen HW getestet und läuft 
soweit.

@ Johann L. (gjlayde)
Der Unterschied ist, ob das Programm in den Prozessor reinpasst oder 
nicht.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

pic user schrieb:

> @ Johann L. (gjlayde)
> Der Unterschied ist, ob das Programm in den Prozessor reinpasst oder
> nicht.

Lohnt sich denn -mcall-prologues? Das bringt idR nur was bei extrem 
teuren Funktionen wie float-Berechnungen. Andsonsten hat man eher 
billige Prologe, und mit -mcall-prologues wird der Code gerne mal 
größer.

Neben -Os gibt's noch weitere Optionen die man antesten kann wie 
-fno-split-wide-types, -fno-inline-small-functions, -morder1 bzw. 
-morder2 und natürlich generell intelligentere Unsetzung.

Gibt es denn ein nicht verhuntzes, übersetzbares Beispiel für den Code 
von oben?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.