Forum: Mikrocontroller und Digitale Elektronik [STM32] Hardfaults beim benutzen von Bitfeldern vermeiden


von Michael S. (Gast)


Lesenswert?

Hallo!

Ich beschäftige mich gerade mit dem STM32F0DISCOVERY Board und möchte 
den STM32F0 aus Spaß möglichst nur nach Datenblatt programmieren. Dabei 
bin ich auf ein kurioses Problem gestoßen, dessen Lösung ich hier nun 
poste, falls jemand anderes ebenfalls darüber stolpert.

Ich wollte im FLASH_ACR Register den Prefetch Buffer einschalten. Und da 
ich ein großer Fan von Bitfeldern bin, tat ich folgendes:
1
  struct __attribute__((__packed__)) flash_acr
2
  {
3
    unsigned latency : 3;
4
    unsigned : 1;
5
    unsigned prftbe : 1;
6
    unsigned prftbs : 1;
7
  };
8
  volatile flash_acr &acr = *((volatile flash_acr*)0x40022000);
9
  acr.prftbe = 1;

Dies wird in die folgenden Instruktionen übersetzt:
1
 8000448:  2110        movs  r1, #16
2
 800044a:  4a27        ldr  r2, [pc, #156]
3
 800044c:  7813        ldrb  r3, [r2, #0]
4
 800044e:  430b        orrs  r3, r1
5
 8000450:  7013        strb  r3, [r2, #0]

Bei der ldrb Instruktion springt er allerdings in den Hardfault 
Interrupt.

Die Adresse ist auf jeden Fall richtig, denn wenn ich das ganze ohne 
Bitfelder mache geht es:
1
  volatile unsigned &flash_acr = *((volatile unsigned*)0x40022000);
2
  flash_acr |= 1<<4;

Dabei werden die folgenden Instruktionen erzeugt:
1
 8000448:  2111        movs  r1, #16
2
 800044a:  4a27        ldr  r2, [pc, #156]
3
 800044c:  6813        ldr  r3, [r2, #0]
4
 800044e:  430b        orrs  r3, r1
5
 8000450:  6013        str  r3, [r2, #0]

Der Unterschied ist hierbei, dass statt eines einzelnen Bytes das ganze 
32 Bit Word gelesen wird. Scheinbar ist byteweises Lesen hier nicht 
erlaubt.

Daher muss man dem Compiler irgendwie mitteilen, dass er auch bei der 
Bitfeld Methode ganze Wörter lesen und schreiben soll. Das geht indem 
man das Bitfeld bis zur Wortgrenze auffüllt:
1
  struct __attribute__((__packed__)) flash_acr
2
  {
3
    unsigned latency : 3;
4
    unsigned : 1;
5
    unsigned prftbe : 1;
6
    unsigned prftbs : 1;
7
    unsigned : 26;
8
  };
9
  volatile flash_acr &acr = *((volatile flash_acr*)0x40022000);
10
  acr.prftbe = 1;

Damit werden die selben Instruktionen erzeugt wie in der zweiten 
Methode.

Ich hoffe das hilft irgendjemandem von euch!

Cheers,
Michael

von Vergessen (Gast)


Lesenswert?

Wäre die Nutzung von Bit Banding (soweit in der jeweiligen 
Controllerversion verfügbar) nicht die bessere Alternative?  Wenn es 
bezüglich der zu nutzenden Datenbreite Einschränkungen gibt steht das 
eigentlich in der Doku.

von (º°)·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.· (Gast)


Lesenswert?

The Flash memory registers have to be accessed by 32-bit words
(half-word and byte accesses are not allowed).

Wer Lesen kann ist klar im Vorteil.

von Uwe B. (Firma: TU Darmstadt) (uwebonnes)


Lesenswert?

Vergessen schrieb:
> Wäre die Nutzung von Bit Banding (soweit in der jeweiligen
> Controllerversion verfügbar) nicht die bessere Alternative?  Wenn es
> bezüglich der zu nutzenden Datenbreite Einschränkungen gibt steht das
> eigentlich in der Doku.

M0, M0+ und M7 haben fast nie Bitbanding...

von Lothar (Gast)


Lesenswert?

LPC810 M0+ hat sogar Bit-addressierbaren Speicher - wie ein 8051

Bit-Banding ist auf dem Bus keine atomare Operation

von Gerd E. (robberknight)


Lesenswert?

Michael S. schrieb:
> Daher muss man dem Compiler irgendwie mitteilen, dass er auch bei der
> Bitfeld Methode ganze Wörter lesen und schreiben soll. Das geht indem
> man das Bitfeld bis zur Wortgrenze auffüllt:

ist es nicht eigentlich eher Aufgabe des Compilers zu wissen daß man bei 
diesem konkreten µC-Kern nur auf ganze Wörter/Wortadressen zugreifen 
kann?

Was für nen Compiler verwendest Du? Version?

Mit was für Optionen sagst Du dem mit was für nem µC er es zu tun hat?

von A. S. (Gast)


Lesenswert?

Gerd E. schrieb:
> ist es nicht eigentlich eher Aufgabe des Compilers zu wissen daß man bei
> diesem konkreten µC-Kern nur auf ganze Wörter/Wortadressen zugreifen
> kann?

Nein. Ein Compiler sollte nicht jedes Derivat kennen müssen, da sonst 
für einen neuen oder seltenen Typen immer ein Update notwendig ist. Wenn 
dann sollte es m.E. Schlüsselworte (oder #pragmas) dafür geben, für die 
man selber verantwortlich ist.

von (prx) A. K. (prx)


Lesenswert?

Lothar schrieb:
> LPC810 M0+ hat sogar Bit-addressierbaren Speicher - wie ein 8051
> Bit-Banding ist auf dem Bus keine atomare Operation

RAM-Bitbanding gibts m.E. überall dort, wo es IO-Bitbanding gibt, weil 
das eine Architektureigenschaft des verwendeten ARM Kerns ist (wobei bei 
manchen Kernen Bitbanding insgesamt optional ist).

: Bearbeitet durch User
von eagle user (Gast)


Lesenswert?

Achim S. schrieb:
> Gerd E. schrieb:
>> ist es nicht eigentlich eher Aufgabe des Compilers zu wissen daß man bei
>> diesem konkreten µC-Kern nur auf ganze Wörter/Wortadressen zugreifen
>> kann?
>
> Nein. Ein Compiler sollte nicht jedes Derivat kennen müssen, da sonst
> für einen neuen oder seltenen Typen immer ein Update notwendig ist. Wenn
> dann sollte es m.E. Schlüsselworte (oder #pragmas) dafür geben, für die
> man selber verantwortlich ist.

Ist dafür nicht die Option -fstrict-volatile-bitfields zuständig? Damit 
sollte der Compiler so auf die Hardware zugreifen, wie die struct 
definiert ist. In diesem Fall mit uint32_t-Feldern, woanders vielleicht 
mit zwei uint8_t und einem uint16_t.

Die Hardware-Register erlauben ja total unterschiedliche Zugriffe und 
optimieren möchte man ja auch. Auf ein 8 Bit breites Bitfeld möchte man 
doch Byte-weise zugreifen -- falls es die Hardware erlaubt. Zum 
Beispiel: Timer im STM32F205:
1
The 32-bit peripheral registers have to be written by words
2
(32 bits). All other peripheral registers have to be written by
3
half-words (16 bits) or words (32 bits). Read accesses can be done
4
by bytes (8 bits), half-words (16 bits) or words (32 bits).

von Vincent H. (vinci)


Lesenswert?

eagle user schrieb:
> Ist dafür nicht die Option -fstrict-volatile-bitfields zuständig? Damit
> sollte der Compiler so auf die Hardware zugreifen, wie die struct
> definiert ist. In diesem Fall mit uint32_t-Feldern, woanders vielleicht
> mit zwei uint8_t und einem uint16_t.
>
> Die Hardware-Register erlauben ja total unterschiedliche Zugriffe und
> optimieren möchte man ja auch. Auf ein 8 Bit breites Bitfeld möchte man
> doch Byte-weise zugreifen -- falls es die Hardware erlaubt. Zum
> Beispiel: Timer im STM32F205:
>
1
The 32-bit peripheral registers have to be written by words
2
> (32 bits). All other peripheral registers have to be written by
3
> half-words (16 bits) or words (32 bits). Read accesses can be done
4
> by bytes (8 bits), half-words (16 bits) or words (32 bits).


Nach 8 Antworten endlich einmal eine sinnvolle!

Vorerst sollte man aber noch erwähnen, dass ganz am Anfang von einem M0 
core die Rede war. Bei einem M0 spielt es überhaupt keine Rolle in 
welcher Form auf welches Peripherie-Register zugegriffen werden "darf", 
weil sowieso jeder unaligned access in einem hardfault endet.

Ein STM32F205 ist jedoch ein M3 core, unaligned access wäre somit 
theoretisch eine Option. Praktisch ist es meines Wissens jedoch so, dass 
ST selbst in ihren Bibliotheken ausschließlich *volatile uint32_t 
Zugriffe macht. Das hat wohl zweierlei Gründe. Erstens könnte man 
unaligned access ja in Software deaktivieren und zweitens wäre es wohl 
die reinste Wartungshölle, parallel aligned- und unaligned Bibliotheken 
nachzuziehen.

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.