Forum: Mikrocontroller und Digitale Elektronik avr-libc - Assembler Demo


von Peter (Gast)


Lesenswert?

Hallo,

ich habe mir eine kleine Demo mit der avr-libc zusammengebastelt und es 
funktioniert auch ganz gut, solange ich keine Variablen definiere und 
darauf zugreife.

Das Problem liegt vermutlich an meinem selbstgestrickten Makefile und 
eben wenig Kenntnis was die avr-libc (und auch Assembler) betrifft.

Man findet bei denen auf der Seite zwar eine Menge Erklärungen und das 
Ganze gefällt mir auch sehr gut. Aber leider gibt es keine lauffähige 
Assembler-Demo, die auch ein Makefile beinhaltet.

Das Assembler Beispiel, welches vorgestellt wird besteht nur aus einer 
Quelldatei. Ausserdem wird dabei auch auf die Definition von Variablen 
verzichtet.

Da ich mich hier erst einarbeite, würde ich mich sehr freuen, wenn mir 
jemand eine kleine Demo zur Verfügung stellt (Zugriff auf Variablen, 
inclusive Makefile).

Vielen Dank schonmal, Gruß Peter

von Peter (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe mal meine Demo angehängt.

Wenn ich
1
rcall data_init

in main einfüge, dann geht die Led nicht mehr an / der Controller stürzt 
ab.

Bitte einmal drüberschauen, Gruß Peter

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Peter schrieb:
> Wenn ich
> rcall data_init
>
> in main einfüge, dann geht die Led nicht mehr an / der Controller stürzt
> ab.
>
> Bitte einmal drüberschauen, Gruß Peter

 Du hast Stack Pointer nicht auf einen bestimmten Wert gesetzt, rufst
 aber eine Routine mit rcall auf:
1
main:
2
  rcall  stack_init


 So etwas macht man vorher, etwa so:
1
main:
2
  ldi   r16, RAMEND          ;Init stack pointer
3
  out   _SFR_IO_ADDR(SP), r16
4
  rcall   ports_init
5
  rcall   data_init

von ArminD (Gast)


Lesenswert?

Hallo,

müsste man den Stackpointer nicht initialisieren vor dem ersten rjmp ?

von Peter (Gast)


Lesenswert?

Vielen Dank für Eure Hilfe! Genau das wars.

Ich komme halt von der C-Schiene und da ist man darauf eingestellt 
einfach alles in Funktionen zu verlegen.
Bei Assembler kann man das auch machen, aber natürlich erst nach der 
Initialisierung das Stacks.

Danke und Gruß Peter

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Prinzipiell könntest du auch den init-Code der avr-libc verwenden, indem 
du __init anspringst anstatt main.  Dadurch würde stach_init und 
ram_init entfallen.  Der Start-Code der avr-libc springt am Ende nach 
main, so dass du  nix weiter tunb musst.

Falls du Funktionen der avr-libc verwenden willst brauchst du die 
Bibliotheken, die in deinem Link-Aufruf nicht enthalten sind.  Am 
einfachsten bekommst du den Bibliothekssupport, indem avr-gcc als 
Linker-Treiber verwendet wird anstatt avr-ld händisch zu verwenden.

Das wird dann auch gegen die libgcc linken, die auch Teile des 
Startup-Codes anthält, nämlich Init von .data und .bss.  avr-gcc linkt 
aber nur gegen die benötigten Funktionen, wenn sie tatsächlich gebraucht 
werden, d.h. avr-gcc gibt ein ".global __do_copy_data" aus falls etwas 
in .data ist und __do_clear_bss falls etwas in .bss oder COMMON ist.

Der Startup-Code der avr-libc bringt auch eine Vektortabelle, die dann 
nicht mehr händisch und Device-abhängig geschrieben werden muss.

von Peter (Gast)


Lesenswert?

Johann L. schrieb:
> Prinzipiell könntest du auch den init-Code der avr-libc verwenden, indem
> du __init anspringst anstatt main.  Dadurch würde stach_init und
> ram_init entfallen.  Der Start-Code der avr-libc springt am Ende nach
> main, so dass du  nix weiter tunb musst.

Das würde ich ja auch gerne. Aber für reinen Assembler findet man auf 
der libc-Seite nur Code-Snippets und kein Demo-Projekt mit Makefile und 
Quellcode.

Wie meinst Du das mit __init? Wenn ich das statt main an Adresse 0 
schreibe, dann bekomme ich eine 'undefined reference'. Was muss ich 
einbinden und wie?

Johann L. schrieb:
> Falls du Funktionen der avr-libc verwenden willst brauchst du die
> Bibliotheken, die in deinem Link-Aufruf nicht enthalten sind.  Am
> einfachsten bekommst du den Bibliothekssupport, indem avr-gcc als
> Linker-Treiber verwendet wird anstatt avr-ld händisch zu verwenden.

Wie mach ich das am einfachsten?

Johann L. schrieb:
> Der Startup-Code der avr-libc bringt auch eine Vektortabelle, die dann
> nicht mehr händisch und Device-abhängig geschrieben werden muss.

Finde ich ganz klasse. Aber wie binde ich das alles zusammen?

Kannst Du mir nicht ein einfaches Projekt mit Makefile hochladen?
Das wäre eine tolle Hilfe.

Gruß Peter

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

...hier mal ein simples main.c
1
int x;
2
int y = 1;
3
4
int main (void)
5
{
6
    return 0;
7
}

Und was avr-gcc -S -Os -fno-common daraus macht:
1
  .file "main.c"
2
  .section  .text.startup,"ax",@progbits
3
.global  main
4
  .type  main, @function
5
main:
6
  ldi r24,0
7
  ldi r25,0
8
  ret
9
  .size  main, .-main
10
.global  y
11
  .data
12
  .type  y, @object
13
  .size  y, 2
14
y:
15
  .word  1
16
.global  x
17
  .section .bss
18
  .type  x, @object
19
  .size  x, 2
20
x:
21
  .zero  2
22
.global __do_copy_data
23
.global __do_clear_bss

Die .file, .size und .type Direktiven sind hilfreich beim Debuggen oder 
mit avr-nm, avr-size etc.  aber wirklich notwendig sind sie nicht.  Die 
.global Direktiven werden nur gebraucht, wenn die Applikation in mehrere 
Module aufgespalten werden soll und aus anderen Modulen auf die 
Variablen zugegriffen wird.

Der Code kann also vereinfacht werden zu:
1
.text
2
.global  main
3
main:
4
  loop:
5
    rjmp loop
6
7
.data
8
9
y:  .word  1
10
11
.section .bss
12
13
x:  .zero  2
14
15
.global __do_copy_data ;; weil .data nicht leer ist
16
.global __do_clear_bss ;; weil .bss nicht leer ist

Die Datei kann als main.S oder main.sx benannt werden und mit
1
$ avr-gcc main.S -save-temps -c
übersetzt werden zu main.o.  Das save-temps bewirkt, dass die 
Zwischendatei main.s behalten wird. Das ist das, was nach Auflösung der 
C-Makros und C-Includes von main.S übrig bleibt.

Falls ISRs erstellt werden sollen, dann z.B. so:
1
#include <avr/io.h>
2
3
.text
4
5
.global INT0_vect
6
INT0_vect:
7
    reti
8
9
.global __vector_default
10
__vector_default:
11
    panic:      
12
        wdr
13
        rjmp panic

Der Startup-Code der avr-libc ist so geschrieben, dass INT0_vect 
automatisch in die Vektortabelle eingetragen wird, händisch muss da nix 
mehr gemacht werden.

An einigen Stellen werden ein paar Bytes verschwendet, z.B. wird main 
per [r]call aufgerufen und danach gemäß C-Standard exit etc.  Falls das 
wirklich stören sollte kann man das auch umgehen, aber für ein einfaches 
Einsteige-Beispiel ist das i.d.R. nicht der Fall.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter schrieb:
> Johann L. schrieb:
>> Prinzipiell könntest du auch den init-Code der avr-libc verwenden, indem
>> du __init anspringst anstatt main.  Dadurch würde stach_init und
>> ram_init entfallen.  Der Start-Code der avr-libc springt am Ende nach
>> main, so dass du  nix weiter tunb musst.
>
> Das würde ich ja auch gerne. Aber für reinen Assembler findet man auf
> der libc-Seite nur Code-Snippets und kein Demo-Projekt mit Makefile und
> Quellcode.
>
> Wie meinst Du das mit __init? Wenn ich das statt main an Adresse 0
> schreibe, dann bekomme ich eine 'undefined reference'. Was muss ich
> einbinden und wie?

Mein Fehler.  Es genügt eine .global main zu schreiben.  Der Rest 
erledigt die avr-libc und der Startup-Code.

> Johann L. schrieb:
>> Falls du Funktionen der avr-libc verwenden willst brauchst du die
>> Bibliotheken, die in deinem Link-Aufruf nicht enthalten sind.  Am
>> einfachsten bekommst du den Bibliothekssupport, indem avr-gcc als
>> Linker-Treiber verwendet wird anstatt avr-ld händisch zu verwenden.
>
> Wie mach ich das am einfachsten?

Im Makefile LD zu avr-gcc setzen anstatt zu avr-ld.  Wichtig ist dabei, 
dass auch beim Linken -mmcu= mitgegeben wird.

> Johann L. schrieb:
>> Der Startup-Code der avr-libc bringt auch eine Vektortabelle, die dann
>> nicht mehr händisch und Device-abhängig geschrieben werden muss.
>
> Finde ich ganz klasse. Aber wie binde ich das alles zusammen?

avr-gcc anstatt avr-ld.

> Kannst Du mir nicht ein einfaches Projekt mit Makefile hochladen?

Ich hab weder Projekte nocht entsprechende Makefiles, ich kann dir aber 
sagen wie's geht.

Über o-Files zu gehen ist bei kleinen Projekten nicht wirklich 
notwendig, man kann auch den ganzer Rumms mit einem einzigen Aufruf 
übersetzen, also z.B.
1
$ avr-gcc main.S modul.o cdatei.c -mmcu=... -o main.elf
avr-gcc ruft denn den richtigen "Compiler" für jedes Modul auf (as für 
main.S, cc1 + as für cdatei.c, etc) und gibt die ganzen o-Dateien dann 
an collect2 und ld zum Linken.

Natürlich geht auch, zuerst o-Dateien per -c zu erhalten und die dann in 
einem Linkschritt mit den Bibliotheken zusammenzubacken so wie es in 
deinem Makefile gemacht wird.

Aber ich hab bis heute noch kein Projekt gesehen, wo es hilfreich oder 
gar notwendig war, ld händisch aufzurufen.

von Peter (Gast)


Lesenswert?

Hallo Johann L.,

das Beispiel und die Erklärungen von Dir sind genau was ich gesucht 
habe. Vielen Dank dafür.

Ich habe jetzt beide Teile (ISR und main) zusammengefasst und es 
funktioniert hervorragend. Auch das Initialisieren der Variablen klappt 
nun.
Dazu kann man dann das Makefile der C-Demo verwenden.

Sehr Klasse. Ich bin wirklich froh, dass ich das jetzt mal habe und mir 
keine Gedanken mehr machen muss, ob denn der Linker auch wirklich alles 
an den richtigen Platz schreibt.

Eine Frage habe ich noch:
Ich habe ein paar Subroutinen in eine 'misc.S' ausgelagert, welche ich 
dann am Ende der main.S einfach per
1
#include "misc.S"
einbinde.

Wenn ich das so mache, dann muss ich auch im Makefile nichts weiter für 
die 'misc.S' hinzufügen.

Ist das ein gängiges Vorgehen (Stichwort: verschiedene Module)? Oder 
kann man das geschickter lösen?

MFG Peter

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter schrieb:
> Ich habe ein paar Subroutinen in eine 'misc.S' ausgelagert,
> welche ich dann am Ende der main.S einfach per
>
1
#include "misc.S"
> einbinde.
>
> Wenn ich das so mache, dann muss ich auch im Makefile nichts weiter für
> die 'misc.S' hinzufügen.

Aber nur, weil dein Makefile all von clean abhängig macht, d.h. bei 
jedem Build werden alle generierten Dateien gelöscht anstatt nur 
diejenigen Dateien neu zu erzeugen, die wirklich erzeugt werden müssen.

Zum Beispiel hängt die main.o jetzt nicht mehr nur von main.S ab, 
sondern auch von misc.S.

> Ist das ein gängiges Vorgehen (Stichwort: verschiedene Module)?

Nein.

Wenn es wirklich nur ein Modul ist, dann braucht es nicht includet zu 
werden und es genügt, dagegen zu linken.  Wie bereits oben erklärt 
müssen die Symbole, die außerhalb des definierenden Moduls verwendet 
werden, per .global oder .globl global gemacht werden.

von Peter (Gast)


Lesenswert?

Ich habs geändert, Danke.

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.