GCC [STM32] -> wie sehen wie ein struct realisiert wird?

Gast #7285837
Lesenswert?

Hallo,

ich würde mich sehr dafür interessieren wie mein GCC ein von mir 
definiertes struct realisiert. Also insbesondere die Auswirkungen von 
'packed' und 'aligned' genauso wie eine potenzielle Nutzung von 
Bitfields
1
#include <stdint.h>
2

3
typedef struct __attribute__((packed)){
4
    uint8_t a;
5
    int32_t b;
6
    bool c;
7
    uint64_t d;
8
} ts_test;

oder gerne auch in Kombination mit Bitfields und einer Union:
1
#include <stdint.h>
2

3
typedef struct __attribute__((packed)){
4
    uint8_t a : 6;
5
    int32_t b : 20;
6
    bool c : 1;
7
    uint64_t d : 35;
8
} ts_test;
9

10
typedef union{
11
    ts_test obj;
12
    uint8_t can_data;
13
} tu_test;

Da wäre es für mich sehr interessant, wie der Compiler das genau 
umsetzt, sprich ob er wirklich (in Abhängigkeit des Optimize?) 
Stuffing-Bits einführt, etc.

*Ist jemandem eine Möglichkeit bekannt wo das irgendwie dargestellt 
ist?* Ich werde weder aus dem *.list noch aus dem *.map File schlau, 
geschweige denn würde ich solche Informationen dort finden.
Gast #7285860
Lesenswert?

Schreib dir ein Minimalprogramm mit Verwendung deiner Struktur
und lasse es im Debugger laufen. Dann beobachte die Struktur
mit dem Debugger, er wird dir auch Adressen zeigen (können).
Daraus kannst du dann die Anordnung, Speicherausnutzung und
eventuelle "Löcher" erkennen.
#7285861
Lesenswert?

Michael U schrieb:
> *Ist jemandem eine Möglichkeit bekannt wo das irgendwie dargestellt
> ist?* Ich werde weder aus dem *.list noch aus dem *.map File schlau,
> geschweige denn würde ich solche Informationen dort finden.

Datentypen sind in der Form im finalen Compilat nicht mehr vorhanden. Am 
einfachsten geht sowas, indem du dir die Offsets bzw. die Adressen der 
Elemente und ihre Größe als Debug-Ausgabe rausschreibst. Allerdings geht 
das für Bitfelder nicht.
Alternativ eine Instanz der Struktur als volatile anlegen und dann alle 
Elemente nacheinander beschreiben. Dann kann man im Assembler-Code 
nachschauen, wo er das hinschreibt.
Persönliche Seite #7285862
Lesenswert?

Michael U schrieb:
> Da wäre es für mich sehr interessant, wie der Compiler das genau
> umsetzt, sprich ob er wirklich (in Abhängigkeit des Optimize?)
> Stuffing-Bits einführt, etc.

Wenn das Layout von Datentypen von Optimierungs-Optionen abhängt, dann 
ist das ein Compiler-Bug.  Bzw. das Layout von Strukturen ist nicht von 
Optimierungs-Einstellungen abhängig.

Optionen wie -f[no-]pack-struct, -f[no-][un-]signed-char etc. sind 
keine Optimierungs-Optionen sondern Schalten zum Einstellen des ABI 
(Binary Interface).  Manchmal wird ein anderes ABI gewählt, weil das 
kleineren Code ermöglicht oder Speicher spart, ist aber ne andere 
Schublade als Optimierung.

Zur Frage: GCC-Schalter zur Ausgabe des Layouts (in welcher Form auch 
immer) kenne ich keine.
(Firma: egal) #7285922
Lesenswert?

Eventuell im Programm mal eine paar Prints einbauen die dir Sizeof und 
offsetoff ausgeben. Das lässt schonmal Rückschlüsse darüber zu wie viele 
Byte insgesammt verwendet werden und wo die einzelnen Variabeln 
beginnen.
Das schöne ist das diese Werte bereits vom Compiler erzeugt werden und 
somit im Assemblerlisting auffindbar sind (oder einfach laufen lassen).

- https://en.cppreference.com/w/cpp/language/sizeof
- https://en.cppreference.com/w/cpp/types/offsetof
Moderator Persönliche Seite #7285942
Lesenswert?

Von Bitfeldern mal abgesehen, die allgemeine Daumenregel sagt: schreib 
die größten Datentypen (also die mit dem größten Alignment) zuerst, von 
da kleiner werdend. Auf diese Weise braucht es innerhalb einer 
Struktur kein Padding, egal, wie die Anforderungen an die 
Speicherausrichtung der Zielarchitektur aussehen. Maximal wird Padding 
am Ende der Struktur vonnöten sein.

Also für deine erste Struktur:
1
typedef struct {
2
    uint64_t d;
3
    int32_t b;
4
    bool c;
5
    uint8_t a;
6
} ts_test;

(bool ist normalerweise ein 8-Bit-Typ, aber da das nicht immer so sein 
muss, noch oberhalb von uint8_t.)
Gast #7286085
Lesenswert?

Vielen Dank euch allen für euren Input. Ich werde mich mal mit einem 
Debugger  ransetzen und schauen, ob ich was sehen kann.

Danke auch für die Informationen zu ABI und offsetof() -> das kannte ich 
noch nicht.

Jörg W. schrieb:
> Von Bitfeldern mal abgesehen, die allgemeine Daumenregel sagt: schreib
> die größten Datentypen (also die mit dem größten Alignment) zuerst, von
> da kleiner werdend.

Leider bin ich da nicht so frei wie ich es gerne wäre. Das Layout den 
CAN Pakets (Listing korrigiert, siehe unten) ist gegeben. Ich suche 
gerade Möglichkeiten geschwindigkeitsoptimiert den Inhalt des CAN-Pakets 
in eine für die Applikation gut nutzbare (meschlich leserliche!) Form zu 
bringen.
1
#include <stdint.h>
2

3
typedef struct __attribute__((packed)){
4
    uint8_t Feuchtigkeit: 6;
5
    int32_t Temperatur: 20;
6
    bool QBit_Temperatur : 1;
7
    uint64_t Systemzeit_us : 35;
8
} ts_test;
9

10
typedef union{
11
    ts_test obj;
12
    uint8_t can_data[8];
13
} tu_test;

Ideen sind natürlich herzlich Willkommen!
#7286099
Lesenswert?

1
void hexdump(uint8_t buffer[], int size)
2
{
3
   int i;
4
   int line = 0;
5
   uint8_t *bp = buffer;
6

7
   while (bp < buffer + size) {
8
      uint8_t *lbp = bp;
9

10
      printf("%08x  ", line);
11

12
      for (i = 0; i < 16; i++) {
13
          uint8_t c = *lbp++;
14
         if (bp + i > buffer + size) {
15
            break;
16
         }
17
         printf("%02x ", c);
18
      }
19

20
      lbp = bp;
21
      for (i = 0; i < 16; i++) {
22
         int8_t c = *lbp++;
23

24
         if (bp + i > buffer + size) {
25
            break;
26
         }
27
         if (c > ' ' && c < '~') {
28
            printf("%c", c);
29
         } else {
30
            printf(".");
31
         }
32
      }
33
      printf("\r\n");
34

35
      bp += 16;
36
      line += 16;
37
   }
38
}
39

40
...
41

42
hexdump(tu, sizeof(tu));
Moderator Persönliche Seite #7286122
Lesenswert?

Michael U schrieb:
> Das Layout den CAN Pakets (Listing korrigiert, siehe unten) ist gegeben.

Der generische Weg ist es dann, die Felder einzeln aus einem uint8_t 
Array zu erzeugen / kopieren. "packed" verursacht misaligned access, 
nicht jede Architektur kann das (bei manchen gibt das einen Trap), und 
bei denen, die es können, führt es in der Regel zu 
Geschwindigkeitseinbußen.
#7286445
Lesenswert?

Michael U schrieb:
> Ich suche
> gerade Möglichkeiten geschwindigkeitsoptimiert den Inhalt des CAN-Pakets
> in eine für die Applikation gut nutzbare (meschlich leserliche!) Form zu
> bringen.

Wenn es geschwindigkeitsoptimiert sein soll (eigentlich nicht nur dann, 
sondern so ziemlich immer), würde ich die Struktur nicht als Bitfeld 
anlegen, sondern mit Variablen mit ausreichend großem Typ für das 
jeweilige Element, und dann eine Funktion, die die Daten aus den 
CAN-Rohbytes ausliest und da reinfüllt.
Abgesehen davon, dass das Layout von Bitfeldern implementation-defined 
ist, ist auch der Zugriff langsam, da für jeden Zugriff im Hintergrund 
das Bitgefummel durchgeführt werden muss. Daher lieber einmal von Hand 
bitfummeln und danach dann nur noch effizient zugreifen.
#7286838
Lesenswert?

Ich weiß, das hilft nicht wirklich, aber in Ada würde man das so machen 
(Representation Clause):
1
type a_type is mod 2 ** 6;
2
type b_type is range 0 .. 2 ** 20 - 1;
3
type c_type is range 0 .. 1;
4
type d_type is range 0 .. 2 ** 35 - 1;
5
type Byte is range 0 .. 255;
6
...
7
type ts_test is record
8
   a    : a_type;
9
   b    : b_type;
10
   c    : c_type;
11
   d    : d_type;
12
end record;
13

14
for ts_test use record
15
   a at 0 range 0 .. 5;
16
   b at 1 range 0 .. 19;
17
   c at 4 range 0 .. 1;
18
   d at 5 range 0 .. 34;
19
end record;
Man kann also für jedes Feld im Record angeben, bei welchem Byte 
(System.Storage_Unit) an welcher Bitposition es starten bzw. enden soll 
(kann man auch über Atribute zur Compile-Zeit abfragen). Ada kennt die 
Länge eines Maschinenwortes (System.Word_Size), so dass man auch das 
Alignment flexibel anpassen kann.
Die Feldtypen können als Modulare oder als Range-Types (wrap-around oder 
Laufzeitfehler bzw. Exception bei Überlauf) festgelegt werden.
#7287260
Lesenswert?

Da es um CAN Frames geht ein paar Gedanken am Rande:
1. Die Bytereihenfolge (Endiness) kann sich je nach Controller beim 
lesen/schreiben vom/in den CAN-Controller rum drehen.
-> Und evtl so nicht mir mit der Bytereihenfolge des eigentlichen 
Controllers zusammenpassen.
2. Die Daten in der CAN Message unterliegen aus Architektursicht einem 
"Übertragungsprotokoll". Auch wenn es oftmals ein einfaches Protokoll 
ist, welches nur die Lage und Reihenfolge der Bytes definiert.
-> das Handling der rohen CAN-Bytes und der Nutzdaten sollte durch eine 
"Implenentierung eine Protokolls" umgesetzt werden. Damit wären die 
Daten oberhalb des Protokolls auch unabhängig von CAN und evtl 
Bit/Byte-allignment.
3. Normaler Weise wird bei CAN nicht Bit und Byte-Übertragung gemischt.
-> ein Byte sollte immer als kleinste geschlossene Übertragungseinheit 
angesehen werden (es kann vielleicht mehrere Bit große Zustände 
beinhalten). Nachfolgende "Zahlen" beginnen dann im neuen Byte.

Zum Erkennen der Byte-Order wurde ja schon geschrieben, dass das mit dem 
Debugger am schnellst geht.

Da ich das selber vor kurzen für den bxCAN Controller in den STM32F4 
umgesetzt habe, kann ich dein Suchen gerade nach vollziehen 🙈
Gast #7291757
Lesenswert?

Auch in ADA sollte man das nicht machen.
Genau dieses Pattern (für mich eher ein Antipattern) hat mich schon 
Mannmonate beschäftigt, das ganze funktioniert nämlich plötzlich 
nichtmehr, wenn man den selben Code auf Big- und Little-Endian-Maschinen 
laufen lassen möchte.
#7291852
Lesenswert?

Bauteiltöter schrieb:
> Auch in ADA sollte man das nicht machen.
> Genau dieses Pattern (für mich eher ein Antipattern) hat mich schon
> Mannmonate beschäftigt, das ganze funktioniert nämlich plötzlich
> nichtmehr, wenn man den selben Code auf Big- und Little-Endian-Maschinen
> laufen lassen möchte.

Das funktioniert auch dann (wenn man's richtig macht): 
https://www.adacore.com/gems/gem-140-bridging-the-endianness-gap

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren