Forum: Compiler & IDEs GCC und LDS/STS auf Attiny10


von Tim  . (cpldcpu)


Lesenswert?

Ich kämpfa gerade mit avr-gcc 4.7.2 und einem Attiny 10. Der Compiler 
weigert sich, die kurzen 8-bit LDS und STS für den Speicherzugriff zu 
nutzen, sondern erzeugt stattdessen einen indirekten Zugriff über das 
Z-Register. Das ist natürlich kompletter Unsinn und bläht den Code 
unnötig auf.

Kann man dagegen etwas tun?

z.B.
1
      usbTxLen = wantLen;
2
 3e6:  ed e4         ldi  r30, 0x4D  ; 77
3
 3e8:  f0 e0         ldi  r31, 0x00  ; 0
4
 3ea:  30 83         st  Z, r19
statt:
1
     sts 0x4D,r19

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tim    schrieb:
> Kann man dagegen etwas tun?

Atmel nerven.  Der ganze Reduced-Core-Kram liegt derzeit einzig und
allein bei denen.  Den patchen sie selbst in den GCC rein, und meines
Wissens ist er nach wie vor in einem reichlich halbbackenen Zustand.

: Bearbeitet durch Moderator
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ich denk das ist unabhängig vom Tiny und müsste sich auch mit normalen 
AVRs nachvollziehen lassen.

von Tim  . (cpldcpu)


Lesenswert?

Johann L. schrieb:
> Ich denk das ist unabhängig vom Tiny und müsste sich auch mit normalen
> AVRs nachvollziehen lassen.

Wenn ich den gleichen Source für einem ATtiny85 compiliere, werden an 
den gleichen Stellen 16-Bit "STS" Befehle erzeugt. Irgendwie sieht mir 
das nach einem schnellen Kompatibilitätsfix aus.

Jörg Wunsch schrieb:
> Atmel nerven.

Werde es mal machen.

: Bearbeitet durch User
von Tim  . (cpldcpu)


Lesenswert?

Lustig, es geht gleich weiter. AVR-GCC scheint für den ATtiny 10 andere 
calling conventions als diese hier:

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage

zu verwenden. Auf den normalen Cores können R18/R19 "geclobbered" 
werden, auf dem ATtiny 10 geht der compiler offenbar davon aus, dass 
diese erhalten bleiben. Nur wo ist das dokumentiert?

von (prx) A. K. (prx)


Lesenswert?

Tim    schrieb:
> Lustig, es geht gleich weiter. AVR-GCC scheint für den ATtiny 10 andere
> calling conventions als diese hier:

Nicht ganz überraschend, wenn man bedenkt, dass es R0-R15 nicht gibt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Tim    schrieb:
> Lustig, es geht gleich weiter. AVR-GCC scheint für den ATtiny 10 andere
> calling conventions als diese hier:

Was angesichts des nur halben Registersatzes ja nicht verwunderlich
ist.

> Nur wo ist das dokumentiert?

Atmel fragen. ;-)

von Tim  . (cpldcpu)


Lesenswert?

Jörg Wunsch schrieb:
> Atmel fragen. ;-)

Gerade geschehen :)

von Tim  . (cpldcpu)


Lesenswert?

Naja, sieht aus, als wenn ich einfach noch mehr Assembler verwenden 
muss.
Schade, ich hatte V-USB auf dem Attiny 10 schon so weit, dass die ersten 
Requests beantwortet wurden.

von Tim  . (cpldcpu)


Lesenswert?

So funktioniert es immerhin als define:
1
         uint8_t out;
2
         uint8_t adr=1;
3
4
         asm volatile(      
5
          "         lds  %0,%1 \n\t"        
6
          : "=&d" (out)
7
          :  "M" (adr)
8
         );

Leider funktioniert es nicht, wenn "adr" ein Pointer auf eine Variable 
im Speicher ist:
1
main.c:160:10: warning: asm operand 1 probably doesn't match constraints [enabled by default]
2
main.c:160:10: error: impossible constraint in 'asm'

Wie bringe ich dem Compiler bei, dass die pointer konstant sind?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

adr ist ja auch keine Compilezeit-Konstrante, &adr ist es, zumindest für 
adr im Static Storage.

: Bearbeitet durch User
von Tim  . (cpldcpu)


Lesenswert?

Der code oben compiliert ohne Fehlermeldung.

Dieser aber nicht, trotz pointer auf static.
1
         uint8_t out;
2
         static uint8_t adr=1;
3
4
         asm volatile(      
5
          "         lds  %0,%1 \n\t"        
6
          : "=&d" (out)
7
          :  "M" ((uint8_t)&adr)
8
         );

Ich habe es aber jetzt auch so in den Speicher bekommen. Ich werde erst 
einmal abwarten, was vom Ateml support kommt, bevor ich Arbeit in 
Workarounds investiere.

von Tim  . (cpldcpu)


Lesenswert?

Und hier noch ein Aufreger. Cast von statischem 16 pointer auf 8 bit 
const:
1
#define usbMsgPtr_t uchar *
2
...
3
 usbMsgPtr = (usbMsgPtr_t)(&usbDescriptorDevice[12]); 
4
 340:  40 e2         ldi  r20, 0x20  ; 32
5
 342:  50 e0         ldi  r21, 0x00  ; 0
6
 344:  d4 2f         mov  r29, r20

Ich nehme an, das ist ein prinzipielles Problem durch die angenommene 16 
Bit Maschinenwortgröße?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Tim    schrieb:
>           :  "M" ((uint8_t)&adr)

M ist auch die falsche Constraint, nimm i.

von Tim  . (cpldcpu)


Lesenswert?

Geht leider auch nicht. Ich dachte "i" wäre für 6 bit (register) und "M" 
für 8 bit?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Nein, "i" steht für Immediate, also "s" (symbolic) oder "n" (compile 
time constant).  Folgender Code geht für nicht-Tiny und sollte auch mit 
Tinys gehen:
1
char fout (void)
2
{
3
    char out;
4
    static char adr = 1;
5
6
    asm volatile (
7
        "lds %0,%1"
8
        : "=d" (out) : "i" (&adr) : "memory"
9
    );
10
    
11
    return out;
12
}

: Bearbeitet durch User
von Tim  . (cpldcpu)


Lesenswert?

Super, danke! Jetzt geht es!
1
#define LDS(out,mem) \
2
    asm volatile ("lds %0,%1"  : "=d" (out) : "i" (&mem));

Das "memory"-clobber keyword kann ich für Ladebefehle aber weglassen, 
oder? Ansonsten würdem im schlechtesten Fall ja Variablen doppelt 
geladen werden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das Lesen eines SFR's kann prinzipiell diese oder gar ein anderes SFR 
verändern.  Wenn nur RAM gelesen wird, kann das memory-clobber weg, und 
auch das volatile kann dann entfallen dürfen.

von Tim  . (cpldcpu)


Lesenswert?

Danke! Hat im ganzen Code 32 Bytes gespart. Da hat Atmel noch ein paar 
Hausaufgaben.

Es handelt sich übrigens um eine minimal USB-Implementierung auf einem 
ATtiny 10. Wahrscheinlich das komplexeste für den ATtiny10 existierende 
Programm :)

Und es funktioniert sogar:

https://github.com/cpldcpu/u-wire

von Falk B. (falk)


Lesenswert?

Naja, bei 1 K Flash kann man's auch gleich in ASM machen, da würde ich 
nicht so einen Würg Around mit dem Compiler veranstalten.

Edit: Ich vermisse 100nF am AVR.

: Bearbeitet durch User
von Tim  . (cpldcpu)


Lesenswert?

Falk Brunner schrieb:
> Naja, bei 1 K Flash kann man's auch gleich in ASM machen, da würde
> ich
> nicht so einen Würg Around mit dem Compiler veranstalten.

Klar, aber irgendwie ist es nicht der Sinn der Sache, existierende 
C-Programme in Assembler nachzuprogrammieren. Vor allem das Zuweisen der 
Register und die Optimierung von langen Switch/If-Konstrukten überlasse 
ich gerne dem C-Compiler. Das kann er aber besser, je mehr 
zusammenhängenden Code er sieht.

Ich hoffe ja, das Atmel die bestehenden Bugs im Compiler noch beseitigt, 
damit ich die Workaround wieder entfernen kann.

> Edit: Ich vermisse 100nF am AVR.

Du meinst den Aufbau auf dem Broadboard? Da verbergen sich etliche 
passive Bauteile auf den Rückseiten der Break-Out boards.

von Tim  . (cpldcpu)


Lesenswert?

Tim    schrieb:
> Lustig, es geht gleich weiter. AVR-GCC scheint für den ATtiny 10 andere
> calling conventions als diese hier:
>
> http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage
>
> zu verwenden. Auf den normalen Cores können R18/R19 "geclobbered"
> werden, auf dem ATtiny 10 geht der compiler offenbar davon aus, dass
> diese erhalten bleiben. Nur wo ist das dokumentiert?

Hier die Antwort vom Atmel-Support. Das andere Problem schauen sie sich 
noch an.

1
As per current implementation (till Tool chain 3.4.3), below is the register conventions used for AVR_TINY devices:
2
3
r16 - Fixed/ TMP
4
r17 - Fixed/ ZERO
5
r18 - Callee saved
6
r19 - Callee saved
7
r20 - arg
8
r21 - arg
9
r22 - ret/ arg
10
r23 - ret/ arg
11
r24 - ret/ arg
12
r25 - ret/ arg
13
r26 - X
14
r27 - X
15
r28 - Y/ FP/ Callee saved
16
r29 - Y/ FP/ Callee saved
17
r30 - Z
18
r31 - Z

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

hmmm, das scheint wirklich nur die mini-Tinys zu betreffen, etwa:
1
extern char c, d;
2
3
void f1 (void)
4
{
5
    c = d;
6
}

gibt:
1
f1:
2
  ldi r30,lo8(d)   ;  5  *movhi/5  [length = 2]
3
  ldi r31,hi8(d)
4
  ld r20,Z   ;  6  movqi_insn/4  [length = 1]
5
  ldi r30,lo8(c)   ;  7  *movhi/5  [length = 2]
6
  ldi r31,hi8(c)
7
  st Z,r20   ;  8  movqi_insn/3  [length = 1]
8
  ret   ;  15  return  [length = 1]


Und auch an anderen Stellen muß Atmel noch nachsitzen:
1
int f3 (const __flash int *p)
2
{
3
    return *p;
4
}
1
.s: Assembler messages:
2
.s:18: Error: illegal opcode elpm for mcu attiny10
3
.s:19: Error: register not supported
4
.s:20: Error: illegal opcode sbiw for mcu attiny10
5
.s:21: Error: illegal opcode elpm for mcu attiny10
6
.s:22: Error: register not supported

Zudem ist eine Meldung (:20) auch noch falsch: Es wird ADIW erzeugt, 
nicht SBIW.

von Tim  . (cpldcpu)


Lesenswert?

Der Support hat ganz erstaunt nach Codebeispielen gefragt. Bei einem so 
leicht zu reproduzierenden Problem gibt mir das zu denken :) Ich hatte 
ihnen den Auszug aus dem Listingfile geschickt.

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.