Forum: Compiler & IDEs Assembler und gcc


von Hans O. (mc_ratte)


Lesenswert?

Ich möchte ein Logikanalysator bauen. Die Routine zum abfragen der Ports 
möchte ich in Assembler als eine eigene Datei programmieren. Wie komme 
ich in C an die 100 Byte Variablen? Wie in C auf Prog zugreifen?

Anlegen in Assembler mit:  tab:        .Byte  100
Aufruf und Zugriff in C ?

von Oliver (Gast)


Lesenswert?

Falls du avr-gcc und die avrlibc nutzt, steht hier, wie es geht:

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

Oliver

von otto (Gast)


Lesenswert?

Hallo Oliver!
Die Manual-libc ist bestandteil von winavr,bin aber nicht klar gekommen.
Deshalb habe ich das Forum gewählt. Vielen Dank!
Gruß
Hans Otto

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


Lesenswert?

otto schrieb:

> Die Manual-libc ist bestandteil von winavr,bin aber nicht klar gekommen.

Womit denn genau nicht?

Gerade bei IO-Ports bringt das Ausweichen auf Assembler übrigens
nicht viel.  Das lohnt eher bei esoterischen Operationen, die von
C schlecht abgedeckt werden, wie vielleicht einer Multiplikation
zweier 8-bit-Zahlen zu einem 16-bit-Ergebnis (C würde beide zuvor
auf 16 bits ausdehnen und dann multiplizieren).

von Hans O. (mc_ratte)


Lesenswert?

Also ich möchte eine hohe Abtastrate erzielen.
1 Takt zum lesen vom Port  | 2 Takte zum speichern im Sram | 1 Takt für 
Sprung wenn nicht gleich (breq) macht 4 Takte pro Lauf. Bei 20 MHz 
gleich 5 Mhz also 200 n/s Abtastrate. Für C habe ich keine 
Berechnungsgrundlage.Hast Du so etwas? Wie schnell wäre ich, wenn ich 
alles in C schreibe mit Zeiger auf Puffer dann erhöhen,speichern und 
vergeilechen.

Ich find in der Manual-libc nicht die Übergabe, sowie Aufruf,habe mich 
schon doof geklickt. Ist es viel Text mir mitzuteilen wie es geht?

von Stefan E. (sternst)


Lesenswert?

Hans Otto schrieb:

> 1 Takt zum lesen vom Port  | 2 Takte zum speichern im Sram | 1 Takt für
> Sprung wenn nicht gleich (breq) macht 4 Takte pro Lauf.

Und worauf genau soll sich dann das "wenn nicht gleich" beziehen?

von Jörg G. (joergderxte)


Lesenswert?

Wenn dir nicht was extrem cleveres einfällt, wirst du den Compiler kaum 
schlagen:
1
// Ziemlich exakt, was du angedeutet hast:
2
uint8_t *foo(uint16_t len, uint8_t *buffer) {
3
  uint8_t *tmp = buffer;
4
  while(tmp != buffer+len ){
5
    *tmp++ = PINB;
6
  }
7
  return buffer;
8
}
1
foo:
2
  movw r18,r22; return-wert sichern
3
  add r18,r24
4
  adc r19,r25
5
  cp r22,r18
6
  cpc r23,r19
7
  breq .L15
8
  movw r30,r22
9
.L17:
10
  in r24,35-0x20
11
  st Z+,r24
12
  cp r30,r18
13
  cpc r31,r19
14
  brne .L17
15
.L15:
16
  movw r24,r22
17
  ret
Wenn 'len' garantiert immer < 256 ist, könnte man den Vergleich des 
Highbytes weglassen...</Schnapsidee>

hth, Jörg

von Hans O. (mc_ratte)


Lesenswert?

Ja ich habe auf die schnelle ein Fehler gemacht.
;Bei 20 MHz Systemtakt und 100 Werte im SRAM
tab:       .BYTE 100
;
           ldi ZL,LOW(tab)
           ldi ZH,HIGH(tab)
           ldi r16,100
lab1:      in  r17,PIND
           st  Z+,r17
           dec  r16
           brne lab1

5 Takte pro Lauf also 250 ns, und wie schnell ist der C Compiler?

von Michael U. (amiga)


Lesenswert?

Hallo,

ich will Dir nicht den Spaß verderben, aber 100 Werte nutzen praktisch 
nichts. Für ein halbwegs nutzbares Ergebnis brauchst Du mindest 4, 
besser 8-10 Takte für ein zu sampelndes Ereignis.
Bei 1k Speicher wären also z.B. bei serieller Übertragung ca. 100 Bit 
halbwegs auswertbar. Das bedingt aber eine Triggerlogik, die den 
Zeitpunkt dazu auch erkennt.

8 Kanäle mit Pre-/Posttrigger-Sample und Auswertung der Triggerdaten 
habe ich bis 1MHz Samplerate bei 16MHz Takt für einen Mega16 hier, 
Auswertung auf dem PC mit der Software von LoLa, Samplebuffer 1,75kB.
Ist allerdings komplett in ASM.

Ansonsten
http://www.avr.roehres-home.de/logikanalyzer/index.html
falls es Dich interessiert.

Gruß aus Berlin
Michael

von Jörg G. (joergderxte)


Lesenswert?

Auftritt: die Hassliebe der (Gcc-) C-Programmierer, der inline-Asm
1
void store(uint8_t *buf) {
2
  uint8_t num = 100;
3
  uint8_t tmp;
4
  asm volatile ("L_%=:              \n\t"
5
           "in   %[reg], %[pins]    \n\t"
6
           "st   %[ptr]+, %[reg]    \n\t"
7
           "dec  %[cnt]             \n\t"
8
           "brne L_%=b              \n\t"
9
        : [reg] "=&r" (tmp), [ptr] "+e" (buf)
10
        : [cnt] "r" (num), [pins] "I" (_SFR_IO_ADDR(PINB))
11
        :"memory"
12
        );
13
}
Ich bin mir aber nicht 100% sicher, dass z.B. "st z+, r" so korrekt 
übersetzt wird (und ich hab keine Lust zum testen/simulieren).

hth, Jörg

von Oliver (Gast)


Lesenswert?

>Ich find in der Manual-libc nicht die Übergabe, sowie Aufruf,habe mich
>schon doof geklickt. Ist es viel Text mir mitzuteilen wie es geht?

Definition im Assembler-File:
>* .global (or .globl) declares a public symbol that is visible to the linker 
>(e.g. function entry point, global variable)

also:
*.s
1
.global tab
2
tab:       .BYTE 100

*.c
1
extern unsigned char tab[100];

oder umgekehrt, definition im C-File:
>* .extern declares a symbol to be externally defined; this is effectively a 
>comment only, as gas treats all undefined symbols it encounters as globally 
>undefined anyway

*.c
1
unsigned char tab[100];

*.s
1
.extern tab

Oliver

von Hans O. (mc_ratte)


Lesenswert?

So etwas tolles habe ich noch nicht gesehen, aber für mich zuviel 
Aufwand.Ich möchte nur die Zeiten zwischen 8 Signalen beweisen, bzw. 
Prellen aufspüren.Ein Beispiel wäre ein LCD Display das die Signale zum 
richtigen Zeitpunt bekommt.usw. Aber Deine Sache ist Markt reif. Super 
Aufmachung tolle Software.Ich muß Zugegeben das hier nur Pegel abgefragt 
werden und vom analysieren weit weg ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg G. schrieb:
> Auftritt: die Hassliebe der (Gcc-) C-Programmierer, der *inline-Asm*
> Ich bin mir aber nicht 100% sicher, dass z.B. "st z+, r" so korrekt
> übersetzt wird (und ich hab keine Lust zum testen/simulieren).

Da fehlt glaub ein a, sonst gibt das r30+ oder so anstatt z+.
1
void store (uint8_t *buf) 
2
{
3
  uint8_t num = 100;
4
  asm volatile ("0:"                      "\n\t"
5
           "in   __tmp_reg__, %[pins]"    "\n\t"
6
           "st   %a[ptr]+, __tmp_reg__"   "\n\t"
7
           "dec  %[num]"                  "\n\t"
8
           "brne 0b"
9
        : [ptr] "+e" (buf)
10
        : [num] "r" (num), [pins] "I" (_SFR_IO_ADDR(PINB))
11
        :"memory"
12
        );
13
}

Johann

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Oliver schrieb:

> Definition im Assembler-File:
>>* .global (or .globl) declares a public symbol that is visible to the linker
>>(e.g. function entry point, global variable)
>
> also:
> *.s
>
1
> .global tab
2
> tab:       .BYTE 100
3
>
>
> *.c
>
1
> extern unsigned char tab[100];
2
>

Nicht eher so?
1
.data
2
.global tab
3
tab:       .skip 100

Ansonsten ist tab nur ein Byte, das zu 100 initialisiert ist...

Johann

von Hans O. (mc_ratte)


Lesenswert?

Vielen Dank Jörg ich werde es am Wochenende testen.Der inline Assembler 
ist für mich zu kompliziert, deshalb wollte ich es in einer Datei 
stecken.:-)

von Jörg G. (joergderxte)


Lesenswert?

@gjlayde:
Da hast du recht
 - und ich sollte gründlicher im 'Kochbuch' lesen

von Hans O. (mc_ratte)


Lesenswert?

Wenn ich das jetzt alles richtig gelesen habe muss ich wie folgt 
eintragen.
 *s
.global tab

*.c
extern unsigned char tab[100];
ptr = &tab;

ist der Aufruf der Assembler Datei richtig?

extern void prog(void);

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


Lesenswert?

Hans Otto schrieb:

> ptr = &tab;

Wofür denn das?

Aber s. o., du schlägst den Compiler ohnehin nur minimal mit so'nem
primitiven Beispiel:

foo.c:
1
#include <avr/io.h>
2
3
uint8_t tab[100];
4
5
void
6
fill_buf(void)
7
{
8
        uint8_t i;
9
        uint8_t *p;
10
11
        for (i = 100, p = tab; i != 0; i--)
12
                *p++ = PIND;
13
}

Generierter Assemblercode:
1
.global fill_buf
2
        .type   fill_buf, @function
3
fill_buf:
4
/* prologue: function */
5
/* frame size = 0 */
6
        ldi r30,lo8(tab)
7
        ldi r31,hi8(tab)
8
.L2:
9
        in r24,41-32
10
        st Z+,r24
11
        ldi r24,hi8(tab+100)
12
        cpi r30,lo8(tab+100)
13
        cpc r31,r24
14
        brne .L2
15
/* epilogue start */
16
        ret
17
        .size   fill_buf, .-fill_buf
18
        .comm tab,100,1

Damit siehst du dann auch gleich die notwendige Syntax für den
Assemblercode...

Dass du ihn überhaupt schlägst, dürfte an den teilweise für den AVR
nicht mehr so guten ,,besonders schlauen'' Optimierungen liegen, die
GCC 4.3 offensichtlich vorrangig für unsere Desktop-Maschinen mit
ihren registermäßig verkrüppelten Prozessoren vornimmt: um die
Variable i nicht in einem Register führen zu müssen, wird der
Vergleich lieber auf die letzte zu beschreibende Adresse implementiert.
Ältere GCC-Versionen verhalten sich da sehr wahrscheinlich noch
besser (beim AVR).

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Wenn es um Geschwindigkeit geht, fällt vor allem auf, daß rund 50% der 
Zeit in der Schlaufe vertrödelt werden.

Wir schreiben also folgendes C-Programm:
1
#include <stdint.h>
2
3
extern void scan (uint8_t*);
4
5
uint8_t scan_buf[SCAN_SIZE];
6
7
int main()
8
{
9
    scan (scan_buf);
10
    return 0;
11
}

Die Funcktion scan() ist in Assembler implementiert. Sie expandiert ein 
Makro, das SCAN_SIZE mal den Port liest und den Wert speichert:
1
#include <avr/io.h>
2
3
.macro _SCAN cnt
4
.if (\cnt > 0)
5
    in   r24, _SFR_IO_ADDR (PINB)
6
    st   z+, r24
7
    _SCAN (\cnt)-1
8
.endif
9
.endm
10
11
.text
12
.global scan
13
scan:
14
    movw r30, r24
15
    _SCAN(SCAN_SIZE)
16
    ret

Übersetzt wird das dann zB per
1
avr-gcc -mmcu=atmega8 -c -Os scan.c -DSCAN_SIZE=100 -o scan.o
2
avr-gcc -mmcu=atmega8 -c scan-asm.S -DSCAN_SIZE=100 -o scan-asm.o
3
avr-gcc -mmcu=atmega8 scan.o scan-asm.o -o scan.elf
4
avr-objdump -d scan.elf > scan.lst
und liegfert ein Ergebnis wie angehängt.

Johann

von Hans O. (mc_ratte)


Lesenswert?

Vielen Dank an alle Mitwirkenden die mich bei meinem mini Projekt so 
hilfreich  unterstützt haben.Das war eine lehrreiche Sitzung mit den 
Profis.

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.