Forum: Mikrocontroller und Digitale Elektronik avr: mein erstes asm-prgramm, bitte um Kritik


von Michael R. (Firma: Brainit GmbH) (fisa)


Angehängte Dateien:

Lesenswert?

Hallo allerseits,

da sich hier ja ein paar Assembler-Gurus tummeln... ich habe beschlossen 
mich mal an Assembler am AVR zu versuchen. Anbei mein erster (noch 
unvollständiger) Versuch. Es handelt sich um den ersten (und 
einfachsten) Teil eines FFT-Moduls, basierend (ok, abgeschrieben) von 
Elm-Chans Fast FFT Module.

Mir gehts nicht darum, warum ich funktionierenden Code nochmal schreibe? 
sondern ums Lernen. lernen kann ich am besten wenn ich bestehenden, 
guten Code versuche zu verstehen und versuche selbst zu verändern und 
erweitern.

Der Code macht nix anderes als einen Block von Festkomma-Werten 
(zukünftig vom ADC) entgegenzunehmen, eine Window-Funktion 
drüberzulegen, und im FFT-Eingangspuffer abzulegen; dabei werden Real- 
und Imaginärteil auf identische Werte gesetzt (warum? müsst ihr Elm Chan 
fragen :-)

Etwas unsicher bin ich bei zwei Sachen:
- was ist die übliche Extension für asm-Files, welche dann in C-Code 
eingebunden werden? Ich hab irgendwo .sx gelesen, aber irgendwie kommt 
mir das fremd vor.... .s oder .S möchte ich vermeiden, da das die 
temporärfiles vom gcc sind.

- wie geht man mit "gemeinsamen" include-files (fft.h) um? Passt das mit 
der "Sicherung" per #define FFT_ASM so?

Ansonsten sind sicher 1000 Anfängerfehler drinnen, bitte um wohlwollende 
Korrektur :-)


Danke, Michi

Edit: ich hab beim ersten mal die falsche header-Datei erwischt, die 
ffft.h (drei f) ist die originale vom Elm-Chan. Könnte ein Mod das 
entfernen? Danke!

von chris (Gast)


Lesenswert?

wo sind die ASM-Datein?????

von Fpgakuechle K. (Gast)


Lesenswert?

chris schrieb:
> wo sind die ASM-Datein?????

gibbets nicht, das assemblerprogramm ist als inline assembler-code 
geschrieben. Schadir mal die ffft.h an und scroll nach unten.

MfG,

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Fpga Kuechle schrieb:
> chris schrieb:
>> wo sind die ASM-Datein?????
>
> gibbets nicht, das assemblerprogramm ist als inline assembler-code
> geschrieben. Schadir mal die ffft.h an und scroll nach unten.

NEIN!!!!

die ffft.h ist das original vom Elm Chan, hab leider die falsche Datei 
erwischt, habs aber eh dazugeschrieben. Bitte löschen bzw. ignorieren!


der ASM-Code steckt in der fft.sx; das war eine meiner Fragen welche 
extension man verwendet, wenn man C und asm mixt. .sx stand in 
irgendeinem der vielen Tutorials.

von Fpgakuechle K. (Gast)


Lesenswert?

dann wäre .asm die bessere Endung da wäre bei mir gleich das Atmel 
Studio gestartet worden.
1
1:  lpm  r18, Z+         ; window (lo byte)
2
  lpm     r19, Z+        ; window (hi byte)
3
  ld  r20, X+         ; input (lo byte)
4
  ld  r21, X+        ; input (hi byte)
5
6
  FMUL16x16 r25,r24,r23,r22,r21,r20,r19,r18,r15 ; output = input * window (LSBs not used)
7
8
  st  Y+, r24        ; output re (lo byte)
9
  st  Y+, r25        ; output re (hi byte)
10
  st  Y+, r24        ; output im (lo byte)
11
  st  Y+, r25        ; output im (lo byte)
12
13
  subi  r16, 1         ; loop (lo byte)
14
#if FFT_SIZE > 256
15
  sbci  r17, 0        ; loop (hi byte)
16
#endif
17
  brne  1b        ; loop over FFT_SIZE

Der letzte Befehl , soll das  ein Sprung zum Label 1 in der ersten Zeile 
werden.
Ist der Sprung relativ? Wenn ja ist es dann OK das die Zeile eins drüber 
per präprozessor eingefügt wird oder nicht? bessere Labelnamen z.B. 
LOOP_WINDOW wären sehr hilfreich.


Das zero register für die FMUL sollte vor jedem Aufruf auf 0 gesetzt 
werden. Bei dir scheint es nur einmal ge-CLR-ed zu werden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Fpga Kuechle schrieb:
> dann wäre .asm die bessere Endung da wäre bei mir gleich das Atmel
> Studio gestartet worden.

.sx ist eine der Standard-Extensions die GCC kennt.  Sie kennzeichnet 
Assembler-Quellen, über die vor der Assemblierung noch der 
C-Präprozessor (cpp) läuft.

.asm ist hingegen keine Standard-Extension, d.h. ohne weiteres Zutun 
wird die Datei dann als Linker-Description-File interpretiert...

>   st  Y+, r24        ; output re (lo byte)
>   st  Y+, r25        ; output re (hi byte)
>   st  Y+, r24        ; output im (lo byte)
>   st  Y+, r25        ; output im (lo byte)

Sieht komisch aus das.  Mindestens eins von Kommentar oder Code ist 
falsch.  Es sei denn lo(Im) = hi(Im) = lo(Re) = hi(Re).

>>   brne  1b        ; loop over FFT_SIZE

> Der letzte Befehl, soll das  ein Sprung zum Label 1 in der ersten
> Zeile werden.

Braucht es nicht mehr zu werden; ist es schon.

> Ist der Sprung relativ?

Ja, BRNE nimmt nur relative Offsets.

> Wenn ja ist es dann OK das die Zeile eins drüber
> per präprozessor eingefügt wird oder nicht?

Kein Problem. Der Assembler sieht nix vom Präprozessor, da er danach 
läuft.

> Das zero register für die FMUL sollte vor jedem Aufruf auf 0 gesetzt
> werden.

Es genügt diese Register auf den Wert zu setzen, den man drin braucht.

KeinProblem sofern es am Ende des Codes, bzw. sobald wieder C-Code 
ausgeführt wird, wieder 0 enthält.

von Fpgakuechle K. (Gast)


Lesenswert?

Johann L. schrieb:
> Fpga Kuechle schrieb:

>>>   brne  1b        ; loop over FFT_SIZE
>
>> Der letzte Befehl, soll das  ein Sprung zum Label 1 in der ersten
>> Zeile werden.
>
> Braucht es nicht mehr zu werden; ist es schon.
>

Warum heisst dann das Label '1' aber das Argument von BRNE  '1b'?

> Das zero register für die FMUL sollte vor jedem Aufruf auf 0 gesetzt
>> werden.
>
> Es genügt diese Register auf den Wert zu setzen, den man drin braucht.
>
> KeinProblem sofern es am Ende des Codes, bzw. sobald wieder C-Code
> ausgeführt wird, wieder 0 enthält.

? so wie ich den Kommentar bei der Definition des Macros verstehe, muss 
das Register immer auf 0 gesetzt sein, bevor das Makro aufgerufen wird. 
Aufgerufen wird es direct nach dem Label 1. Es ist nicht erkennbar das 
es dabei immer auf 0 gesetzt wird.

MfG,

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


Lesenswert?

Fpga Kuechle schrieb:
> Warum heisst dann das Label '1' aber das Argument von BRNE  '1b'?

UNIX-Assembler-Syntax: das sind “local labels”, die bekommen nur
eine Zahl als Namen, und dem Sprungziel wird ein “b” oder “f”
angehangen.  Es wird dann jeweils nach dem nächstgelegenen Label mit
dieser Nummer rückwärts oder vorwärts gesucht.

Ist besonders praktisch innerhalb von Codeschnipseln, die sich nur
auf sich selbst beziehen und dann eingebettet werden können in ein
größeres Stück (bspw. in Form von Inline Assembler).

von c-hater (Gast)


Lesenswert?

Johann L. schrieb:

>>   st  Y+, r24        ; output re (lo byte)
>>   st  Y+, r25        ; output re (hi byte)
>>   st  Y+, r24        ; output im (lo byte)
>>   st  Y+, r25        ; output im (lo byte)
>
> Sieht komisch aus das.  Mindestens eins von Kommentar oder Code ist
> falsch.  Es sei denn lo(Im) = hi(Im) = lo(Re) = hi(Re).

Es ist nur der Kommentar in der letzten Zeile falsch.

Das bissel Code ist KEINE FFT, sondern nur die vorgelagerte 
Windowing-Funktion für eine solche. Im Text des OP ist das auch korrekt 
beschrieben, nur der Name der Datei ist irreführend, wenn keine FFT in 
der Dose ist, sollte auf dem Dosenetikett eigentlich auch nicht FFT 
draufstehen...

Warum allerdings das Ergebnis der Windowing-Funktion gleichmäßig auf 
Real- und Imaginärteil verteilt wird, das weiß ich auch nicht (und hat 
sich auch der der Autor des OP schon in selbigem gefragt). Ich kenne 
ansonsten keine FFT-Implementierung, die das macht, da wird beim 
Windowing überall nur der Realteil bearbeitet und der Imaginärteil mit 
Nullen gefüllt, bevor die eigentliche FFT auf die Daten losgelassen 
wird. Diese Frage kann also wohl nur Elm beantworten.

Ich vermute (ohne es selbst überprüft zu haben): Es spielt mathematisch 
keine Rolle, ob da überall Null drinsteht oder immer derselbe Wert wie 
im zugehörigen Realteil und Elm hat diese (für mich auch neue) Tatsache 
benutzt, um ein "paar" (immerhin mindestens 256) Takte durch das 
Entfallen der ansonsten fälligen zusätzlichen Schleife zur 
Initialisierung des Imaginärteils der Daten zu sparen.

> KeinProblem sofern es am Ende des Codes, bzw. sobald wieder C-Code
> ausgeführt wird, wieder 0 enthält.

Das ist gewährleistet. Das Zero-Register wird nämlich zu Beginn der 
Routine auf den Stack gerettet und am Ende wiederhergestellt. D.h.: Wenn 
vorher eine Null drinstand, wird sie auch hinterher wieder drinstehen. 
Und innerhalb der Routine wird das Register nur einmal schreibend 
angefaßt und dabei wird auch nur eine Null reingeschrieben. Das 
FMUL-Macro benutzt das Register ausschließlich lesend.

Oder anders ausgedrückt: Da könnte man wahrscheinlich noch locker fünf 
Takte sparen...

von S. R. (svenska)


Lesenswert?

Michael Reinelt schrieb:
> Ich hab irgendwo .sx gelesen, aber irgendwie kommt mir das fremd vor....
> .s oder .S möchte ich vermeiden, da das die temporärfiles vom gcc sind.

Die Endung ".S" beschreibt eine Original-Quelldatei, und ".s" ist etwas, 
was Präprozessor und/oder Compiler mal ausgespuckt haben. Da unter 
Windows beide Dateien gleich sind, kann man das da nicht machen; mit 
".asm" ist man aber in jedem Fall auf der sicheren Seite.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Danke fürs Drüberschauen! (und Danke @Mods fürs aufräumen)

Copy&Paste-Fehler im Kommentar ist korrigiert.

Nachdem .sx GCC-Standard ist, werd ich dabei bleiben, auch wenns im 
ersten Moment (nicht nur für mich) ungewohnt ist.

c-hater schrieb:
> wenn keine FFT in
> der Dose ist, sollte auf dem Dosenetikett eigentlich auch nicht FFT
> draufstehen...
Ist ja noch nicht fertig, im Endausbau wird der Inhalt schon zum Etikett 
passen.

c-hater schrieb:
> Ich vermute (ohne es selbst überprüft zu haben): Es spielt mathematisch
> keine Rolle, ob da überall Null drinsteht oder immer derselbe Wert wie
> im zugehörigen Realteil und Elm hat diese (für mich auch neue) Tatsache
> benutzt, um ein "paar" (immerhin mindestens 256) Takte durch das
> Entfallen der ansonsten fälligen zusätzlichen Schleife zur
> Initialisierung des Imaginärteils der Daten zu sparen.

Vermute ich auch. Ich denke der Unterschied zwischen Im=0 und Im=Re ist 
(neben einer leichten Skalierung um Sqrt(2) eine Phasenverschiebung, die 
sollte egal sein.

"Sparen" täte man sich vermutlich nix, die beiden "st Y+" könnte man 
auch mit meinem zero-reg aufrufen.

Wenn der Rest mal fertig ist kann ich ja schauen obs einen unterschied 
macht.

Eine Frage ist noch unbeantwortet: die "Absicherung" per #ifdef FFT_ASM 
im Header-File, passt das so, ist das so üblich?

Ansonsten habts ihr keine Fehler gefunden?

von Fpgakuechle K. (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Fpga Kuechle schrieb:
>> Warum heisst dann das Label '1' aber das Argument von BRNE  '1b'?
>
> UNIX-Assembler-Syntax: das sind “local labels”, die bekommen nur
> eine Zahl als Namen, und dem Sprungziel wird ein “b” oder “f”
> angehangen.  Es wird dann jeweils nach dem nächstgelegenen Label mit
> dieser Nummer rückwärts oder vorwärts gesucht.

Wieder was gelernt - local labels. Danke für die Erklärung.

MfG,

von c-hater (Gast)


Lesenswert?

Michael Reinelt schrieb:

> "Sparen" täte man sich vermutlich nix, die beiden "st Y+" könnte man
> auch mit meinem zero-reg aufrufen.

Ähm ja, auch wieder wahr. Umso interessanter wäre es, zu erfahren, was 
sich der gute Elm nun tatsächlich dabei gedacht hat.

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.