mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik unaligned acces bei einem imx6q


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mir ist nicht klar, warum die Long Long Variante einen unaligned access 
auslöst und die Int-Variante nicht. Der pointer unaligned liegt doch in 
beiden Fällen auf einer nicht durch 4 teilbaren Adresse, oder nicht?

Long Long Variante:
===================
int main(int argc, char **argv)
{
  long long i, temp, number=10;

  char *ptr = malloc(sizeof(long long) * number+1);
  long long *unaligned = (long long *)&ptr[2];

  for(i = 0; i < number; i++)
     temp = unaligned[i];

  return(0);
}

Int Variante
=============
int main(int argc, char **argv)
{
  int i, temp, number=10;

  char *ptr = malloc(sizeof(int) * number+1);
  int *unaligned = (int *)&ptr[2];

  for(i = 0; i < number; i++)
     temp = unaligned[i];

  return(0);
}

Autor: S. R. (svenska)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Decius schrieb:
> Mir ist nicht klar, warum die Long Long Variante einen unaligned access
> auslöst und die Int-Variante nicht.

Der erzeugte Assemblercode kann darüber vermutlich Auskunft geben.

Einen int32_t kann man immer in einem int64_t verstecken, d.h. der 
Compiler kann theoretisch ein einzelnes LDRD auf eine ausgerichtete 
Adresse durchführen, maskieren und shiften. Mit einem int64_t geht das 
nicht mehr.

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ S.R
Kann gut sein das es an einer prinzipiell anderen Umsetzung auf 
Assemblerebene liegt.

Die URL weiss ich nicht mehr, aber ich hatte gelesen, dass der Compiler 
aufgrund der Effizienz versucht bei aligned Zugriffen zu bleiben. Könnte 
also sein das er in der Int-Variante den unaligned Zugriff dadurch 
verhindert, dass er ihn mit aligned Zugriffen nachbildet. Was dann aber 
wahrscheinlich bei der Long Long Variante mit vertretbarer Effizienz 
nicht mehr möglich ist.

Das ist jetzt nur eine Vermutung meinerseits, es würde den Effekt aber 
erklären.

Autor: S. R. (svenska)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Decius schrieb:
> Kann gut sein das es an einer prinzipiell anderen Umsetzung auf
> Assemblerebene liegt.

Das ist sogar äußerst wahrscheinlich. Guck dir den Code an, dann hast du 
den exakten Grund. Rumtheoretisieren bringt im konkreten Fall nix.

Je nach Architektur sind unaligned-Zugriffe einfach nur ein 
Performance-Problem (x86), verboten (manche ARMs), oder produzieren 
Datenmüll (andere ARMs, m68k). Dementsprechend machen die Compiler auch 
unterschiedliche Dinge.

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der imx6q hat einen armv7-a Core. Damit unterstützt dieser sogar 
unaligned Zugriffe in Hardware, wenn diese korrekt initialisiert wird. 
Es ist zwar nicht empfohlen, aber es sollte schon möglich sein mit dem 
GCC, diesem uC und unaligned Zugriffen zu arbeiten. Ich werde durch ein 
fremdes Gerät gezwungen, damit umgehen zu können.

Ansonsten danke für Deine Antworten S.R.

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genaueres kann ich nicht vollständig schreiben, da ich an dem Folgenden 
noch arbeite, und so das Thema noch nicht gelöst ist. Aber der unaligned 
access wird wahrscheinlich die Hardwareintialisierung, die 
Linuxkernelübersetzung, das Bauen der Linuxdistribution und die 
Applikationscompilation tangieren. Die Applikation am Anfang dieses 
Threads, noch etwas erweitert, dient dazu meine Linuxdistribution zu 
testen, ob sie mit unaligned Zugriffen umgehen kann.

1) Hardware:
MRC p15,0,r4,c1,c0,0 /* read SCTL to r4 */
ORR r4,r4,#0x00400000 /* enable unaligned mode (U=1) */
BIC r4,r4,#0x00000002 /* disable alignment faults (A=0) */
MCR p15, 0, r4, c1, c0, 0 /* write r4 to SCTL*/

2) Linuxkernel:
muss mit den folgenden gcc optionen übersetzt werden.
-march=armv7-a
-mtune=cortex-a9
-munaligned-access

3) Linuxdistribution:
Aufgrund des neuen Kernels muss die Linuxdistribution neu gebaut werden.

4) Applikation:
muss mit den folgenden gcc optionen übersetzt werden.
-march=armv7-a
-mtune=cortex-a9
-munaligned-access

Punkt 1 erfolgt korrekt, das wurde durch Auslesen des STCL Registers 
innerhalb eines Treibers getestet. Thema im Moment sind die Punkt 2 und 
3.
Wie genau diese beiden Punkte erfolgen müssen, hängt natürlich vom 
Buildsystem ab. Aber habe ich da eventuell vom Prinzip her noch etwas 
vergessen?

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Decius schrieb:
> Der imx6q hat einen armv7-a Core. Damit unterstützt dieser sogar
> unaligned Zugriffe in Hardware, wenn diese korrekt initialisiert wird.

Aber nur bei max. 4-Byte-Zugriffen! Die LDRD/STRD Instruktionen, welche 
vermutlich für deine 8-Byte-Variable genutzt werden, brauchen immer 
Word-Alignment bei der Adresse (siehe Kapitel "A3.2.1
Unaligned data access" im ARMv7A/R Architecture Reference Manual).

Solche Probleme kann man grundsätzlich vermeiden, indem man keine 
Pointer wild umcastet und dereferenziert. Das ist in C(++) nämlich 
undefiniertes Verhalten (bis auf bestimmte Ausnahmen). Für fast alle 
solche Fälle gibt es korrekte wohldefinierte Vorgehensweisen, die 
garantiert immer auf jeder Plattform korrekt funktionieren. Weil du 
nicht geschrieben hast was du eigentlich vorhast, kann man hier aber 
keinen Lösungsvorschlag machen.

Beitrag #5041921 wurde vom Autor gelöscht.
Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb im Beitrag #5041921:
> malloc() solle eigentlich Pointer zurückgeben, die zum Alignment jedes
> beliebigen Datentyps passen.
Ja, aber nur der Original-Pointer, nicht das Ergebnis von 
Pointer-Arithmetik...

Autor: S. R. (svenska)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Decius schrieb:
> Der imx6q hat einen armv7-a Core. Damit unterstützt dieser sogar
> unaligned Zugriffe in Hardware, wenn diese korrekt initialisiert wird.

Nicht für 64 Bit-Zugriffe, weil diese - wenn unaligned - nicht atomisch 
sind.

Decius schrieb:
> Ich werde durch ein fremdes Gerät gezwungen, damit umgehen zu können.

Das hätte ich gerne etwas erklärt. ;-)
Hast du einen Linux-Treiber (bzw. -Anwendung), die böses Alignment 
macht?

Autor: René H. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Welchen Compiler und Version verwendest Du?

Ich kenne den imx6q nicht, auf einem RISC können solche casts zu 
unschönen core dumps führen. Je nach Compiler wird man gewarnt.
Auf einem Intel x86 ist das z.B. kein Problem.

Der Grund dafür ist, wenn der Speicher an einer Page Grenze liegt. 
Malloc interessieren die Page Grenzen nicht.

C11 kennt dafür aligned_malloc(). Evtl. kennt dein Compiler auch 
posix_memalign().

Grüsse,
René

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Als Buildsystem wird das yocto 1.8.2 verwendet. Das oben ist nur ein 
Beispielprogramm aus dem Internet um sicher einen Unaligned access 
provozieren zu können, es hat überhaupt nichts mit dem eigentlichen 
Programm zu tun. Aber ok da wäre dann wohl eine Lösung ohne Long Long 
Variablen besser.

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das eigentliche Programm ist ein Daemon, eventuell betrifft es auch noch 
ein Kernel-Module.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Decius schrieb:
> Aber ok da wäre dann wohl eine Lösung ohne Long Long Variablen besser.

Und sobald jemand den unaligned Access ganz abschaltet, oder den Treiber 
auf eine andere Plattform portiert, geht es wieder kaputt? Wäre es nicht 
besser, ganz auf undefinierte casts zu verzichten und es korrekt zu 
implementieren? Das geht mit großer Wahrscheinlichkeit auch mit 64 Bit 
Typen, man muss nur wissen was du eigentlich bezweckst.

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Dr. Sommer
Das oben ist nur ein
Beispielprogramm aus dem Internet um sicher einen Unaligned access
provozieren zu können, es hat überhaupt nichts mit dem eigentlichen
Programm zu tun.

1) Bei dem Programm hier geht es nur darum um überhaupt erstmal einen 
unaligned Access sicher provozieren zu können.Dabei soll es keine 
Abhängigkeit zu von irgendwoher gelieferten Daten geben.
/**********************************************************
* Source: https://lwn.net/Articles/259732/
* Entgegen der Erwartung provoziert das Programm hier auch keinen
* unaligned Access
***********************************************************/
unsigned int compare_ether_addr(const uint8_t *addr1, const uint8_t *addr2)
{
  const uint16_t *a = (const uint16_t *) addr1;
  const uint16_t *b = (const uint16_t *) addr2;
  return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | (a[2] ^ b[2])) != 0;
}

int main(int argc, char **argv)
{
  uint8_t buffer1[8]={0};
  uint8_t buffer2[8]={0};
  uint32_t result = 0;

  result = compare_ether_addr( &buffer1[2], buffer2 );

  return(result);
}

2) Ausserdem ist die frage ob ich nur Schritte vergessen habe, eine 
Distribution für die Freigabe des unaligned acces umzuarbeiten.

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Dr. Sommer: Es geht um Kosten, vorhandenen Quellcode und keine 
Anforderungen hinsichtlich der Protierbarkeit. Die zwei ersten Punkte 
haben Priorität. Ich hab mir schon nicht umsonst, die Beibehaltung des 
unaligned acces ausgesucht.

Ansonsten muss vorhanderner Quellcode umgeschrieben werden, und das ist 
kein Dreizeiler. Dabei könnte man in Strukturen mit dem Attribut packed 
und byteweisen Zugriff arbeiten. Wenn dann wird das aber der letzte 
Rettungsanker.

Autor: Nop (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Decius schrieb:

> 1) Bei dem Programm hier geht es nur darum um überhaupt erstmal einen
> unaligned Access sicher provozieren zu können.

Wie wäre es hiermit:
uint8_t *byte_ptr;
volatile uint16_t *int_ptr;
volatile uint32_t *word_ptr;
volatile uint64_t *dword_ptr;

uint64_t ponyhof[2];

byte_ptr = (uint8_t *) ponyhof;
byte_ptr++;
int_ptr = (volatile uint16_t *) byte_ptr;
word_ptr = (volatile uint32_t *) byte_ptr;
dword_ptr = (volatile uint64_t *) byte_ptr;

*int_ptr = 42;
*word_ptr = 42;
*dword_ptr = 42;

Autor: Programmiergenie (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Decius schrieb:
> Das oben ist nur ein
> Beispielprogramm aus dem Internet um sicher einen Unaligned access
> provozieren zu können, es hat überhaupt nichts mit dem eigentlichen
> Programm zu tun.
Das war mir schon klar, daher die Frage was du eigentlich vorhast.

Decius schrieb:
> * Entgegen der Erwartung provoziert das Programm hier auch keinen
> * unaligned Access
Wenn ich das richtig sehe sind beide Adressen Vielfache von 2, somit in 
Ordnung für 2-Byte-Datentypen.

Decius schrieb:
> 2) Ausserdem ist die frage ob ich nur Schritte vergessen habe, eine
> Distribution für die Freigabe des unaligned acces umzuarbeiten.
Das klingt nach einem aufwändigen Workaround...

Decius schrieb:
> @Dr. Sommer: Es geht um Kosten, vorhandenen Quellcode und keine
> Anforderungen hinsichtlich der Protierbarkeit. Die zwei ersten Punkte
> haben Priorität.
Ah, der klassische 
"Aus-Unwissenheit-lassen-wir-Probleme-sich-ansammeln-damit-wir-später-me 
hr-Aufwand-haben"-Ansatz.

Da du partout nicht verraten willst, wo das eigentliche Problem liegt, 
hier ein Schuss ins Blaue:
uint64_t test (uint8_t* data) {
  uint64_t i;
  memcpy (&i, data, sizeof(uint64_t));
  return i * 42;
}
Das funktioniert immer, auf jeder Plattform, mit allen 
Compiler-Einstellungen/Versionen, egal welches Alignment "data" hat. Es 
stürzt niemals ab, es ist lediglich das Ergebnis undefiniert. Falls du 
die Daten auf bestimmte statt zufällige Weise interpretieren möchtest, 
z.B. annehmen dass die Daten im Little-Endian-Format vorliegen, kannst 
du das so machen:
uint64_t test (uint8_t* data) {
  uint16_t i = ((uint16_t) data[0]) | (((uint16_t) data[1]) << 8);
  return i * 42;
}
Um Schreibarbeit zu sparen hier nur mit 16 Bits. Das funktioniert 
garantiert immer. Kein Herumfrickeln mit Compiler-Optionen und Umbauen 
der Linux-Distribution nötig.

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>@Programmiergenie:
>"Wenn ich das richtig sehe sind beide Adressen Vielfache von 2, somit in
>Ordnung für 2-Byte-Datentypen."

Adressraum 0x0000 ... 0x0007
0x0000
0x0001
0x0002
0x0003

0x0004
0x0005
0x0006
0x0007

0x0002 und 0x0006 mögen gerade und durch 2 teilbarsein. So wie ich das 
sehe sind es aber dennoch unaligned Adressen. Das Kriterium ist die 
Teilbarkeit durch 4........Ahhh, moment........

Arrays sind nicht gepackt oder? Das heisst, bei uint16_t test[2] würden 
die Felder auf den Adressen 0x0000 und 0x0004 liegen?

>@Programmiergenie:
>Ah, der klassische
>"Aus-Unwissenheit-lassen-wir-Probleme-sich-ansammeln-damit-wir-später-m e
>hr-Aufwand-haben"-Ansatz.

Ohne komplettes Projektwissen und die vollständige Kenntnis der 
Randbedingung, sowie ohne Kenntnis der später gewählten Lösung, sind 
solche Beurteilungen sinnlos. Spare Sie Dir also bitte.

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Arrays sind nicht gepackt oder? Das heisst, bei uint16_t test[2] würden
>die Felder auf den Adressen 0x0000 und 0x0004 liegen?

Ist so im Debugger nicht nachvollziehbar. Anders als Strukturen liegt 
das Array geschlossen hintereinander im Speicher.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Decius schrieb:
> So wie ich das
> sehe sind es aber dennoch unaligned Adressen.
Nö. Wie bereits erwähnt, steht in Kapitel "A3.2.1 Unaligned data access" 
im ARMv7A/R Architecture Reference Manual, dass für 2-Byte-Typen die 
Adressen lediglich vielfache von 2 sein müssen. Nur für 4 oder 8-Byte 
Typen muss die Adresse ein Vielfaches von 4 sein. Das unterscheidet sich 
eben stark nach Plattform. Daher sollte man einfach korrekten Code 
schreiben, statt zu pokern dass es auf der einen Plattform doch 
irgendwie geht.

Decius schrieb:
> Arrays sind nicht gepackt oder? Das heisst, bei uint16_t test[2] würden
> die Felder auf den Adressen 0x0000 und 0x0004 liegen?
Doch, Arrays sind gepackt, um Speicher zu sparen. Die Größe eines jeden 
Datentyps ist ja so, dass nachfolgende Elemente automatisch korrektes 
Alignment haben.

Autor: Decius (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Dr. Sommer
>Das unterscheidet sich
>eben stark nach Plattform. Daher sollte man einfach korrekten Code
>schreiben, statt zu pokern dass es auf der einen Plattform doch
>irgendwie geht.

Da bin ich Deiner Meinung, allerdings bin ich in meinen Entscheidungen 
nicht komplett frei. Ich würde auch gern offener über alles schreiben, 
es geht aber nicht.

******************************************

Den von Dir erwähnten Abschnitt "A3.2.1 Unaligned data access" habe ich 
mir mal genauer angesehen. Kann es sein das bei SCTL.A=0(vorletzte 
Spalte) der GCC trotz der Option -munaligned-access immer noch 
Assembler-Befehle benutzt die ein Alignment fault verursachen? Kann man 
den GCC, z.B. über eine Option, veranlassen nur Befehle zu benutzen die 
kein Alignment fault verursachen? Wenn das Programm sich so nicht 
übersetzen lässt, muss es dann eben umgeschrieben werden.

Autor: Dr. Sommer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Decius schrieb:
> Kann es sein das bei SCTL.A=0(vorletzte
> Spalte) der GCC trotz der Option -munaligned-access immer noch
> Assembler-Befehle benutzt die ein Alignment fault verursachen?

Gerade bei -munaligned-access generiert der GCC Instruktionen, welche 
alignment faults produzieren könnten:

-munaligned-access
-mno-unaligned-access
Enables (or disables) reading and writing of 16- and 32- bit values from 
addresses that are not 16- or 32- bit aligned. [...] If unaligned access 
is not enabled then words in packed data structures are accessed a byte 
at a time.

Das bezieht sich aber nur auf "packed" structs (die sind ja ohnehin eine 
nicht standardkonforme GCC-Erweiterung). Aber auch mit 
-mno-unaligned-access können natürlich noch Instruktionen generiert 
werden, die Alignment Faults produzieren. Jeder Zugriff auf 
(Halb)Wort-Pointer (LDR/LDRH-Instruktionen) ist ja potentiell unaligned, 
und da müsste ja jeder einzelne durch 4 einzelne Byte-Zugriffe 
(LDRB-Instruktionen - die einzigen die nie Alignment-Faults 
produzieren) und Veroderung realisiert werden, was die Performance 
dramatisch verschlechtern würde (das wäre btw äquivalent zur Nutzung 
meines obigen memcpy-Beispiels, falls der Compiler das memcpy nicht 
wegoptimiert).
Der GCC nimmt nun einmal an, dass man korrekten Code geschrieben hat, 
und das bedeutet dass man keine beliebigen char-Pointer auf 
2,4,8-Byte-Typen umcastet und dereferenziert. Wenn man das doch tut, 
helfen auch Compiler-Optionen nicht...

Ein Workaround wäre wie du schon erwähnt hast die Nutzung von packed 
structs und -mno-unaligned-access, aber das ist eben GCC-spezifisch und 
nicht portabel (und kann sich in zukünftigen GCC-Versionen auch ändern).

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.