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?
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). :-)
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.
@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
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
spess53 schrieb:> Klar kann man das.
Dann wird das vermutlich auch so funktionieren, wie ich es beschrieben
habe, also eine Funktion je Quelltext-Datei?
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
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)
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.
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