Forum: Mikrocontroller und Digitale Elektronik Frage zu AVR Studio 4.19: Von Nutzung eines Macros bedingte Assemblierung


von Markus (Gast)


Lesenswert?

Hallo,

ich habe ein sehr spezielles Anliegen bzgl. AVR Studio 4.19 und bisher 
noch keine Lösung dafür gefunden.

Ich möchte ein bestimmtes Unterprogramm nur bedingt mit assemblieren 
lassen. Und zwar in Abhängigkeit davon, ob eine bestimmtes Macro im 
restlichen Quellcode verwendet wurde. Dabei kann es vorkommen, dass das 
Macro von der Reihenfolge her erst nach dem Unterprogramm im Code 
verwendet wird oder auch in einer anderen Datei verwendet wird, die das 
Unterprogramm per include einbindet.

Hintergrund ist der, dass ich mir eine Befehlsbibliothek in Form von 
Macros schreibe. Dabei möchte ich jeweils zwei Versionen von jedem 
Befehl anbieten. Eine Version, die die benutzen Register und das 
Statusregister auf dem Stack zwischenspeichert und eine Version, die das 
nicht tut - die verwendeten Register also überschreibt. Letztere 
Versionen benötigen weniger Code und werden damit schneller ausgeführt.

Trotzdem möchte ich jeden Befehl auch als "kugelsicher" anbieten, damit 
der User selbst wählen kann welche Variante gerade sinnvoll ist.

Hier jetzt mal ein konkretes Beispiel. Die beiden Versionen des BEfehls 
"ClearRXBuffer" (er löscht einen UART Empfangsbuffer):
1
.macro UH_ClearRXBuffer
2
    rcall UH_UP_ClearRXBuffer
3
.endmacro
4
5
.macro UH_ClearRXBuffer_
6
    lds  @0, UH_Status               ; Flag "Gültige Daten im Buffer" löschen
7
    andi @0, 0b11111110
8
    sts  UH_Status, @0
9
10
    clr  @0                          ; Anzahl gelesene Bytes auf 0 setzen
11
    sts  UH_ReceivedBytes, @0
12
.endmacro

Der Befehl "UH_ClearRXBuffer_" (der mit Unterstrich am Ende) ist direkt 
im Macro realisiert. Das ist die Variante, die die Register nicht 
sichert. Der Befehl "UH_ClearRXBuffer" ist die Version, die die Register 
sichert. Da "UH_ClearRXBuffer" sehr oft in einem Programm vorkommen 
kann, habe ich daraus einen Unterprogrammaufruf gemacht. Folgendes 
Unterprogramm wird dabei per rcall aufgerufen:
1
UH_UP_ClearRXBuffer:
2
    push r16Stack sichern
3
    lds  r16, SREG
4
    push r16
5
6
    lds  r16, UH_Status              ; Flag "Gültige Daten im Buffer" löschen
7
    andi r16, 0b11111110
8
    sts  UH_Status, r16
9
10
    clr  r16                         ; Anzahl gelesene Bytes auf 0 setzen
11
    sts  UH_ReceivedBytes, r16
12
13
    pop  r16
14
    out  SREG, r16
15
    pop  r16
16
    ret

So, nun kann es aber sein, dass jemand, der die Bibliothek benutzt immer 
nur die kurze Version nutzt "UH_ClearRXBuffer_". Also würde in dem Fall 
das Unterprogramm niemals aufgerufen werden, weil der ganze Code schon 
im Macro "UH_ClearRXBuffer_" steckt.

Derzeit ist es aber so, dass das Unterprogramm in jedem Fall mit 
assembliert wird. Es schluckt also Programmspeicher, auch wenn es gar 
nicht benötigt wird.

Man könnte das natürlich vorher manuell entscheiden und auskommentieren, 
aber das ganze wird immer aufwändiger, je mehr solcher Befehle ich mir 
erstelle.

Von daher suche ich eine Möglichkeit das ganze zu automatisieren.

Ich habe es mit .ifdef und .if DEFINED(XYZ) versucht. Aber auch wenn ich 
diese abzuprüfenden Symbole im Macro "UH_ClearRXBuffer_" definiere, 
funktioniert das nicht. Es ist nicht gesagt, dass das Macro immer vor 
dem Unterprogramm vom precompiler gesehen wird.

Hat dazu jemand eine Idee?

von Markus (Gast)


Lesenswert?

Bisher habe ich nichts dazu gefunden. Vermutlich ist es wegen der 
unzulässigen Vorwärtsreferenz auch gar nicht möglich?

Derzeit behelfe ich mir, indem ich die Unterprogramme zwingend hinter 
allem anderen Code im CSEG inkludiere und die Macro-Definitionen an den 
Anfang setze. Funktioniert halt nur, wenn man diese Konvention einhält.

Das sieht dann etwa so aus:
1
; Steht am Anfang der Main.asm Datei
2
3
.set Compile_ClearRXBuffer=0
4
5
.macro UH_ClearRXBuffer
6
    rcall UH_UP_ClearRXBuffer
7
    .set Compile_ClearRXBuffer=1
8
.endmacro
9
10
.macro UH_ClearRXBuffer_
11
    lds  @0, UH_Status               ; Flag "Gültige Daten im Buffer" löschen
12
    andi @0, 0b11111110
13
    sts  UH_Status, @0
14
15
    clr  @0                          ; Anzahl gelesene Bytes auf 0 setzen
16
    sts  UH_ReceivedBytes, @0
17
.endmacro


In einer anderen Includedatei (die sich hinter allem anderen code im 
CSEG befinden muss) sitzt dann das entsprechende Unterprogramm mit dem 
Compiler-IF:
1
.if Compile_ClearRXBuffer==1
2
UH_UP_ClearRXBuffer:
3
    push r16                         ; Arbeits- und Statusregister auf Stack sichern
4
    lds  r16, SREG
5
    push r16
6
7
    lds  r16, UH_Status              ; Flag "Gültige Daten im Buffer" löschen
8
    andi r16, 0b11111110
9
    sts  UH_Status, r16
10
11
    clr  r16                         ; Anzahl gelesene Bytes auf 0 setzen
12
    sts  UH_ReceivedBytes, r16
13
14
    pop  r16                         ; Arbeits- und Statusregister restaurieren
15
    out  SREG, r16
16
    pop  r16
17
    ret
18
.endif

Falls jemand eine elegantere Lösung hat, würde mich das trotzdem 
interessieren. Ich bin bzgl. des AVR Assemblers und seinen Möglichkeiten 
nicht sonderlich fit.

Gruß Markus

PS: Bevor mir wieder jemand "Selbstdarstellung" vorwirft. Nein, ich 
halte eigentlich nicht gern Monologe und schreibe auch nicht solche 
Posts, um Aufmerksamkeit zu erregen. Ich kann mir aber vorstellen, dass 
diese spezielle Fragestellung und der Weg zur Lösung auch andere Leute 
interessieren könnten (oder zumindest Denkanstöße für andere Dinge 
liefern). :-)

von Konrad S. (maybee)


Lesenswert?

Packe jede Underscore-Funktion in eine eigene Quelltext-Datei. Mach 
daraus jeweils eine Object-Datei und baue aus all diesen Object-Dateien 
eine statische Bibliothek. Linke die Anwendung gegen die statische 
Bibliothek und das Binary enthält nur den Bibliothekscode, der in der 
Anwendung referenziert wird.

von Markus (Gast)


Lesenswert?

@Konrad: Uff, das klingt gut, danke für den Hinweis. Allerdings kann ich 
die Schritte nicht umsetzen. Das ist sicherlich ein Workflow eines 
C-Compilers, oder?

Ich bin völlig C-unerfahren (sowohl auf AVR als auch auf dem PC) und 
daher in der Praxis nicht mit den Workflows einer C-Compilierung 
vertraut. Wenn das bzgl. des AVR Studios nicht mit 2-3 Sätzen zu 
erklären ist, müsste ich mich erstmal einarbeiten.

Gruß Markus

von Konrad S. (maybee)


Lesenswert?

Nein, mit C hat das nichts zu tun. Da geht es (nur) um die 
Funktionsweise des Linkers. Hoffentlich hast du dazu eine gute 
Dokumentation.

von spess53 (Gast)


Lesenswert?

Hi

>Nein, mit C hat das nichts zu tun. Da geht es (nur) um die
>Funktionsweise des Linkers. Hoffentlich hast du dazu eine gute
>Dokumentation.

Der Assembler des AVR-Studios hat keinen Linker.

MfG Spess

von Markus (Gast)


Lesenswert?

Ok, also fällt das dann eh flach mit den Objectdateien.

von Konrad S. (maybee)


Lesenswert?

spess53 schrieb:
> Der Assembler des AVR-Studios hat keinen Linker.

Ach du liebe S.....e!
Und kann man wenigstens Bibliotheken erstellen?

von spess53 (Gast)


Lesenswert?

Hi

>Ach du liebe S.....e!
>Und kann man wenigstens Bibliotheken erstellen?

Klar kann man das.

MfG Spess

von Konrad S. (maybee)


Lesenswert?

spess53 schrieb:
> Klar kann man das.

Dann wird das vermutlich auch so funktionieren, wie ich es beschrieben 
habe, also eine Funktion je Quelltext-Datei?

von spess53 (Gast)


Lesenswert?

Hi

>Dann wird das vermutlich auch so funktionieren, wie ich es beschrieben
>habe, also eine Funktion je Quelltext-Datei?

Warum? Es gibt doch bedingte Assemblierung. Ist etwas Handarbeit.

Im Prinzip mache ich das wie Markus, nur habe ich die Freigaben der 
Funktionen einer Bibliothek in einer separaten Include-Datei.

MfG Spess

von Konrad S. (maybee)


Lesenswert?

spess53 schrieb:
> nur habe ich die Freigaben der
> Funktionen einer Bibliothek in einer separaten Include-Datei.

D.h. die Bibliothek wird zusammen mit der Applikation übersetzt? (Im 
Unterschied z.B. zu einer libc bei der C-Programmierung)

von Markus (Gast)


Lesenswert?

Spess, kannst Du dazu etwas aus dem Nähkästchen plaudern?

Ich habe meine Befehlsbibliothek wie folgt aufgeteilt:
Hier mal als Beispiel eine UART Bibliothek:
1
- UART-Def.asm: Hier befinden sich die Konstantendefinitionen und die Macros drin
2
- UART-CSEG.asm: Hier sind Unterprogramme zu finden, die von einigen der Macros aufgerufen werden.
3
- UART-SRAM.asm: Hier sind solche Dinge wie ein Empfangsbuffer und Bibliothekseigene Variablen als Datensegment enthalten.

Im Hauptprogramm werden diese drei Dateien dann an entsprechender Stelle 
inkludiert:
1
.include "UART-Def.asm"
2
[... Definitionen des Hauptprogramms ...]
3
4
.CSEG
5
[... Code des Hauptprogramms ...]
6
.include "UART-CSEG.asm"
7
8
.DSEG
9
.include "UART-SRAM.asm"
10
[... Datensegmente des Hauptprogramms ...]

Wobei durch die aktuelle Problematik vorgegeben ist, dass sich .include 
"UART-CSEG.asm" wirklich hinter allem Code des Hauptprogramms befinden 
muss.

Ob diese Art der Organisation üblich oder optimal ist weiß ich nicht. 
Ich habe keine Vergleichsmöglichkeiten.

von spess53 (Gast)


Lesenswert?

Hi

>Spess, kannst Du dazu etwas aus dem Nähkästchen plaudern?

OK. Bibliotheken bestehen bei mir aus zwei Dateien

xyz.inc

Enthält Macros, PIN-Vereinbarungen, Konstanten ... und eine Tabelle in 
der Form

   .equ use_function1 = 0
   .equ use_function2 = 0
   ...

xyz.asm

Enthält den Programmcode und den von der Bibliothek benötigten Speicher 
in .dseg.

Jede Funktion wird mit

   .if use_function
function1:
   ...
   .endif

   .if use_function
function2:
   ...
   .endif

bedingt assembliert.

Beide Dateien (und noch andere) sind in eine eigenen 
Include-Verzeichnis, das über Project->Assembler Options->Additional 
include path eingebunden wird. Von der xyz.inc wird allerdings eine 
Kopie im Project-Ordner benutzt.
Wenn du nun function2 benutzen willst änderst du einfach

    .equ use_function2 = 0  in   .equ use_function2 = 1

um.

In der Praxis kann das durchaus auch komplizierter sein, wenn z.B. eine 
Funktion noch andere benötigt

  .equ use_function3 = use_function1 | use_function2

Der RAM kann ebenfalls bedingt freigegeben werden

MfG Spess

von Markus (Gast)


Lesenswert?

@Spess: Alles klar, besten Dank für den Einblick. Ich denke, dann geht 
das in die richtige Richtung, so wie ich das handhabe.

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.