www.mikrocontroller.net

Forum: Compiler & IDEs inline ASM 64Bit Shift


Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

der nur ASM-Programmiere macht in C und hat ein Problem... ;-)

Ich habe eine globale Sruktur:
typedef union
  {
    volatile unsigned long long dcf_bits;
    struct
      {
        volatile unsigned long long DUMMY:5;       // Rest für long long
        volatile unsigned long long START:1;       // Start Minute (0)
        volatile unsigned long long METEO:14;      // Wetterdaten
        volatile unsigned long long RUF:1;         // Rufbit 
        volatile unsigned long long MEST_F:1;      // Zeitumstellung
        volatile unsigned long long MESZ:2;        // MEZ (10), MESZ (01)
        volatile unsigned long long SEK_S:1;       // Schaltsekunde
        volatile unsigned long long BEGIN:1;       // Start Zeit (1)
        volatile unsigned long long MIN_E:4;       // Minute BCD
        volatile unsigned long long MIN_Z:3;       // Minute BCD  
        volatile unsigned long long MIN_P:1;       // Parität Minute
        volatile unsigned long long STU_E:4;       // Stunde BCD
        volatile unsigned long long STU_Z:2;       // Stunde BCD
        volatile unsigned long long STU_P:1;       // Parität Stunde
        volatile unsigned long long TAG_E:4;       // Tag BCD
        volatile unsigned long long TAG_Z:2;       // Tag BCD
        volatile unsigned long long WTAG:3;        // Wochentag BCD
        volatile unsigned long long MON_E:4;       // Monat BCD
        volatile unsigned long long MON_Z:1;       // Monat BCD
        volatile unsigned long long JAHR_E:4;      // Jahr BCD
        volatile unsigned long long JAHR_Z:4;      // Jahr BCD
        volatile unsigned long long DATUM_P:1;     // Parität Datum
      };
  } dcf_struktur;

extern dcf_struktur dcf77_daten;

In der Empfangsroutine wird mit
        dcf77_daten.dcf_bits = (dcf77_daten.dcf_bits >> 1);      
        dcf77_daten.dcf_bits |= 0x8000000000000000; // Bit setzen
(Das Bit wird natürlich nur bei empfangener 1 gesetzt,
geschoben wird immer).

Eigentlich bin ich eher angenehm überrascht von dem, was WinAVR da so 
erzeugt.
Die 64Bit-Geschichte sieht allerdings schrecklich aus, spielt aber 
bisher zuverlässig.

Meine Versuche, so auf die Schnelle den Shift und das Setzen des 
höchsten Bits per inline-ASM zu erledigen, sind daran gescheitert, daß 
er entweder den Zugriff auf dcf77_daten.dcf77_bits nicht auflösen kann 
oder mit den in/out-Registerzuweisungen gemault hat.
Eigentlich gibt es ja keinen in/out...
{
    asm volatile ("lds %A0, dcf77_daten.dcf77bits \n\t"
                  "lsr %A0 \n\t"
                  "sts dcf77_daten.dcf77_bits, %A0 \n\t"

                  "lds %A0, dcf77_daten.dcf77bits+1 \n\t"
                  "ror %A0 \n\t"
                  "sts dcf77_daten.dcf77_bits+1, %A0 \n\t"
...
                  "lds %A0, dcf77_daten.dcf77bits+7 \n\t"
                  "ror %A0 \n\t"
                  "sts dcf77_daten.dcf77_bits+7, %A0 \n\t"
    );

Ich will es ja nur an besagte Stellen einfügen (als Macro wäre es 
natürlich lsebarer).

Vielleicht kann ja da jemand den passenden Ansatz aus dem Hut zaubern...

Gruß aus Berlin
Michael

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lass das alles bleiben und mach einen Vektor mit 60 ganzen Bytes, das 
spart Speicher und Zeit, glaub mir :-)
Beispielsweise das Minuten-Feld so zusammenzufassen ist unsinnig, denn 
du mussts nachher ja doch wieder aufdröseln und von Hand 
zusammenrechnen, die Bits stellen nämlich KEIN Dualzahlensystem dar, 
sondern ein gepacktes BCD.

Ansonsten: Warum sind die Felder der Struktur alle 'long long'? 
Außerdem: schmeiß den 'long'- und 'short'-Kram raus und nehm die Typen 
aus <stdint.h> (uint_xx_t), das weißt du wenigstens, wie lang sie sind.

Und nein, ein 'int' ist nicht zwangsläufig 32 Bit breit und ein 'long 
int' muss auch nicht zwangsläufig größer als ein 'int' sein...

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

die ganze Geschichte ist eine beinahe 1:1 Kopie meines ASM-Programmes.
Hintergrund ist voresrt einzig eine C-Übung. :-)
Die long long - Geschichten sind noch drin, weil ich auch meine schon 
erkannten Fehler noch nicht rauskorrigiert habe.

Ich weiß, das, daß die Datentypen Systemabhängig sind, die Nutzung von 
stdint hat mir ein paar Warnings beschert, die ich vorerst nicht klären 
konnte. Ist mir im Moment auch relativ egal, die Chance, woanders als 
auf dem AVR in C zu programmieren, ist sehr klein.

Zum Format: ich weiß, daß es packed BCD ist, genau deshalb läßt es sich 
so gut lesbar (für mich?) zusammenstellen, selbst meine Software-Uhr 
läuft auch im packed BCD, das bleibt konsistent. Wandeln muß man 
irgendwo ja sowieo, so ist es selbst in der Ausgabe kürzer.

Ist aber eigentlich alles nicht mein augenblickliches Problem, der Shift 
in ASM kostet rund 50 Zyklen gegenüber einer Kopie-Mov-Arie, die der GCC 
dort erzeugt.

Vielleicht hat da ja doch mal jemand sowas gemacht.

PS: ich habe gerade mal mir den Tyoen in der Struktur rumgespielt: es 
ist sowohl dem Speicherbedarf aus auch der Programmgröße völlig egal, ob 
ich
typedef union
  {
    volatile unsigned long long dcf_bits;
    struct
      {
        volatile unsigned char  DUMMY:5;      // Rest für long long
        volatile unsigned char START:1;       // Start Minute (0)
        volatile unsigned int  METEO:14;      // Wetterdaten
        volatile unsigned char RUF:1;         // Rufbit 
        volatile unsigned char MEST_F:1;      // Zeitumstellung
        volatile unsigned char MESZ:2;        // MEZ (10), MESZ (01)
        volatile unsigned char SEK_S:1;       // Schaltsekunde
        volatile unsigned char BEGIN:1;       // Start Zeit (1)
        volatile unsigned char MIN_E:4;       // Minute BCD
        volatile unsigned char MIN_Z:3;       // Minute BCD  
        volatile unsigned char MIN_P:1;       // Parität Minute
        volatile unsigned char STU_E:4;       // Stunde BCD
        volatile unsigned char STU_Z:2;       // Stunde BCD
        volatile unsigned char STU_P:1;       // Parität Stunde
        volatile unsigned char TAG_E:4;       // Tag BCD
        volatile unsigned char TAG_Z:2;       // Tag BCD
        volatile unsigned char WTAG:3;        // Wochentag BCD
        volatile unsigned char MON_E:4;       // Monat BCD
        volatile unsigned char MON_Z:1;       // Monat BCD
        volatile unsigned char JAHR_E:4;      // Jahr BCD
        volatile unsigned char JAHR_Z:4;      // Jahr BCD
        volatile unsigned char DATUM_P:1;     // Parität Datum
      };
  } dcf_struktur;

benutze...

Gruß aus Berlin
Michael

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael U. wrote:
> Ich weiß, das, daß die Datentypen Systemabhängig sind, die Nutzung von
> stdint hat mir ein paar Warnings beschert, die ich vorerst nicht klären
> konnte.
Zeig die doch mal her.

> Ist mir im Moment auch relativ egal, die Chance, woanders als
> auf dem AVR in C zu programmieren, ist sehr klein.
Na... wie groß ist denn ein int und ein long aufm AVR...? :-)
Die Warnings lassen sich klären, dann empfehle ich dir trotzdem wieder 
stdint.

> Zum Format: ich weiß, daß es packed BCD ist, genau deshalb läßt es sich
> so gut lesbar (für mich?) zusammenstellen, selbst meine Software-Uhr
> läuft auch im packed BCD, das bleibt konsistent.
Na wenn du meinst, aber das Bitgefummel haste ja schon bemerkt.

> Vielleicht hat da ja doch mal jemand sowas gemacht.
Jo, haben schon viele gemacht :-)
Viele (mich eingeschlossen) benutzen wirklich Arrays und gut. Die 
anderen bemerken das Bitgeflüster beim Schieben vermutlich garnicht.

Was du aber machen kannst (böse, naja): Caste deine Struktur in einen 
ganzen Typ (uint64_t, oder mit union) und schiebe den mit Inline-ASM, 
dafür isses nämlich wieder in der Anleitung zur AVR-Libc dokumentiert.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael U. wrote:

> Meine Versuche, so auf die Schnelle den Shift und das Setzen des
> höchsten Bits per inline-ASM zu erledigen

Warum willst Du ohne Not ASM benutzen?
Das DCF77 kommt mit ner Wahnsinns-Speed von 1Bit/s daher, da braucht man 
nichts zu beschleunigen.

Wenn Du allerdings Flash sparen willst, dann nimm einfach nen besseren 
Ansatz:
Man muß nicht immer sämtliche 59 Bits nutzlos durch die Gegend shiften, 
es reicht völlig, wenn man direkt das richtige Bit im richtigen Byte 
setzt.

Dazu nimmt man einfach 2 Tabellen, eine liefert die Byteadresse, die 
andere die Bit-Wertigkeit.
Hier ist ein Beispiel dafür:

Beitrag "DCF77 Uhr in C mit ATtiny26"


Wenn Du unbedingt packed-BCD nehmen willst, kann man das leicht 
umstellen.
Es ist allerdings vollkommen sinnfrei, da C damit nicht umgehen kann. 
Alle Rechenoperationen und Zahlenein-/Ausgaben erfolgen ausschließlich 
binär.
Du mußt Dir dann auch eigene Rechenfunktionen für packed-BCD schreiben.


Peter

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael U. wrote:

> Die 64Bit-Geschichte sieht allerdings schrecklich aus, spielt aber
> bisher zuverlässig.

Ja ne, 64Bit mag der AVR-GCC überhaupt nicht.
Nimm 8 Byte und gut is:

#include <io.h>

uint8_t field_64[8];            // 64 bit = 8 byte


void shift_right64( uint8_t bit )
{
  uint8_t i;

  for( i = 0; i < (sizeof( field_64 ) - 1); i++ ){
    field_64[i] >>= 1;
    if( field_64[i+1] & 1 )
      field_64[i] |= 1<<7;
  }
  field_64[7] >>= 1;
  if( bit )
    field_64[7] |= 1<<7;        // insert new bit as bit 63
}



Peter

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

:-))

Peter, ich weiß daß das Beispiel mit DCF77 auf viele andere Arten und 
auch besser zu lösen geht.
Mir geht es im Moment ja auch nur darum, mit C zu diskutieren. ;-)
Da ich sonst ASM mache, interessierte mich eben, wie ich inline ASM mit 
Zugriff auf einen Union-Bestandteil hinbekomme.
Da stehen mir eben die Zeiger-Geschichten noch etwas im Weg.
Das es nun gerade DCF77 ist: naja, das kenne ich und weiß, was passieren 
soll.

Als normale globale Variable klappt es jetzt erstmal prinzipiell, den 
globalen union-Verweis kann der assembler aber nicht auflösen.

Ob das ganze so bleibt, steht in anderen Sternen.

Gruß aus Berlin
Michael

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

Bewertung
0 lesenswert
nicht lesenswert
Michael U. wrote:

> Meine Versuche, so auf die Schnelle den Shift und das Setzen des
> höchsten Bits per inline-ASM zu erledigen, ...

GCC's Inline-Assembler ist alles andere als leichte Kost.  Der greift
so tief in den Compiler ein, dass du dann schon den halben Weg zurück
gelegt hast, die ganze 64-Bit-Geschichte gleich als Pattern für den
Compiler zu schreiben. ;-)

Andererseits ist es in deinem Falle einfach.  Erstens könntest du den
Namen "dcf77_daten" tatsächlich direkt im Assemblercode benutzen --
aber natürlich kennt der Assembler nichts von deiner C-Struktur --,
und zweitens ist die Übergabe einer Speicheradresse als Parameter
eine der einfacher zu beherrschenden Constraints:
        asm volatile(
                        "lds __tmp_reg__, %0 + 7" "\n\t"
                        "lsr __tmp_reg__" "\n\t"
                        "sts %0 + 7, __tmp_reg__" "\n\t"
                        "lds __tmp_reg__, %0 + 6" "\n\t"
                        "ror __tmp_reg__" "\n\t"
                        "sts %0 + 6, __tmp_reg__" "\n\t"
                        "lds __tmp_reg__, %0 + 5" "\n\t"
                        "ror __tmp_reg__" "\n\t"
                        "sts %0 + 5, __tmp_reg__" "\n\t"
                        "lds __tmp_reg__, %0 + 4" "\n\t"
                        "ror __tmp_reg__" "\n\t"
                        "sts %0 + 4, __tmp_reg__" "\n\t"
                        "lds __tmp_reg__, %0 + 3" "\n\t"
                        "ror __tmp_reg__" "\n\t"
                        "sts %0 + 3, __tmp_reg__" "\n\t"
                        "lds __tmp_reg__, %0 + 2" "\n\t"
                        "ror __tmp_reg__" "\n\t"
                        "sts %0 + 2, __tmp_reg__" "\n\t"
                        "lds __tmp_reg__, %0 + 1" "\n\t"
                        "ror __tmp_reg__" "\n\t"
                        "sts %0 + 1, __tmp_reg__" "\n\t"
                        "lds __tmp_reg__, %0" "\n\t"
                        "ror __tmp_reg__" "\n\t"
                        "sts %0, __tmp_reg__"
                        : "+m" (dcf77_daten));

Allerdings sieht man an diesem Falle auch, warum man besser nicht
einfach so den C-Code durch Assemblercode ersetzen sollte: die
Reihenfolge deiner Bitschieberei war falsch herum.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch wrote:
> GCC's Inline-Assembler ist alles andere als leichte Kost.  Der greift
> so tief in den Compiler ein, dass du dann schon den halben Weg zurück
> gelegt hast, die ganze 64-Bit-Geschichte gleich als Pattern für den
> Compiler zu schreiben. ;-)

Wenn es so einfach ist, warum hat das dann noch keiner gemacht?

Auch versteht ja der Inline-Assembler nichtmal die nötigen weiteren 4 
Register %E0..%H0.


Peter

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

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:

> Wenn es so einfach ist, warum hat das dann noch keiner gemacht?

Weil Opensource-Software so nicht funktioniert.  Es passiert genau
das, wo jemandem hinreichend stark ,,der Schuh drückt''.  Solange
die wenigen Anwender der 64-Bit-Integer-Datentypen damit leben
können, die automatisch generierten Hilfsfunktionen der libgcc.a
statt expliziter Patterns zu benutzen, wird sich da auch kaum was
ändern.

Bei GCC kommt noch dazu, dass man das ganze Lizenzgeraffel erstmal
auf die Reihe bekommen hat, bevor man dort Code unterbringt.  Die
FSF besteht darauf, dass man ihnen das Copyright abtritt (die
offizielle Begründung ist, dass sie auf diese Weise die Rechte
besser verteidigen könnten).  Das ist ein wenig Papierkrieg.  Als
Deutscher kann man übrigens sein Copyright einfach mal gar nicht
abtreten, d. h. die entsprechende Unterschrift unter da Papier ist
ein völlig sinnloser Akt, aber sie scheint die FSF seelisch zu
beruhigen.

> Auch versteht ja der Inline-Assembler nichtmal die nötigen weiteren 4
> Register %E0..%H0.

Der Inline-Assembler versteht hier genau das, was der Compiler intern
versteht.  Die Ergänzung der Constraint-Modifier E...H wäre also der
erste (notwendige) Schritt für eine Integration von 64-Bit-Patterns
(allerdings der meiner Meinung nach leichteste Teil davon, den würde
sogar ich mir zutrauen).

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Danke, Jörg, voller Erfolg (für mich zumindest).
Ich habe noch das Setzen des höchsten Bits beim Empfang einer 1 
reingebaut
( sec und ror statt lsr), läuft alles.

Sind rund 600 Byte gesparter Flash + die Laufzeit.
Bringt also im Spezialfall auch woanders sicher Punkte, sollte ich es 
mal brauchen.

Gruß aus Berlin
Michael

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

Bewertung
0 lesenswert
nicht lesenswert
Michael U. wrote:

> Sind rund 600 Byte gesparter Flash

Ob du jetzt von Atmel Geld zurück bekommst? :-)

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Jörg Wunsch wrote:
> Michael U. wrote:
>
>> Sind rund 600 Byte gesparter Flash
>
> Ob du jetzt von Atmel Geld zurück bekommst? :-)

Ich werde es mal versuchen. ;-))

Naja, es geht um diese Geschichte hier:
Beitrag "Sensoren mit RFM02/12, FOST02, HP03S (ASM)"

Ich habe in der vorigen Woche mal das Modul mit dem Display komplett 
nach C getragen, als erste! Übung. ;)
Die RFM12-Empfamgsroutine läuft komplett transparent im FIFO-IRQ des 
RFM12.
DCF77 läuft im 10ms Timer-IRQ.

Im ungünstigsten Fall darf also der 10ms IRQ nicht länger dauern als der 
Empfang eine Bytes durch den RFM12 bei 19200 Baud (rund 500µs).

Das reicht unter allen Umständen mit sehr viel Reserve. In ASM...
In C lief es zwar auch stabil, die erzeugte Codelänge der DCF Geschichte 
hat mich aber beunruhigt. Nun kann ich wieder ruhig schlafen.
Oder weiter programmieren. ;-)

Der 2. ungünstigste Fall ist die RFM12-IRQ-Routine, wenn das Paket 
komplett ist (18 Byte). Die werden dann komplett in die Struktur für die 
Sensoren kopiert. Das kann der GCC aber brauchbar.


Gruß aus berlin
Michael

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael U. wrote:
> Sind rund 600 Byte gesparter Flash + die Laufzeit.

Schön, aber da ist mein obiger C-Code doch besser.
Er braucht nur 36 Byte statt 80 Byte für den Assemblercode.


Es ging mir auch mal so, als ich von Assembler wechselte, daß ich 
Assembler mit C mixen wollte.
Aber in 99,9% der Fälle bringt es nix, außer Verschlechterung der 
Lesbarkeit und Portabilität.

Schau lieber ins Assemblerlisting, um zu sehen wie der Compiler tickt 
(was er mag bzw. was ihm Bauchschmerzen bereitet).


Peter

Autor: Michael U. (amiga)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:
> Michael U. wrote:
>> Sind rund 600 Byte gesparter Flash + die Laufzeit.
>
> Schön, aber da ist mein obiger C-Code doch besser.
> Er braucht nur 36 Byte statt 80 Byte für den Assemblercode.
>
>
> Es ging mir auch mal so, als ich von Assembler wechselte, daß ich
> Assembler mit C mixen wollte.
> Aber in 99,9% der Fälle bringt es nix, außer Verschlechterung der
> Lesbarkeit und Portabilität.
Sicher richtig.
>
> Schau lieber ins Assemblerlisting, um zu sehen wie der Compiler tickt
> (was er mag bzw. was ihm Bauchschmerzen bereitet).
Hätte ich das nicht gemacht, wäre ich nicht darüber gestolpert, es lief 
ja auch komplett in C stabil.

Mein eigentliches Problem ist ganz simpel:
Ich will mit dem Kram weiter machen.
Mit diesem Modul in C um zu üben.
Ich brauche einen der Webserver mit AVR, der den RFM12-Empfang rein 
bekommt (sehe ich kein unlösbares Problem für mich) und der einen 
minimalen FTP-Client hat. Sehe ich noch ziemliche Probleme.

Seltsamerweise gibt es ja dutzende Logger für alles mögliche (mit AVR), 
die auf SD-Card loggen.
Seltsamerweise gibt es auch genug Webserver (mit AVR), mit denen ich 
auch aus Afrika schauen kann, ob hier ein Fenster offen steht oder wie 
warm es ist.

Seltsamerweise gibt es aber nur eine mir bekannte Source mit einem 
FTP-Client. Allerdings für die flasche Hardware (Realtek 8019).

Fast jeder hat mittlerweile ein NAS, das auch einen FTP-Server bietet.
Es liegt (für mich) also nahe, die lokal geloggten Daten mit dem 
Webserver per Funkmodul zu empfangen, zwischenzuspeichern und alle 1-2 
Stunden per FTP als Datei auf das NAS zu schieben.

Muß ich also selber machen. in C eben. ;-)))

Gruß aus Berlin
Michael

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.