Forum: Compiler & IDEs inline ASM 64Bit Shift


von Michael U. (amiga)


Lesenswert?

Hallo,

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

Ich habe eine globale Sruktur:
1
typedef union
2
  {
3
    volatile unsigned long long dcf_bits;
4
    struct
5
      {
6
        volatile unsigned long long DUMMY:5;       // Rest für long long
7
        volatile unsigned long long START:1;       // Start Minute (0)
8
        volatile unsigned long long METEO:14;      // Wetterdaten
9
        volatile unsigned long long RUF:1;         // Rufbit 
10
        volatile unsigned long long MEST_F:1;      // Zeitumstellung
11
        volatile unsigned long long MESZ:2;        // MEZ (10), MESZ (01)
12
        volatile unsigned long long SEK_S:1;       // Schaltsekunde
13
        volatile unsigned long long BEGIN:1;       // Start Zeit (1)
14
        volatile unsigned long long MIN_E:4;       // Minute BCD
15
        volatile unsigned long long MIN_Z:3;       // Minute BCD  
16
        volatile unsigned long long MIN_P:1;       // Parität Minute
17
        volatile unsigned long long STU_E:4;       // Stunde BCD
18
        volatile unsigned long long STU_Z:2;       // Stunde BCD
19
        volatile unsigned long long STU_P:1;       // Parität Stunde
20
        volatile unsigned long long TAG_E:4;       // Tag BCD
21
        volatile unsigned long long TAG_Z:2;       // Tag BCD
22
        volatile unsigned long long WTAG:3;        // Wochentag BCD
23
        volatile unsigned long long MON_E:4;       // Monat BCD
24
        volatile unsigned long long MON_Z:1;       // Monat BCD
25
        volatile unsigned long long JAHR_E:4;      // Jahr BCD
26
        volatile unsigned long long JAHR_Z:4;      // Jahr BCD
27
        volatile unsigned long long DATUM_P:1;     // Parität Datum
28
      };
29
  } dcf_struktur;
30
31
extern dcf_struktur dcf77_daten;

In der Empfangsroutine wird mit
1
        dcf77_daten.dcf_bits = (dcf77_daten.dcf_bits >> 1);      
2
        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...
1
{
2
    asm volatile ("lds %A0, dcf77_daten.dcf77bits \n\t"
3
                  "lsr %A0 \n\t"
4
                  "sts dcf77_daten.dcf77_bits, %A0 \n\t"
5
6
                  "lds %A0, dcf77_daten.dcf77bits+1 \n\t"
7
                  "ror %A0 \n\t"
8
                  "sts dcf77_daten.dcf77_bits+1, %A0 \n\t"
9
...
10
                  "lds %A0, dcf77_daten.dcf77bits+7 \n\t"
11
                  "ror %A0 \n\t"
12
                  "sts dcf77_daten.dcf77_bits+7, %A0 \n\t"
13
    );

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

von Sven P. (Gast)


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...

von Michael U. (amiga)


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
1
typedef union
2
  {
3
    volatile unsigned long long dcf_bits;
4
    struct
5
      {
6
        volatile unsigned char  DUMMY:5;      // Rest für long long
7
        volatile unsigned char START:1;       // Start Minute (0)
8
        volatile unsigned int  METEO:14;      // Wetterdaten
9
        volatile unsigned char RUF:1;         // Rufbit 
10
        volatile unsigned char MEST_F:1;      // Zeitumstellung
11
        volatile unsigned char MESZ:2;        // MEZ (10), MESZ (01)
12
        volatile unsigned char SEK_S:1;       // Schaltsekunde
13
        volatile unsigned char BEGIN:1;       // Start Zeit (1)
14
        volatile unsigned char MIN_E:4;       // Minute BCD
15
        volatile unsigned char MIN_Z:3;       // Minute BCD  
16
        volatile unsigned char MIN_P:1;       // Parität Minute
17
        volatile unsigned char STU_E:4;       // Stunde BCD
18
        volatile unsigned char STU_Z:2;       // Stunde BCD
19
        volatile unsigned char STU_P:1;       // Parität Stunde
20
        volatile unsigned char TAG_E:4;       // Tag BCD
21
        volatile unsigned char TAG_Z:2;       // Tag BCD
22
        volatile unsigned char WTAG:3;        // Wochentag BCD
23
        volatile unsigned char MON_E:4;       // Monat BCD
24
        volatile unsigned char MON_Z:1;       // Monat BCD
25
        volatile unsigned char JAHR_E:4;      // Jahr BCD
26
        volatile unsigned char JAHR_Z:4;      // Jahr BCD
27
        volatile unsigned char DATUM_P:1;     // Parität Datum
28
      };
29
  } dcf_struktur;

benutze...

Gruß aus Berlin
Michael

von Sven P. (Gast)


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.

von Peter D. (peda)


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

von Peter D. (peda)


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:
1
#include <io.h>
2
3
uint8_t field_64[8];            // 64 bit = 8 byte
4
5
6
void shift_right64( uint8_t bit )
7
{
8
  uint8_t i;
9
10
  for( i = 0; i < (sizeof( field_64 ) - 1); i++ ){
11
    field_64[i] >>= 1;
12
    if( field_64[i+1] & 1 )
13
      field_64[i] |= 1<<7;
14
  }
15
  field_64[7] >>= 1;
16
  if( bit )
17
    field_64[7] |= 1<<7;        // insert new bit as bit 63
18
}


Peter

von Michael U. (amiga)


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

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


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:
1
        asm volatile(
2
                        "lds __tmp_reg__, %0 + 7" "\n\t"
3
                        "lsr __tmp_reg__" "\n\t"
4
                        "sts %0 + 7, __tmp_reg__" "\n\t"
5
                        "lds __tmp_reg__, %0 + 6" "\n\t"
6
                        "ror __tmp_reg__" "\n\t"
7
                        "sts %0 + 6, __tmp_reg__" "\n\t"
8
                        "lds __tmp_reg__, %0 + 5" "\n\t"
9
                        "ror __tmp_reg__" "\n\t"
10
                        "sts %0 + 5, __tmp_reg__" "\n\t"
11
                        "lds __tmp_reg__, %0 + 4" "\n\t"
12
                        "ror __tmp_reg__" "\n\t"
13
                        "sts %0 + 4, __tmp_reg__" "\n\t"
14
                        "lds __tmp_reg__, %0 + 3" "\n\t"
15
                        "ror __tmp_reg__" "\n\t"
16
                        "sts %0 + 3, __tmp_reg__" "\n\t"
17
                        "lds __tmp_reg__, %0 + 2" "\n\t"
18
                        "ror __tmp_reg__" "\n\t"
19
                        "sts %0 + 2, __tmp_reg__" "\n\t"
20
                        "lds __tmp_reg__, %0 + 1" "\n\t"
21
                        "ror __tmp_reg__" "\n\t"
22
                        "sts %0 + 1, __tmp_reg__" "\n\t"
23
                        "lds __tmp_reg__, %0" "\n\t"
24
                        "ror __tmp_reg__" "\n\t"
25
                        "sts %0, __tmp_reg__"
26
                        : "+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.

von Peter D. (peda)


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

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


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).

von Michael U. (amiga)


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

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


Lesenswert?

Michael U. wrote:

> Sind rund 600 Byte gesparter Flash

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

von Michael U. (amiga)


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

von Peter D. (peda)


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

von Michael U. (amiga)


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

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.