Forum: Compiler & IDEs STM32WB: Mit CubeMX generierter Code wirft HardFault


von Rem V. (veryxrv)


Lesenswert?

Hallo zusammen,

ich habe mir ein STM Nucleo Board für den STM32WB Microcontroller 
besorgt und möchte per CubeMX zunächst ein blankes Projekt in CubeIDE 
erstellen, auf den Controller laden und ausführen. Leider bekomme ich 
selbst mit dem unveränderten, in CubeMX speziell für das Board 
generierten Code einen HardFault.

Ich muss dazu sagen, dass ich aus der PC-Software Ecke stamme und bei 
eingebetteten Systemen ziemlicher Einsteiger bin.

Folgendes habe ich versucht:

1. CubeIDE starten und neues Projekt erstellen
2. Bei der Projekterstellung des Board auswählen (Nucleo WB55)
3. An der Konfig in CubeMX nichts verändern
4. Code aus CubeMX heraus generieren
5. Debuggen

Sobald in startup_stm32wb55xx_cm4.s die Funktion "SystemInit" aufgerufen 
wird, tritt ein HardFault auf. Der Fault Analyzer zeigt einen 
IMPRECISERR und STKERR an, PC zeigt jedoch auf 0x0.

Wenn ich im Einzelschritt debugge, sehe ich, dass nach der Anweisung zum 
Aufruf von "SystemInit" folgendes stattfindet, bevor der HardFault 
getriggert wird:

 87         bl  SystemInit
0800270a:   bl      0x80024f0 <SystemInit>
080024f0:   push    {r7}
080024f2:   add     r7, sp, #0               <<<<<<<<<< HardFault
199         SCB->VTOR = VECT_TAB_OFFSET;

Das passiert übrigens bei jedem beliebigen Funktionsaufruf aus dem 
Startup heraus, sobald das "add" ausgeführt wird.

Hat jemand eine Ahnung, warum das so ist? Im Prinzip nutze ich ja ein 
Blanko-Projekt, was speziell für dieses Board vorkonfiguriert ist.

Besten Dank im Voraus!

von ... (Gast)


Lesenswert?

Das ist ja schoen das das so zeitig passiert.
Damit hast du Gelegenheit dich intensiv mit dem Controller
jenseits von HAL zu beschaeftigen.

Lesestoff zu dem Thema 'Hardfault' gibt es u.a. bei TI:
SPMA043.PDF Diagnosing Software Faults...

von Nop (Gast)


Lesenswert?

Da ist offenbar der Stack nicht richtig eingestellt. Schau Dir mal im 
resultierenden Hexfile die ersten vier Bytes der ersten Datenzeile an, 
da muß der Stackpointer bei Reset drinstehen.

von Rem V. (veryxrv)


Lesenswert?

Vielen Dank für die Anregungen!

Prinzipiell stimme ich zu, dass das eine gute Übung für den Einstieg 
ist. Leider sitze ich aber schon eine ganze Weile davor und komme nicht 
weiter. Das Dokument von TI ist zwar hilfreich, allerdings sind bei mir 
alle ansonsten aufschlussreichen register einfach 0.

Der Stackpointer wird mit 0x20040000 initialisiert und zwar mit der 
Anweisung

  ldr   r0, =_estack
  mov   sp, r0          /* set stack pointer */

im Startup (Reset Handler).

Ich habe versucht, herauszufinden, ob das richtig ist. Mir scheint es 
aber so zu sein, dass der Wert im Controller fest hinterlegt ist. Stimmt 
das?

EDIT: Hab gefunden, wo es definiert ist. Laut Kommentar sollte es das 
Ende des RAM sein. Versuche gerade herauszufinden, ob es das auch ist.

EDIT2: Laut Referenz Endet der external RAM an Adresse 0x9FFFFFFF. 
Verstehe ich das richtig? Habe den Wert nun im Linker Script für den RAM 
auf diese Adresse angepasst und nun tritt der HardFault an anderer 
Stelle auf:

Im Startup wird die Funktion "__libc_init_array" aufgerufen. Diese 
wiederum ruft "_init" auf. Im Disassembly von "_init" ist folgender Code 
zu sehen:

0800278c:   push    {r3, r4, r5, r6, r7, lr}
0800278e:   nop
08002790:   pop     {r3, r4, r5, r6, r7}
08002792:   pop     {r3}
08002794:   mov     lr, r3
08002796:   bx      lr                        <<<<< lr = 0x0

lr hat zunächst die richtige Rücksprungadresse, wird dann aber mit r3 = 
0x0 überschrieben (warum?). Mit lr = 0x0 wird dann versucht, an 0x0 zu 
springen, was dann wieder zum HardFault führt.

: Bearbeitet durch User
von Rem V. (veryxrv)


Lesenswert?

Ich denke, ich habe das Problem gelöst:

Der SRAM des Controllers ist 256 KB groß und beginnt bei 0x20000000, 
geht also nur bis 0x20010000. Per Default eingetragen ist aber 
0x20040000, was offenbar zu dem Fehler führt. Ich habe den Wert im 
Linker Script entsprechend auf 0x20010000 angepasst und nun funktioniert 
alles.

Nach wie vor bin ich etwas überrascht, dass die Standardeinstellungen so 
gar nicht zum Board zu passen scheinen...

EDIT: Gerade noch mal nachgerechnet. Was ich oben geschrieben habe passt 
natürlich nicht, 0x20000000 + 256 KB = 0x20040000 ist schon richtig 
gewesen. Dennoch seltsam, warum die Änderung das Problem scheinbar 
behoben hat. Kann das jemand erklären?

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Rem V. schrieb:

> Der Stackpointer wird mit 0x20040000 initialisiert und zwar mit der
> Anweisung
>
>   ldr   r0, =_estack
>   mov   sp, r0          /* set stack pointer */
>
> im Startup (Reset Handler).

Das sollte eigentlich bereits im Hexfile richtig gesetzt sein, manuell 
ist das schon eher ungewöhnlich.

> EDIT2: Laut Referenz Endet der external RAM an Adresse 0x9FFFFFFF.

Das dürfte der Adreßbereich sein, den externes RAM haben kann, sofern 
bestückt. Aber den Stack packt man normalerweise nicht ins externe RAM, 
sondern ins interne. Erstens ist das performanter, und zweitens müßte 
man im externen Fall die Chip-Konfig auch noch in Assembler machen, weil 
C-Funktionen einen bereits funktionierenden Stack erwarten.

> 0800278c:   push    {r3, r4, r5, r6, r7, lr}
> 0800278e:   nop
> 08002790:   pop     {r3, r4, r5, r6, r7}
> 08002792:   pop     {r3}
> 08002794:   mov     lr, r3
> 08002796:   bx      lr                        <<<<< lr = 0x0

Also da werden doch eigentlich nur alle Register einmal gepusht, dann 
gepopt, und am Ende wird das, was vorher als LR gepusht worden ist, in 
R3 gepopt, welches dann in LR kopiert wird. Sinnfrei, aber sieht nicht 
verkehrt aus.

Es sei denn, da ist noch irgendwo die Nullungsschleife fürs RAM drin, 
und das nullt sich u.a. auch den Stack weg. Das ist doof, wenn man dann 
nämlich u.a. LR wieder vom Stack popt, dann kriegt man die beobachtete 
null raus.

Step mal im Einzelschritt durchs Disassembly, da muß irgendwo eine 
Schleife sein, die grob so aussieht:

- ein Register wird mit 0 geladen
- ein anderes Register mit der Startadresse vom RAM
- ein weiteres mit irgendeiner End-Adresse
- und dann wird der Inhalt des genullten Registers in einer Schleife in 
das Register mit der Startadresse geladen. Das letztere wird dann 
typischerweise um 4 erhöht.

von Rem V. (veryxrv)


Lesenswert?

Es ist so, dass _estack im Linker Script standardmäßig von der IDE auf 
0x20040000 gesetzt wird. Auf diese Weise bekomme ich aber immer diesen 
HardFault. Setze ich diesen Wert auf 0x20030000 funktioniert alles (ist 
auch in den mitgelieferten Beispielen so).

Bei der scheinbar sinnlosen _init Funktion hatte ich noch eine nicht 
vorhandene Speicheradresse für den Stack im Linker Script hinterlegt, 
was beim Laden aus dem Stack dazu geführt hat, dass die Register alle 0 
waren.

Was ich nicht ganz verstehe ist nun, warum es mit einem initialen Stack 
Pointer von 0x20040000 (Ende SRAM) nicht funktioniert. Ich habe mal 
etwas rumprobiert und die letzte funktionierende Speicheradresse ist 
irgendwo in der Nähe von 0x2003df0, etwa 12 kB vor dem Ende des SRAM. 
Alles darüber hinaus führt zum HardFault.

von Nop (Gast)


Lesenswert?

Rem V. schrieb:

> Was ich nicht ganz verstehe ist nun, warum es mit einem initialen Stack
> Pointer von 0x20040000 (Ende SRAM) nicht funktioniert.

Was für ein Chip ist überhaupt auf dem Board? Was sagt denn dessen 
Reference Manual zum Thema Speicherbereiche?

> Ich habe mal
> etwas rumprobiert und die letzte funktionierende Speicheradresse ist
> irgendwo in der Nähe von 0x2003df0, etwa 12 kB vor dem Ende des SRAM.
> Alles darüber hinaus führt zum HardFault.

Kann natürlich sein, daß irgendwas in der IDE da noch irgendwas anderes 
hinlegt, was mit der Verwendung kollidiert.

Zudem kennt jedenfalls der Cortex-M3/4 zwei verschiedene Stacks, die für 
Verwendung in einem RTOS gedacht sind: Thread Mode und Handler Mode. 
Wenn Du auch noch ein RTOS drin hast, was schon irgendwas einstellt, 
wäre es möglich, daß Du tatsächlich zwei Stacks hast, die da 
kollidieren.

Siehe hier: 
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0179b/ar01s02s06.html

von Rem V. (veryxrv)


Lesenswert?

Nop schrieb:
> Was für ein Chip ist überhaupt auf dem Board? Was sagt denn dessen
> Reference Manual zum Thema Speicherbereiche?

Der Chip ist ein STM32WB55RG mit 256 kB SRAM beginnend bei 0x20000000 
(lt. Reference Manual).

Nop schrieb:
> Wenn Du auch noch ein RTOS drin hast, was schon irgendwas einstellt,
> wäre es möglich, daß Du tatsächlich zwei Stacks hast, die da
> kollidieren.

Das FreeRTOS ist in der Konfiguration deaktiviert. Mein Projekt ist ein 
blanko Projekt aus der Vorlage für genau dieses Board (STM Nucleo WB55), 
ohne jeglichen "Schnickschnack". Fast alle Peripheriekomponenten außer 
GPIO sind deaktiviert.

EDIT: Bin gerade durch Zufall auf die Info gestoßen, dass es 
unterschiedliche Firmware Binaries für den Chip gibt, je nach dem, ob 
ein Wireless Stack benötigt wird. Da eine Wireless-Demo vorgeladen ist, 
ist wahrscheinlich eine entsprechende Wireless-Firmware drauf. Das 
könnte eventuell das Problem sein.

: Bearbeitet durch User
von ... (Gast)


Lesenswert?

> (lt. Reference Manual).
Ja, mit der falschen Literatur und stuemperhaften Herumprobieren
wird das noch bis zum Sankt Nimmerleinstag dauern.

Die "Memory Map" ist bei ST im allg. im Datasheet.

Und die verraet auch welche Peripherie dort auf dem Speicher
herumzaubert.

So wird das jedenfalls nuex.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Bei dem Bluetooth STM32 hier is das aber andersrum, also sei leise.

The detailed memory map and the peripheral mapping of the STM32WB55xx 
devices can be found in the reference manual RM0434.

@topic:
Gibts denn eine Doku was die Wireless FW auf dem Cortex M0 macht?
Ansonsten bleibt das hier wirklich ein stochern im dunkeln und der 
SRAM2b sollte nicht vom M4 genutzt werden.

Ansonsten steht beim SRAM2 a/b, dass man den Zugriff durch den M0 (CPU2) 
durch das C2RFD Bit im SYSCFG Register sperren kann.
Wenns dann geht ist klar worans liegt.

von Nop (Gast)


Lesenswert?

... schrieb:

> Die "Memory Map" ist bei ST im allg. im Datasheet.

Üblicherweise sowohl Kapitel 2 Refman als auch Kapitel 4 Datasheet.

Mw E. schrieb:

> Ansonsten steht beim SRAM2 a/b, dass man den Zugriff durch den M0 (CPU2)
> durch das C2RFD Bit im SYSCFG Register sperren kann.

Aber nur code execution aus dem SRAM, nicht jedweden Zugriff. Es gibt 
aber auch noch C2BOOT in PWR_CR4, was man auf 0 setzen kann, dann bootet 
CPU2 nicht und wird nicht dazwischenfunken.

von Rem V. (veryxrv)


Lesenswert?

Habe gerade nochmal in das Reference Manual geschaut. SRAM2 beginnt ja 
genau ab 0x20040000, während der (funktionierende) SRAM1 genau ab 
0x20030000 beginnt. So wie ich das verstehe, gibt es zwar eine 
Möglichkeit, den SRAM2 gemeinsam zu nutzen, dafür muss aber scheinbar 
eine entsprechende arbitration Logik implementiert werden. So verstehe 
ich das jedenfalls mit meinem Halbwissen.

Das reicht mir jedenfalls, um mich weiter einarbeiten zu können. Danke 
für eure Hilfe! :)

von Giesser (Gast)


Lesenswert?

Ich habe auch schon häufig festgedtellt, dass die Standard Cube MX 
Projekte für die ST Boards nie recht  funktionieren. HardFault etc..

Seither erstelle ich immer selbst ein Projekt mit Auswahl des 
Controllers und Config.. Da kann man schön Schritt für Schritt 
vorgehen..

Just my two cents..

von The A. (the_a343)


Lesenswert?

Hallo Remo V.

Wenn du jetzt weißt, was falsch ist, dann mach doch im STM Forum ein 
Bugrepo RT....

Danke und Grüße, Adib.

von Rem V. (veryxrv)


Lesenswert?

Giesser schrieb:
> Seither erstelle ich immer selbst ein Projekt mit Auswahl des
> Controllers und Config.. Da kann man schön Schritt für Schritt
> vorgehen..

Kannst du das kurz genauer beschreiben, wie du da vorgehst bzw. was du 
verwendest?
Falls du damit meinst, dass du in CubeMX anstatt des Boards nur den 
Controller auswählst: Hab ich schon versucht, funktioniert aber bei dem 
in CubeIDE integrieren MX nicht :-/

The A. schrieb:
> Wenn du jetzt weißt, was falsch ist, dann mach doch im STM Forum ein
> Bugrepo RT....

Mache ich!

von Hauke Radtki (Gast)


Lesenswert?

Rem V. schrieb:
> The A. schrieb:
>> Wenn du jetzt weißt, was falsch ist, dann mach doch im STM Forum ein
>> Bugrepo RT....
>
> Mache ich!

Kannst du dann den Link hier rein stellen? Hatte das gleiche Problem und 
würde gerne das Issue verfolgen.

von Peter Schmid (Gast)


Lesenswert?

Der Thread ist zwar alt, vielleicht hilft mein Kommentar aber anderen 
STM32WB Benutzern weiter.

Der STM32WB hat 2 CPUs. CPU1 ist ein Cortex M4 und im allgemeinen läuft 
hier die Applikation. CPU2 ist eine Cortex M0+ und die ist für die BLE 
Kommunikation zuständig. Die beiden CPUs teilen sich Flash und RAM. Wenn 
ein BLE Stack installiert ist, ist ein Teil des RAMs für die CPU2 
reserviert und vor Zugriffen der CPU1 geschützt. Sobald die CPU1 
versucht auf dieses RAM zuzugreifen, gibt es ein Hardfault. Von den 256 
KiB RAM sind 64 KiB (ab 0x20030000) für CPU2 reserviert oder mit CPU1 
geshared. Das könnte man auch ändern, davon würde ich aber die Finger 
lassen. Wenn man schon eine BLE MCU einsetzt, nehme ich an, dass man den 
BLE Stack auch nutzen will.

Wer sich für ein interaktives Forth System auf Basis STM32WB und CubeMX 
interessiert, kann ja mal hier reinschauen:
https://spyr.ch/twiki/bin/view/MecrispCube/

Peter

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.