Hallo,
ich habe gehört, dass ARM es nicht mögen und mit einem Fehler reagieren,
wenn man eine Adresse als Pointer auf eine 32Bit Adresse interpretieren
möchte, diese jedoch kein vielfaches von 4 ist.
Beispielsweise habe ich ein Array aus uint8_t und möchte Element 3 bis 6
als 32 Bit Wert interpretieren.
Dann kriege ich hier leider einen infinite_loop. Mache ich den folgenden
Zugriff auf eine Variable mit dem Startelement 0,4,8,16,... geht das
wunderbar.
1
uint8_tArray[100];
2
uint8_tx;
3
4
*((uint32_t*)&Array[3])=(uint32_t)&x;// geht nicht
Arno schrieb:> Und was mache ich, wenn ich diese wahllos unvorhersehbar gemischt in> einem Array speichern möchte? Mal 16,32,8 bit.
Vorher zerlegen und die Teile dann richtig aligned speichern.
Arno schrieb:> ich habe gehört, dass ARM es nicht mögen
Welche ARM? Von den ARM µCs haben nur die Cortex-M0 das Problem, weil
sie als Armv6-m tatsächlich nur alinged Addressen ansteuern können.
Cortex-M3 ff. sind aber Armv7-m und können das sehr wohl (allerdings mit
1-2 Straftakten).
Cortex-M3/4 können das, man kann es aber abschalten, um solche Fehler zu
finden (gibt dann eine Exception). Solcher Code ist nämlich in C und C++
verboten.
Details und wie man es korrekt macht findet sich unter
Serialisierung.
memcpy (geht immer und ist genau dafür gemacht!)
unsigned long kartoffeln;
unsigned char uca[1000];
int idx = 17+4;
/* rin inne Kartoffeln*/
memcpy(kartoffeln, uca+idx, sizeof(kartoffeln));
/* aus ausse Kartoffeln*/
memcpy(uca+idx+200, kartoffeln, sizeof(kartoffeln));
Dr. Sommer schrieb:> A. S. schrieb:>> memcpy (geht immer und ist genau dafür gemacht!)>> Aber das Ergebnis ist Plattform abhängig und der Code daher nicht> portabel...
OP hat im Titel die Problemdomäne auf STM32 eingeschränkt. Grundsätzlich
ist die Methode mit memcpy portabel zu allen Architekturen der selben
Endianness. Er soll die 4 Bytes einfach in den Speicher eines uint32
kopieren und fertig.
Lösung 2:
Er soll sich vorher Gedanken machen so daß die Daten nicht wie Kraut und
Rüben zu liegen kommen sondern jedes Wort immer schön an einem
vielfachen seiner eigenen Länge ausgerichtet sein wird. Dann kann er
einfach ein union aus nem byte-array und nem struct nehmen und direkt
drauf zugreifen, das wäre die eleganteste Lösung, geht aber eben auch
nur innerhalb der selben Endianness.
Strategien um das struct zu entwerfen: entweder stur nach Größe
sortieren, die größten zuerst, dann die nächst kleineren, usw. Oder wenn
zusammengehöriges zusammen bleiben soll dann eben von Hand genau so
anordnen daß jedes Element an einem ganzzahligen Vielfachen seiner
eigenen Länge zu liegen kommt, erforderlichenfalls an entsprechnder
Stelle explizit Dummy-Bytes als Platzhalter einfügen wo sonst Padding
entstehen würde. Das geht eigentlich recht fix, die Regeln sind simpel
und alle ABIs der letzten zig Jahre und auch alle zukünftigen haben die
selben Alignment-Regeln.
Zur Sicherheit vielleicht noch ein static assert auf das erwartete
sizeof des structs machen um einen eventuellen Bruch der Regeln in der
Zukunft einfach detektieren zu können (natürlich darf er es NICHT als
packed deklarieren sonst ist es Essig mit detektieren einer
Regeländerung mittels sizeof).
Bernd K. schrieb:> OP hat im Titel die Problemdomäne auf STM32 eingeschränkt.
Es wäre aber denkbar dass man den Code irgendwann mal woanders hin
portieren könnte.
Bernd K. schrieb:> Grundsätzlich ist die Methode mit memcpy portabel zu allen Architekturen> der selben Endianness.
Und dem selben Padding und dem selben Vorzeichen Format (teilweise),
falls man auch vorzeichenbehaftete Integer verwendet.
Bernd K. schrieb:> Dann kann er einfach ein union aus nem byte-array und nem struct nehmen> und direkt drauf zugreifen, das wäre die eleganteste Lösung
Dies ist in C Implementation defined (nicht portabel) und in C++ ganz
verboten. Also nicht sehr elegant.
Bernd K. schrieb:> alle ABIs der letzten zig Jahre und auch alle zukünftigen haben die> selben Alignment-Regeln.
Sicher? AVR z.B. hat gar keine Alignment Anforderungen, ARM schon.
Die sicherste Lösung ist die Verwendung von Bitshifts. Die funktionieren
auf vorzeichenlosen Integern garantiert immer überall gleich. Steht auch
alles im verlinkten Artikel...
Arno schrieb:> *((uint32_t *)&Array[4]) = (uint32_t)&x; // geht>> Wie löse ich dieses Dilemma?
Wirklich am besten fährt man in C, wenn man seine Algorithmen so
schreibt, daß man dazu möglichst keine Cast's benötigt.
Bedenke mal eines: mit einem Cast zwingt man den Compiler zu etwas,
gegen das er sich eigentlich sträubt - und das allerhöchstwahrscheinlich
aus gutem Grunde.
Also halte dich besser daran:
Bernd K. schrieb:> Lösung 2:>> Er soll sich vorher Gedanken machen so daß die Daten nicht wie Kraut und> Rüben zu liegen kommen
Sehe ich ganz genauso. Wenn man merkt, daß man mit Casts um sich
schmeißen muß wie ein Anarchist die Bomben, dann sollte man seinen
ganzen Algorithmus in die Tonne werfen und sich einen besseren
ausdenken.
W.S.
Dr. Sommer schrieb:> Sicher? AVR z.B. hat gar keine Alignment Anforderungen, ARM schon.
Seit wann kann AVR z.b. 5 Bit schreiben?
Ein Alignment auf Bytes ist völlig willkürlich.
Dr. Sommer schrieb:> Aber das Ergebnis ist Plattform abhängig und der Code daher nicht> portabel...
Nein. Zur Aufgabenstellung ist memcpy immer portabel und richtig: die 4
folgenden Bytes sollen ein 32bit wert sein. Dass man sie ggf nochmals
umformen muss, weil sie anders kodiert wurden, ist davon unabhängig.
Hier ging es um das alignment, nicht um endian oder Repräsentation.
D. H. schrieb:> Dr. Sommer schrieb:> Nein, Speicheradressen sind immer in Bytes. Ein Byte kann aber mehr als> 8 Bit haben.>> https://en.wikipedia.org/wiki/12-bit> https://en.wikipedia.org/wiki/18-bit> https://en.wikipedia.org/wiki/4-bit> https://en.wikipedia.org/wiki/36-bit
Ohne ausformulierte Sätze habe ich keine Ahnung, was du mir sagen
willst. Vermutlich willst du nur trollen und langwierige Erklärungen
provozieren.
A. S. schrieb:> Hier ging es um das alignment, nicht um endian oder Repräsentation
Okay, streng genommen richtig. Ich wollte halt ggf. später auftretenden
Problemen vorgreifen und eine komplett-Lösung vorschlagen. Gerade in C
ist es eben so, dass das, was man machen will, nicht immer das richtige
ist, und alternative Herangehensweisen sinnvoll sein können.
Außerdem ist erst memcpy und dann tauschen auch nicht kürzer als
Bitshifts.
Dr. Sommer schrieb:> Außerdem ist erst memcpy und dann tauschen auch nicht kürzer als> Bitshifts.
Der TO wollte ab Adresse a n Bytes. Die Aufgabe ergibt sich auch, wenn
man structs kopieren will.
Bitshiften vermeidet das Problem implizit, weil es nur Bytes kopiert.
Die Aufgabe ist aber eine andere: von einer bekannten KODIERUNG in eine
andere zu übersetzen. Wir wissen aber darüber nichts. Und ansehen kann
man es den Daten auch nicht.
Und ja, ich halte es für ein echtes Problem, wenn Leute sich dann selber
Byte-zugriffs-mechanismen bauen, weil sie denken, memcpy ist zu langsam.
Da wird dann gecastet statt stur &x, sizeof(x)
A. S. schrieb:> Die Aufgabe ist aber eine andere: von einer bekannten KODIERUNG in eine> andere zu übersetzen.
Und genau das geht mit memcpy nicht, weil es die Bytes nicht tauscht und
kein Padding beachtet.
A. S. schrieb:> Und ja, ich halte es für ein echtes Problem, wenn Leute sich dann selber> Byte-zugriffs-mechanismen bauen, weil sie denken, memcpy ist zu langsam.
Das ist nochmal ein ganz anderes Problem. memcpy kann ja auch ggf.
wegoptimiert werden.
So werden die 4 Bytes in Little-Endian-Reihenfolge (leicht änderbar) in
das Array geschrieben. Dieses Verhalten bleibt auch auf anderen
Plattformen gleich. Die Frage ist aber noch, warum eigentlich eine
Adresse so in ein Array gepackt werden soll? Das Array wird doch
bestimmt per UART oder so verschickt, was soll ein anderes Bauteil mit
einer internen Adresse anfangen?
Dr. Sommer schrieb:> Und genau das geht mit memcpy nicht, weil es die Bytes nicht tauscht und> kein Padding beachtet.
Was hast Du immerzu mit Deinem Padding? Er kennt doch den Quelloffset
genau und die Zieladresse ist eine normale Variable und die ist per
Definition immer richtig aligned.
Bernd K. schrieb:> Er kennt doch den Quelloffset> genau und die Zieladresse ist eine normale Variable und die ist per> Definition immer richtig aligned.
Ja, bei einem einzelnen Integer ist das kein Problem. Wenn man aber, wie
hier nahe gelegt, ein ganzes struct per memcpy in ein Array kopiert um
dieses zu verschicken, hat man ein Plattform-abhängiges Padding drin:
A. S. schrieb:> Der TO wollte ab Adresse a n Bytes. Die Aufgabe ergibt sich auch, wenn> man structs kopieren will.
Dr. Sommer schrieb:> Wenn man aber, wie> hier nahe gelegt, ein ganzes struct per memcpy in ein Array kopiert um> dieses zu verschicken, hat man ein Plattform-abhängiges Padding drin
Dann entwirft man das Struct vorher so (und das hab ich unübersehbar
dazugeschrieben) daß auch unter strengst möglichen Alignmentregeln kein
Padding entstehen wird! Das ist ganz einfach, man muß mur seinen Geist
öffnen und die Logik einströmen lassen.
Bernd K. schrieb:> Dann entwirft man das Struct vorher so (und das hab ich unübersehbar> dazugeschrieben) daß auch unter strengst möglichen Alignmentregeln kein> Padding entstehen wird!
Und du bist absolut sicher dass das immer unter allen Plattformen
garantiert wirkt? Auch wenn man Datentypen verwendet, welche z.B. 3
Bytes groß sind?
Welchen Vorteil bietet das gegenüber manuellem Kopieren der Bytes?
Dr. Sommer schrieb:> Auch wenn man Datentypen verwendet, welche z.B. 3> Bytes groß sind?
Zeig mir mal einen nativen Datentyp der 3 Bytes groß ist. Wenn das ein
struct aus 3 Bytes sein soll dann hat das auf strengen Platformen ein
sizeof() von 4. Dann fügt man halt noch ein explizites Dummy-Byte in
sein struct ein dann hat es überall 4, selbst auf Plattformen die
überhaupt keine Regeln erzwingen!
Bernd K. schrieb:> Zeig mir mal einen nativen Datentyp der 3 Bytes groß ist
Es gibt Systeme (PIC?) mit int24_t.
Bernd K. schrieb:> Dann fügt man halt noch ein explizites Dummy-Byte in> sein struct ein dann hat es überall 4, selbst auf Plattformen die> überhaupt keine Regeln erzwingen!
Das heißt man frickelt herum, bis es auf Biegen und Brechen "ganz
sicher" auf allen Plattformen funktioniert, statt einfach die vom
C-Standard garantiert korrekte Lösung zu benutzen?
Arno schrieb:> Danke alle, ich habe jetzt erstmal wider Willen alles auf 32bit aligned.
Du musst jeden Member nur auf seine eigene Länge alignen, also uint16_t
auf 2 Byte und uint8_t auf beliebige. Du musst sie also einfach so
umsortieren daß sie alle automatisch auf solche Offsets fallen, zum
Beispiel 2 einzelne Byte, dann ein uint16, dann ein uint32, die fügen
sich in der Reihenfolge auch bei strengstem Alignment lückenlos. Ist
eigentlich ganz einfach.
Dr. Sommer schrieb:> Das heißt man frickelt herum, bis es auf Biegen und Brechen
Nein, man befolgt einfach die Alignment-Regeln. Da muss man gar nichts
frickeln, das kann man fast blind hinschreiben weil die Regeln absolut
simpel sind.
Bernd K. schrieb:> Da muss man gar nichts> frickeln, das kann man fast blind hinschreiben weil die Regeln absolut> simpel sind.
Kennst du eine Quelle, die diese Regeln allgemeingültig für alle
Plattformen, auf denen Standard-C läuft, konsistent darlegt? Im
C-Standard stehen sie nämlich nicht, und das ist das Dokument, nach dem
man programmieren sollte.
Dr. Sommer schrieb:> Es gibt Systeme (PIC?) mit int24_t.
Wie gesagt, wenn man das portabel machen will fügt man noch ein
explizites Padding-Byte am Ende ein und dann funktioniert das auch auf
strengeren Plattformen (also auf allen).
Dr. Sommer schrieb:> Kennst du eine Quelle, die diese Regeln allgemeingültig für alle> Plattformen, auf denen Standard-C läuft, konsistent darlegt?
Du schaust Dir die ABIs aller in Frage kommenden Prozessoren an und
nimmst die mit dem strengsten Alignment. Daran orientierst Du Dich. Bei
allen die in den letzten zig Jahren gebaut wurden bedeutet das:
~~~ Alignment an einem Vielfachen der eigenen Länge. ~~~
Ganz einfach.
Bernd K. schrieb:> Du schaust Dir die ABIs aller in Frage kommenden Prozessoren an und> nimmst die mit dem strengsten Alignment. Daran orientierst Du Dich.
Ist mir zu aufwendig. Wer weiß wohin der Code irgendwann mal portiert
werden soll. Ich schreibe einfach die bereits genannte Lösung hin und
weiß ganz sicher, dass sie immer funktioniert:
Beitrag "Re: STM32: Adresse mit Rest als uint32_tinterpretieren"Bernd K. schrieb:> Ganz einfach.
Ich hätte da halt gerne einen etwas besseren Beleg als "prof7bit hat das
mal geschrieben".
Dr. Sommer schrieb:> Ist mir zu aufwendig.
Na Du bist ja extrem einfach zu überfordern wenn sowas simples wie
Variablen nach der Größe zu sortieren oder unterschiedliche Bauklötzchen
in die Slots passender Größe einzufügen schon an Deine Grenzen stößt.
> Ich hätte da halt gerne einen etwas besseren Beleg als "prof7bit hat das> mal geschrieben".
Glaub wem Du willst und mach was Du willst. Ich für meinen Teil wollte
eigentlich nur dem Fragesteller einen gangbaren und sicheren Weg
aufzeigen.
Hier, wenn Du noch mehr dazu lesen willst:
http://www.catb.org/esr/structure-packing/