Forum: Compiler & IDEs Array über Inline-Assembler ansprechen?


von Martin Meyer (Gast)


Lesenswert?

Hallo allerseits,

habe gerade folgendes Problem: In einer Interrupt-Routine (Timer) soll 
ein 8bit-Wert aus einem Array an einen Port ausgegeben werden. Es gibt 
eine globale Variable, die die aktuelle Position im Array markiert, 
diese wird in der Timer-Routine inkrementiert. Stellt euch das ganze als 
eine Art Spieluhr vor, das Array ist die Programmwalze.

Hier ist der C-Code, der auch funktioniert:

SIGNAL (SIG_OUTPUT_COMPARE1A)
{
  PortA = program[pos];
  pos++;
  if (pos > prog_len) pos = 0;
}

Das Problem ist nur, daß die maximal geforderte Taktfrequenz nicht 
erreicht wird, weil der vom AVR-GCC erzeugte Code doch recht lang ist. 
In Assembler wären das weniger als 10 Byte.

Allerdings möchte ich nicht das ganze Programm in Assembler schreiben, 
weil es auch einige floating-point-Berechnungen in der Anwendung gibt.

Deshalb nun meine Frage: Kann ich eine ISR auch in Inline-Assembler 
schreiben? Wenn ja, wie spreche ich das Array an? Wie finde ich die 
Startadresse? Wie bekomme ich den Index in das Z-Register? Wie teile ich 
"C" mit, welche Register ich verwurschtelt habe? Muß ich die selber auf 
den Stack schieben oder macht der Compiler das selbst?

Ich habe zwar im Netz ein Tutorial gefunden, aber das ist so kryptisch, 
daß ich nach einer Weile aufgegeben habe. Vielleicht kann mich einer mal 
in die richtige Richtung stoßen, so daß es "klick" macht.

Danke!!!

von Karl heinz B. (kbucheg)


Lesenswert?

Was ist wenn du das so umschreibst:
1
uint8_t* pos;
2
uint8_t* last;
3
4
SIGNAL (SIG_OUTPUT_COMPARE1A)
5
{
6
  PortA = *pos++;
7
  if( pos > last )
8
    pos = program;
9
}
10
11
int main()
12
{
13
  pos = program;
14
  last = pos + prog_len;
15
16
   ...

  

von Michael Wilhelm (Gast)


Lesenswert?


SIGNAL (SIG_OUTPUT_COMPARE1A)
{
  if (pos < prog_len)
  {
     pos++;
  }
  else
  {
     pos = 0;
  }
  PortA = program[pos];
}

sollte noch kürzer sein

MW

von Martin Meyer (Gast)


Lesenswert?

Ok... Hat 3 Minuten gedauert, bis ich das verstanden habe, aber macht 
Sinn! Ich probiere das nach Feierabend mal aus und melde mich dann 
wieder!

Danke schonmal!

von Karl heinz B. (kbucheg)


Lesenswert?

Der Hauptvorteil den ich mir verspreche liegt darin,
dass ich die Arrayindizierung aus der ISR raus kriege.

So gehts noch einen Tick schneller
Achte auf die Deklaration von 'last'. Da ist jetzt noch
ein const mit drinnen.
1
#include <avr/io.h>
2
#include <avr/signal.h>
3
4
#define prog_len 20
5
uint8_t program[prog_len];
6
7
uint8_t* pos;
8
uint8_t* const last = program + prog_len;
9
10
SIGNAL (SIG_OUTPUT_COMPARE1A)
11
{
12
  PORTA = *pos++;
13
  if( pos > last )
14
    pos = program;
15
}
16
17
int main()
18
{
19
  pos = program;
20
//  last = pos + prog_len;
21
}

1
SIGNAL (SIG_OUTPUT_COMPARE1A)
2
{
3
  8e:  1f 92         push  r1
4
  90:  0f 92         push  r0
5
  92:  0f b6         in  r0, 0x3f  ; 63
6
  94:  0f 92         push  r0
7
  96:  11 24         eor  r1, r1
8
  98:  8f 93         push  r24
9
  9a:  9f 93         push  r25
10
  9c:  ef 93         push  r30
11
  9e:  ff 93         push  r31
12
  PORTA = *pos++;
13
  a0:  e0 91 76 00   lds  r30, 0x0076
14
  a4:  f0 91 77 00   lds  r31, 0x0077
15
  a8:  81 91         ld  r24, Z+
16
  aa:  f0 93 77 00   sts  0x0077, r31
17
  ae:  e0 93 76 00   sts  0x0076, r30
18
  b2:  8b bb         out  0x1b, r24  ; 27
19
  if( pos > last )
20
  b4:  e6 57         subi  r30, 0x76  ; 118
21
  b6:  f0 40         sbci  r31, 0x00  ; 0
22
  b8:  39 f0         breq  .+14       ; 0xc8 <__vector_6+0x3a>
23
  ba:  30 f0         brcs  .+12       ; 0xc8 <__vector_6+0x3a>
24
    pos = program;
25
  bc:  82 e6         ldi  r24, 0x62  ; 98
26
  be:  90 e0         ldi  r25, 0x00  ; 0
27
  c0:  90 93 77 00   sts  0x0077, r25
28
  c4:  80 93 76 00   sts  0x0076, r24
29
  c8:  ff 91         pop  r31
30
  ca:  ef 91         pop  r30
31
  cc:  9f 91         pop  r25
32
  ce:  8f 91         pop  r24
33
  d0:  0f 90         pop  r0
34
  d2:  0f be         out  0x3f, r0  ; 63
35
  d4:  0f 90         pop  r0
36
  d6:  1f 90         pop  r1
37
  d8:  18 95         reti

Ein Register, R25, hätte er noch einsparen können, wenn ich
das richtig sehe. Aber sonst sehe ich nicht mehr viel
was überflüssig wäre.


Wie lauten eigentlich die Forumskürzel für Assemblercode?
Also das Äquivalent zu [ C ] [ /C ]

von Karl heinz B. (kbucheg)


Lesenswert?

Das war jetzt mit OPtimizer auf -Os

Auf -O3 generiert der gcc Folgendes
1
SIGNAL (SIG_OUTPUT_COMPARE1A)
2
{
3
  8e:  1f 92         push  r1
4
  90:  0f 92         push  r0
5
  92:  0f b6         in  r0, 0x3f  ; 63
6
  94:  0f 92         push  r0
7
  96:  11 24         eor  r1, r1
8
  98:  2f 93         push  r18
9
  9a:  3f 93         push  r19
10
  9c:  8f 93         push  r24
11
  9e:  9f 93         push  r25
12
  a0:  ef 93         push  r30
13
  a2:  ff 93         push  r31
14
  PORTA = *pos++;
15
  a4:  e0 91 76 00   lds  r30, 0x0076
16
  a8:  f0 91 77 00   lds  r31, 0x0077
17
  ac:  81 91         ld  r24, Z+
18
  ae:  8b bb         out  0x1b, r24  ; 27
19
  if( pos > last )
20
  b0:  80 e0         ldi  r24, 0x00  ; 0
21
  b2:  e6 37         cpi  r30, 0x76  ; 118
22
  b4:  f8 07         cpc  r31, r24
23
  b6:  41 f0         breq  .+16       ; 0xc8 <__vector_6+0x3a>
24
  b8:  38 f0         brcs  .+14       ; 0xc8 <__vector_6+0x3a>
25
    pos = program;
26
  ba:  22 e6         ldi  r18, 0x62  ; 98
27
  bc:  30 e0         ldi  r19, 0x00  ; 0
28
  be:  30 93 77 00   sts  0x0077, r19
29
  c2:  20 93 76 00   sts  0x0076, r18
30
  c6:  04 c0         rjmp  .+8        ; 0xd0 <__vector_6+0x42>
31
  c8:  f0 93 77 00   sts  0x0077, r31
32
  cc:  e0 93 76 00   sts  0x0076, r30
33
  d0:  ff 91         pop  r31
34
  d2:  ef 91         pop  r30
35
  d4:  9f 91         pop  r25
36
  d6:  8f 91         pop  r24
37
  d8:  3f 91         pop  r19
38
  da:  2f 91         pop  r18
39
  dc:  0f 90         pop  r0
40
  de:  0f be         out  0x3f, r0  ; 63
41
  e0:  0f 90         pop  r0
42
  e2:  1f 90         pop  r1
43
  e4:  18 95         reti

eher schwach.
2 neue Register angepatzt. Und die Sequenz
1
  ...
2
    pos = program;
3
  ba:  22 e6         ldi  r18, 0x62  ; 98
4
  bc:  30 e0         ldi  r19, 0x00  ; 0
5
  be:  30 93 77 00   sts  0x0077, r19
6
  c2:  20 93 76 00   sts  0x0076, r18
7
  c6:  04 c0         rjmp  .+8        ; 0xd0 <__vector_6+0x42>
8
  c8:  f0 93 77 00   sts  0x0077, r31
9
  cc:  e0 93 76 00   sts  0x0076, r30
10
  d0:  ff 91         pop  r31
11
  ...
könnte auch besser sein.
1
  ...
2
  ldi  r31, 0x62  ; 98
3
  ldi  r30, 0x00  ; 0
4
  sts  0x0077, r31
5
  sts  0x0076, r30
6
  pop  r31
7
  ...

  

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


Lesenswert?

Martin Meyer wrote:

> Deshalb nun meine Frage: Kann ich eine ISR auch in Inline-Assembler
> schreiben?

Ja, aber ich würde es dann eher gleich komplett in Assembler
schreiben.

Siehe auch http://avr-libc.nongnu.org/user-manual/group__asmdemo.html

> Wenn ja, wie spreche ich das Array an?

Ist ein globaler Name.

> Wie finde ich die
> Startadresse?

Der Name ist die Startadresse.

> Wie bekomme ich den Index in das Z-Register?

Mit MOV?

> Wie teile ich
> "C" mit, welche Register ich verwurschtelt habe?

Als clobber list.

> Muß ich die selber auf
> den Stack schieben oder macht der Compiler das selbst?

Um die clobbers kümmert sich der Compiler.

> Ich habe zwar im Netz ein Tutorial gefunden, aber das ist so kryptisch,
> daß ich nach einer Weile aufgegeben habe.

Inline-Assembler ist auf Grund der vielen Möglichkeiten kryptisch.
Daher ja auch mein Rat, es wenn schon dann gleich separat als
Assemblerquelle zu schreiben.

von Michael Wilhelm (Gast)


Lesenswert?

   \   __nearfunc __interrupt void timer1_ovf_isr();
   \                     timer1_ovf_isr:
   \   00000000   93FA                       ST      -Y,R31
   \   00000002   93EA                       ST      -Y,R30
   \   00000004   932A                       ST      -Y,R18
   \   00000006   931A                       ST      -Y,R17
   \   00000008   930A                       ST      -Y,R16
   \   0000000A   B72F                       IN      R18,0x3F
     10            if (pos < prog_len)
   \   0000000C   9100....                   LDS     R16,pos
   \   00000010   9110....                   LDS     R17,prog_len
   \   00000014   1701                       CP      R16,R17
   \   00000016   F428                       BRCC    ??timer1_ovf_isr_0
     11            {
     12               pos++;
   \   00000018   ....                       LDI     R30,LOW(pos)
   \   0000001A   ....                       LDI     R31,(pos) >> 8
   \   0000001C   9503                       INC     R16
   \   0000001E   8300                       ST      Z,R16
   \   00000020   C003                       RJMP    ??timer1_ovf_isr_1
     13            }
     14            else
     15            {
     16               pos = 0;
   \                     ??timer1_ovf_isr_0:
   \   00000022   E000                       LDI     R16,0
   \   00000024   9300....                   STS     pos,R16
     17            }
     18            PORTA = program[pos];
   \                     ??timer1_ovf_isr_1:
   \   00000028   9100....                   LDS     R16,pos
   \   0000002C   E010                       LDI     R17,0
   \   0000002E   01F8                       MOVW    R31 : R30,R17 : R16
   \   00000030   ....                       SUBI    R30,LOW((-(program) 
& 0xFFFF))
   \   00000032   ....                       SBCI    R31,(-(program) & 
0xFFFF) >> 8
   \   00000034   8100                       LD      R16,Z
   \   00000036   BB0B                       OUT     0x1B,R16
     19          }
   \   00000038   BF2F                       OUT     0x3F,R18
   \   0000003A   9109                       LD      R16,Y+
   \   0000003C   9119                       LD      R17,Y+
   \   0000003E   9129                       LD      R18,Y+
   \   00000040   91E9                       LD      R30,Y+
   \   00000042   91F9                       LD      R31,Y+
   \   00000044   9518                       RETI

Und das macht IAR daraus.

MW

von Martin Meyer (Gast)


Lesenswert?

Recht herzlichen Dank für alle Eure Hilfen!

Dieses Forum ist wirklich toll, besonders wenn man merkt, daß wirkliche 
Profis sich die Zeit nehmen, einem Anfänger zu helfen.

Mit einer "anderen Formulierung" in C nach Karl-Heinz bin ich nun in der 
Lage, die nötige Taktfrequenz auch ohne Assembler zu erreichen. Damit 
kann ich mich dann mal in Ruhe beschäftigen, wenn ich Zeit habe ;)

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


Lesenswert?

Karl heinz Buchegger wrote:

> Wie lauten eigentlich die Forumskürzel für Assemblercode?
> Also das Äquivalent zu [ C ] [ /C ]

[ avrasm ]
...
[ /avrasm ]

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.