Hallo zusammen, ich habe in einer Software für einen ARM Cortex M0 ein "static const struct" angelegt. Über das Struct wird die Speicheraufteilung in einem EEPROM gemacht. Wenn ich das gleiche Struct jetzt in einem Programm für einen x86 PC verwende und mit dem gcc compiliere, ist das Struct 1 Byte größer, als mit dem ARM-compiler. Also sizeof(struct) gibt mir auf der x86 Platform ein byte mehr aus, als auf der ARM Platform. Ich habe mir schon die Größe jedes einzelne Element des structs ausgeben lassen mit sizeof(struct.element) und verglichen. Die Elemente sind alle gleich groß. Kann mir jemand sagen, wo der Unterschied herkommt? Das macht mir echt Probleme... Gruß, Bean
Liegt wohl an unterschiedlichen Alignment/Padding-Regeln von Einzelbytes. Nimm doch mal offsetof(), dann solltest du sehen, nach welchem Element es auf einmal anders ist. Notfalls kann man mit dem packed-Attribut das auch aufs Minimum zusammenquetschen. Der Zugriff kann dann aber sehr ineffizent werden.
Stichwort alignment. Kann mit compilerspezifischen Anweisungen umgangen werden, #pragma pack oder attribute(packed) o.ä., hat aber Auswirkungen auf die Performance. Ansonsten musst Du auch die endianness berücksichtigen, wenn Du Datenstrukturen zwischen verschiedenen Prozessorarchitekturen austauschen willst.
Ich hab den compiller bei mir immer so eingestellt das alle Warnungen aktiviert sind, damit auch das auffällt. -Wpadded Warn if padding is included in a structure, either to align an element of the structure or to align the whole structure. Sometimes when this happens it is possible to rearrange the fields of the structure to reduce the padding and so make the structure smaller. Wenn du mal die Strukturdefinition possted können wir dir Tipps geben wie man die eventuell besser machen kann. Auf 32Bit wird der Speicher immer in DWORD (4Bytes) adressiert, daher fügt der Compiler Padding-Bytes ein die dann ungenutzt sind, damit ein int immer an einer 4-Byte Speichergrenze anfängt. Es hilft uint8_t oder uint16_t zu verwenden, wenn man weiß das die werte nicht größer sin können.
Mich wundert allerdings, das es auf dem x86 grösser ist. Ich hätte es eher andersrum erwartet...
Matthias L. schrieb: > Mich wundert allerdings, das es auf dem x86 grösser ist. Ich hätte es > eher andersrum erwartet... Warum?
>Warum? Nach meiner bisherigen Erfahrung hat der x86 ein 1-Byte Alignment. ALso keinerlei Füllbytes. Alles einfach an die nächste Adresse Beim ARM gilt, die Adresse muss ganzzahlig durch die grösse des Datums teilbar sein...
Matthias L. schrieb: > Nach meiner bisherigen Erfahrung hat der x86 ein 1-Byte Alignment. Ja, x86 kann misaligned, sofern nicht abgeschaltet. > ALso keinerlei Füllbytes. Alles einfach an die nächste Adresse Könnte man zwar so machen, wäre aber z.T. recht langsam, da etliche CPUs der letzten >20 Jahre das arg übel nehmen. Tatsächlich wird deshalb entsprechend dem Datentyp aligned. > Beim ARM gilt, die Adresse muss ganzzahlig durch die grösse des Datums > teilbar sein... Hängt vom Typ ab. Alte ARMs und Cortex M0 ja. Cortex M3 kann misligned bei Load/Store, wenn ich mich recht erinnere.
Ich kenns hier nur von den PC-SPSen. Alle x86er haben 1Byte Alignemt, wärend ARMs so anlegen, wie beschrieben...
wenn beim Cortex das int16 nit bei einer geraden Addresse liegt bzw. int32 nicht auf einer durch 4 teilbaren Addresse, wird das mit zusätzlichen Operationen erkauft. Ist aber prinzipiell möglich. Deshalb ist default beim GCC Compiler der effizientere Zugriff statt Memory-sparsamere Mode. Ich kann nur empfehlen, wenn du im Memory die Struktur abbilden willst, das Padding im Code explizit auszuschalten:
1 | struct blank_struct_t |
2 | {
|
3 | uint8_t eins; |
4 | uint16_t zwei; |
5 | uint8_t drei; |
6 | } __attribute__((__packed__)); |
weil ja sonst durch Compilerschalter das Verhalten beeinflusst werden würde. Grüße, Adib. --
Alle mir bekannten C Compiler für x86 alignen entsprechend dem Datentyp. Details siehe Handbuch vom Compiler. Programmiert man eine SPS in C?
>Programmiert man eine SPS in C?
ST (strukturierter Text), ähnlich Pascal.
Im Hintergrund läuft WinCE und ein RealTime Treiber. Keine Ahnung, was
da getrieben wird.
lässt man sich die Adressen der Member einer Struktur geben, so erhalte
ich obiges Bild.
Dann sind Erfahrungen mit SPS auch nicht relevant für die Einschätzung von C. Zumal ich eine SPS nicht mit Wettrennen um die Performance-Krone assoziiere. Da gibts andere Ziele.
Natürlich nicht. Mich würde es nur wundern, wenn sich die Compiler auf dem selben Zielsystem unterscheidlich verhalten...
Matthias L. schrieb: > Natürlich nicht. Mich würde es nur wundern, wenn sich die Compiler auf > dem selben Zielsystem unterscheidlich verhalten... Unterschiedliche Ziele. Auf einem x86 kann ein einzelner misaligned Zugriff auch mal zig Takte Laufzeit kosten. Es hängt aber sehr vom konkreten Code und dem jeweiligen Core ab, wie das ausfällt. Das kann sich kein C Compiler als Standardeinstellung erlauben. Der technische Hintergrund ist hier beschrieben: http://de.slideshare.net/RamdasMozhikunnath/load-storeexecution
Gutem Morgen! Vielen Dank für eure Antworten! Soetwas habe ich mir schon gedacht. Allerdings arbeite ich bereits mit dem __attribute__((_packed_)). Hier meine Typdefinition:
1 | typedef struct |
2 | {
|
3 | uint32_t _operating_running_time[MAXIMUM_CHANNELS]; ///<accumulated hours of closed relay for each channel |
4 | uint32_t _service_time[MAXIMUM_CHANNELS]; ///<the service time (in hour) for each channel |
5 | uint8_t _ort_channel_select; ///<bit map for selected channel for hour and service counter. bitx = 1: channel x is selected |
6 | FirstDayOfWeek _fdow; ///< configured first work day (uint8_t) @ref FirstDayOfWeek |
7 | TimeFormat _tf; ///< configured time format (uint8_t) @ref TimeFormat |
8 | WorkingMode _working_mode; ///< configured working mode (uint8_t) @ref WorkingMode |
9 | Country_t _country; ///< configured country for astro (uint8_t) @ref Country_t |
10 | City_t _locations; ///< configured locations (cities) for astro (uint16_t) @ref City_t |
11 | int16_t _longitude; ///< configured longitude |
12 | int16_t _latitude; ///< configured latitude |
13 | int16_t _time_zone; ///< configured time zone in minute |
14 | DaylightSaving _dst; ///< configured day light (uint8_t) @ref DaylightSaving |
15 | uint8_t _ws_month; ///< configured month for end of winter time |
16 | uint8_t _ws_day; ///< configured day for end of winter time |
17 | uint8_t _sw_month; ///< configured month for end of sommer time |
18 | uint8_t _sw_day; ///< configured day for end of sommer time |
19 | Language _lang; ///< configured language (uint8_t) @ref Language |
20 | uint8_t _selection_flags; ///< configured Configuration |
21 | BacklightMode _blmode; ///< configured back light mode (uint8_t) @ref BacklightMode |
22 | uint16_t _pin_code; ///< configured default pin code |
23 | networkInfo_t networkInfo; ///< configured channel info @ref networkInfo_t |
24 | uint8_t _deviceName[BLE_DEVICE_MAX_DEVICENAME_LEN]; |
25 | } __attribute__((__packed__)) Config; |
Und hier, die Zuweisung:
1 | static const Config config_default = { |
2 | ._operating_running_time = {0LU}, // operating running time |
3 | ._service_time = {0LU}, // service time |
4 | ._ort_channel_select = 0x03, // ORT channel select |
5 | ._fdow = FDOW_Monday, // first day of the week |
6 | ._tf = TF_24H, // time format |
7 | ._working_mode = ModeAdvance, // working mode |
8 | ._country = country_Germany, // astro selected country |
9 | ._locations = Germany_city7, // astro selected locations (city of Freiburg) |
10 | ._longitude = DEFAULT_LONGITUDE, // 8 is the default longitude (Freiburg) |
11 | ._latitude = DEFAULT_LATITUDE, // 48 is the default latitude (Freiburg) |
12 | ._time_zone = DEFAULT_TIMEZONE, // GMT+1 is the default timezone |
13 | ._dst = DST_EST, // daylight saving |
14 | ._ws_month = 3, // Calibrated winter->summer transition month |
15 | ._ws_day = 27, // Calibrated winter->summer transition day |
16 | ._sw_month = 10, // Calibrated summer->winter transition month |
17 | ._sw_day = 30, // Calibrated summer->winter transition day |
18 | ._lang = Lang_English, // Default language |
19 | ._selection_flags = SLT_LANGUAGE_SELECTED | // language and |
20 | SLT_LOCATION_SELECTED | // location have been selected |
21 | SLT_VARIANCE_5, // |
22 | ._blmode = BL_Timed, // backlight mode |
23 | ._pin_code = DEFAULT_PIN_CODE, // 0 |
24 | .networkInfo = { |
25 | .device = { {Channel_Disabled, 0} }, |
26 | .lastReference = 0, |
27 | .commissioningAllowed = 0 |
28 | }, // NetworkInfo structure |
29 | ._deviceName = DEFAULT_DEVICE_NAME // BLE device Name |
30 | };
|
Wie gesagt, habe immernoch das Problem, dass sizeof(config_default) 1Byte größer ausgibt, als auf der Zielarchitektur. Die einzelnen Elemente aber wohl alle gleich groß sind. Gibt es noch irgendwelche Compiler-Flags, die ich probieren kann? (--short-enums habe ich schon an) Gruß und vielen Dank für eure Hilfe, Bean
Deine struct hilft nicht viel, wenn man die einzelnen Typen nicht kennt, die darin vorkommen. Vielleicht ist das alles ja auch nur Fehlalarm und das fehlende Byte wird ganz am Ende nach dem uint8_t dazugedichtet, damit man in einem Array aufeinanderfolgende Elemente so eines structs ablegen kann. Wie bereits gesagt: gib dir halt die Offsets aus, dann muß man nicht spekulieren.
Hallo Klaus, Ok, ich habe mir die Offsets jetzt ausgeben lassen. Und ich kann erkennen, dass sich ab dem Eintrag _locations, der Offset um 1Byte verschiebt. Also ist wohl das Element vor _locations, ein byte größer geworden. Was fange ich mit dieser Information jetzt an? Wie bekomme ich es wieder kleiner? ;-) Gruß, Bean
Unbekannte Attribute werden ignoriert. Probiers mal mit __attribute__((packed))
Kannst du nicht die Member nach ihrer Groesse absteigend sortieren? Dein Country liegt auf einer ungeraden Adresse wenn ich mich nicht verzaehlt habe.
Hallo, ja das habe ich schon probiert. Bei Country_t (dem Typ vor locations), handelt es sich um einen typdef enum. Kann ich das attribut auch darauf anwenden? Gruß, Bean
nur mal kurz überflogen, und anhand der Kommentare die Bytegröße genommen
1 | typedef struct |
2 | {
|
3 | uint32_t _operating_running_time[MAXIMUM_CHANNELS]; ///<accumulated hours of closed relay for each channel |
4 | |
5 | uint32_t _service_time[MAXIMUM_CHANNELS]; ///<the service time (in hour) for each channel |
6 | |
7 | uint8_t _ort_channel_select; ///<bit map for selected channel for hour and service counter. bitx = 1: channel x is selected |
8 | FirstDayOfWeek _fdow; ///< configured first work day (uint8_t) @ref FirstDayOfWeek |
9 | TimeFormat _tf; ///< configured time format (uint8_t) @ref TimeFormat |
10 | WorkingMode _working_mode; ///< configured working mode (uint8_t) @ref WorkingMode |
11 | |
12 | Country_t _country; ///< configured country for astro (uint8_t) @ref Country_t |
13 | City_t _locations; ///< configured locations (cities) for astro (uint16_t) @ref City_t |
14 | /*1 Byte padding*/
|
15 | |
16 | int16_t _longitude; ///< configured longitude |
17 | int16_t _latitude; ///< configured latitude |
18 | |
19 | int16_t _time_zone; ///< configured time zone in minute |
20 | DaylightSaving _dst; ///< configured day light (uint8_t) @ref DaylightSaving |
21 | uint8_t _ws_month; ///< configured month for end of winter time |
22 | |
23 | uint8_t _ws_day; ///< configured day for end of winter time |
24 | uint8_t _sw_month; ///< configured month for end of sommer time |
25 | uint8_t _sw_day; ///< configured day for end of sommer time |
26 | Language _lang; ///< configured language (uint8_t) @ref Language |
27 | |
28 | uint8_t _selection_flags; ///< configured Configuration |
29 | BacklightMode _blmode; ///< configured back light mode (uint8_t) @ref BacklightMode |
30 | uint16_t _pin_code; ///< configured default pin code |
31 | |
32 | networkInfo_t networkInfo; ///< configured channel info @ref networkInfo_t |
33 | uint8_t _deviceName[BLE_DEVICE_MAX_DEVICENAME_LEN]; |
34 | } __attribute__((__packed__)) Config; |
es wird zwischen _Locations und _longitude ein Byte Padding eingefügt Größe von networkInfo_t fehlt Mit -Wpadded zeigt dir der Compiler aber auch die Stelle an
Hm, umsortieren möchte ich nur ungern, dann muss ich die Datenstruktur ja auch auf der Zielplatform ändern... Gibt es keine andere Möglichkeit, dieses Problem zu lösen? Gruß, Bean
Hallo, -Wpadded habe ich eingeschaltet. wenn ich __attribute__((_packed_)) weglasse, bekomme ich auch eine Warnung, "warning: padding struct size to alignment boundary [-Wpadded]". Da ich das __attribute__((_packed_)) zu dem typedef hinzugefügt habe, verschwindet die Warnung. Trotzdem kommt irgendwie ein Byte hinzu. Gruß, Bean
Mr Bean schrieb: > Hm, umsortieren möchte ich nur ungern, dann muss ich die Datenstruktur > ja auch auf der Zielplatform ändern... > Gibt es keine andere Möglichkeit, dieses Problem zu lösen? Wenn es dir nur darum geht, es auf mehreren Architekturen gleich zu bekommen, kannst du das Padding auch manuell machen, indem du Dummyelemente einfügst, sodaß alle größeren Alemente auf glatte Adressen kommen. So oder so ist es aber nicht wirklich portabel, sich auf die Auslegung einer struct zu verlassen.
Klaus W. schrieb: > Vielleicht ist das alles ja auch nur Fehlalarm und das fehlende Byte > wird ganz am Ende nach dem uint8_t dazugedichtet, damit man in einem > Array aufeinanderfolgende Elemente so eines structs ablegen kann. Mr Bean schrieb: > Trotzdem kommt irgendwie ein Byte hinzu. Es geht mir immer noch um dein einsames Byte am Ende: Wenn ich den folgenden Quelltext auf mit gcc auf einem i386 kompiliere
1 | struct st |
2 | {
|
3 | int i; |
4 | char c; |
5 | };
|
6 | |
7 | std::printf( "%u\n", (unsigned)sizeof(struct st) ); |
, dann bekomme ich 8 als Größe raus. Das heißt, es wird auch am Ende noch gepadded. Ist auch logisch, weil man ja mit z.B. malloc(5*sizeof(struct st)) ein Feld allokieren kann, bei dem auch im zweiten oder dritten Feldelement dessen jeweils erstes struct-Element auf einer für int geeigneten Adresse liegen muss. D.h. sobald in einer struct etwas größeres als nur Bytes liegt, wird am Ende auch auf glatte Adressen aufgefüllt (/2 oder /4 teilbar, je nach System).
@ TO: Ist es denn wichtig, dass die Strukturen auf dem Target und auf x86 das gleiche Layout haben? Willst du irgendwie raw-Daten austauschen und bist darauf angewiesen dass das Layout passt? Ansonsten würde ich das Padding einfach in Kauf nehmen und das "packed" weglassen. Ich organisiere meinen EEPROM auch als Struktur und greife per Makro darauf zu. Das Makro liefert mir den Offset eines Members und der wiederum dient als Adresse für mein Eep_WriteByte(...). Wenn sich das Layout also ändert dann spielt das keine Rolle. Allerdings sind Datensätze so nicht zwischen verschiedenen Firmwareversionen kompatibel, weswegen meine Software eine Standard-Bedatung mitbringt die nach dem Flashen erstmal zum Einsatz kommt.
Mr Bean schrieb: > Bei Country_t (dem Typ vor locations), handelt es sich um einen > typdef enum. Die Größe eines Enum-Typs ist extrem Compiler-abhängig. Du solltest für das Speichern im EEPROM besser ein Typ mit bekannter Größe verwenden (uint8_t, oder was halt benötigt wird).
> Hm, umsortieren möchte ich nur ungern, dann muss ich die Datenstruktur > ja auch auf der Zielplatform ändern... > Gibt es keine andere Möglichkeit, dieses Problem zu lösen? Machst Du etwa memcpy der Struktur in den Sendepuffer einer Netzwerkverbindung? (Schreiend wegrenn...)
Also: Vielen Dank erstmal an alle für eure Hilfe. Ich habe das Problem jetzt so gelöst, dass ich "filler" bytes in die Struktur einbaue, um das alignment Problem zu lösen. Die Struktur muss auf arch x86 gleich sein, wie auf der Zielhardware, da ich mit dem PC Programm ein EEPROM image erstelle, das ich dann wieder in die Zielhardware einspiele. Wenn dann die Adressen nicht passen, habe ich Datensalat auf meiner Zielhardware. Und das Erstellen des image, ist auf einem x86 einfach wesentlich schneller, als auf der Zielhardware. Außerdem kann ich den Inhalt so direkt in eine .bin Datei schreiben (auf der Zielhardware nicht möglich). Gruß, Bean
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.