mikrocontroller.net

Forum: Compiler & IDEs Assembler und gcc


Autor: Hans Otto (mc_ratte)
Datum:

Bewertung
0 lesenswert
nicht 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 ?

Autor: Oliver (Gast)
Datum:

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

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

Oliver

Autor: otto (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Hans Otto (mc_ratte)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Jörg G. (joergderxte)
Datum:

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

hth, Jörg

Autor: Hans Otto (mc_ratte)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg G. (joergderxte)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auftritt: die Hassliebe der (Gcc-) C-Programmierer, der inline-Asm
void store(uint8_t *buf) {
  uint8_t num = 100;
  uint8_t tmp;
  asm volatile ("L_%=:              \n\t"
           "in   %[reg], %[pins]    \n\t"
           "st   %[ptr]+, %[reg]    \n\t"
           "dec  %[cnt]             \n\t"
           "brne L_%=b              \n\t"
        : [reg] "=&r" (tmp), [ptr] "+e" (buf)
        : [cnt] "r" (num), [pins] "I" (_SFR_IO_ADDR(PINB))
        :"memory"
        );
}
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

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
.global tab
tab:       .BYTE 100

*.c
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
unsigned char tab[100];

*.s
.extern tab

Oliver

Autor: Hans Otto (mc_ratte)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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+.
void store (uint8_t *buf) 
{
  uint8_t num = 100;
  asm volatile ("0:"                      "\n\t"
           "in   __tmp_reg__, %[pins]"    "\n\t"
           "st   %a[ptr]+, __tmp_reg__"   "\n\t"
           "dec  %[num]"                  "\n\t"
           "brne 0b"
        : [ptr] "+e" (buf)
        : [num] "r" (num), [pins] "I" (_SFR_IO_ADDR(PINB))
        :"memory"
        );
}

Johann

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
>
> .global tab
> tab:       .BYTE 100
> 
>
> *.c
>
> extern unsigned char tab[100];
> 

Nicht eher so?
.data
.global tab
tab:       .skip 100

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

Johann

Autor: Hans Otto (mc_ratte)
Datum:

Bewertung
0 lesenswert
nicht 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.:-)

Autor: Jörg G. (joergderxte)
Datum:

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

Autor: Hans Otto (mc_ratte)
Datum:

Bewertung
0 lesenswert
nicht 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);

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <avr/io.h>

uint8_t tab[100];

void
fill_buf(void)
{
        uint8_t i;
        uint8_t *p;

        for (i = 100, p = tab; i != 0; i--)
                *p++ = PIND;
}

Generierter Assemblercode:
.global fill_buf
        .type   fill_buf, @function
fill_buf:
/* prologue: function */
/* frame size = 0 */
        ldi r30,lo8(tab)
        ldi r31,hi8(tab)
.L2:
        in r24,41-32
        st Z+,r24
        ldi r24,hi8(tab+100)
        cpi r30,lo8(tab+100)
        cpc r31,r24
        brne .L2
/* epilogue start */
        ret
        .size   fill_buf, .-fill_buf
        .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).

Autor: Johann L. (gjlayde) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
#include <stdint.h>

extern void scan (uint8_t*);

uint8_t scan_buf[SCAN_SIZE];

int main()
{
    scan (scan_buf);
    return 0;
}

Die Funcktion scan() ist in Assembler implementiert. Sie expandiert ein 
Makro, das SCAN_SIZE mal den Port liest und den Wert speichert:
#include <avr/io.h>

.macro _SCAN cnt
.if (\cnt > 0)
    in   r24, _SFR_IO_ADDR (PINB)
    st   z+, r24
    _SCAN (\cnt)-1
.endif
.endm

.text
.global scan
scan:
    movw r30, r24
    _SCAN(SCAN_SIZE)
    ret

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

Johann

Autor: Hans Otto (mc_ratte)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.