Forum: Mikrocontroller und Digitale Elektronik CortexM3 (STM32F103) - IMPRECISERR - Wie finden


von Holger (Gast)


Lesenswert?

Hallo zusammen

Ich habe hier eine Applikation in welcher ein STM32F103 als USB-Device 
(HID) an einem PC hängt. Dieses Device emuliert einen Touchscreen.

Code gibts hier:
https://github.com/chandong83/stm32_i2c_to_usb_hid_multitouch

Nun ist es so, dass bei intensiver nutzung des Touchscreens und damit 
hohem Datenaufkommen, nach kurzer Zeit (2-8 Sekunden) ein HardFault 
eintritt.

Dieser Fault habe ich nun mit dem FaultAnalyzer (Kauf von Atoolic durch 
ST sein Dank ist dieser ja nun kostenfrei nutzbar) im STM32CubeIDE 
analyziert.

Es handelt sich um einen PRECISERR, sowie Bus, Memory management or 
usage fault. (FORCED BIT gesetzt)

BFAR: 0x3002bd9f
sp (MSP): 0x20004E90
lr: 0x80061C3
pc: 0x8005272
xpsr: 0x61000024

Nun erinnere ich mich, dass das Bit2 beim lr angibt, ob der MainStack 
oder ProcessStack gültig war.
1
The current link register LR contains the EXC_RETURN value for the exception being serviced and this value indicates which stack holds the preserved register values from the application context. If bit 2 of the EXC_RETURN is zero then the main stack (MSP is saved) was used, else the process stack was used.

In meinem Fall ist Bit2 von LR jedoch 1. Somit war der ProcessStack 
gültig.
Doch wo finde ich diesen?

Vielen Dank für eure Unterstützung.

von Holger (Gast)


Lesenswert?

Ich vermute einen Bufferoverflow irgendwo. Aber wie könnte man so einen 
finden?

von Holger (Gast)


Lesenswert?

Hab da gerade ein schönes PDF gefunden:

http://www.keil.com/appnotes/files/apnt209.pdf


Der darin enthaltene Text lässt jedoch nicht hoffen, dass ich den 
Verursacher finden kann.
1
Asynchronous bus faults are described as imprecise bus faults and can happen when there is write buffering in the processor design. As a result, the processor pipeline proceeds to the subsequent instruction execution before the bus error response is observed. When an asynchronous bus fault is triggered, the BusFault exception is pended. If another higher priority interrupt event arrived at the same time, the higher priority interrupt handler is executed first, and then the BusFault exception takes place. If the BusFault handler is not enabled, the HardFault exception is pended instead. A HardFault caused by an asynchronous BusFault never escalates into lockup. Asynchronous faults are often unrecoverable, as you do not know which code caused the error.

Klingt jedoch so, als könnte die ursache ein zu hoch priorisierter 
interrupt sein?

von A. F. (artur-f) Benutzerseite


Lesenswert?

Holger schrieb:
> Ich vermute einen Bufferoverflow irgendwo. Aber wie könnte man so einen
> finden?

Muss nicht bufferoverflow sein.
At To, hast du dieses Dokument angeschaut:
http://www.keil.com/appnotes/files/apnt209.pdf

Dort wird beschrieben, wie man rausfinden kann, welcher code genau einen 
Fault verursacht.

Gruß

von Holger (Gast)


Lesenswert?

Danke für deinen Link.

Ja, genau dieses Dokument habe ich einen Beitrag weiter oben verlinkt :)
Bisher habe ich damit aber noch keine weiterführenden Informationen 
gefunden. Aber ich sehe dort, dass die den Stack entsprechend 
analysieren.


Nach weiteren Versuchen habe ich nun einen PRECISERR erhalten.
Die Busfaultaddresse ist: 0x3002a59e

Doch laut ReferenceManual befindet sich an dieser Adresse überhaupt 
keine Peripherie

(RM0008, Seite 50 ff.)

von Adam P. (adamap)


Lesenswert?

Hey Holger,

ich nutze zwar AtmelStudio, aber vllt. bringt dich das ein wenig weiter.

Bei meinem letzten HardFault bin ich wie folgt an das Problem 
herangegangen:

Ich habe mir für alle möglichen "Systemfehler" leere Interrupt-Handler 
angelegt - kann natürlich sein, dass diese bei STM Projekten bereits 
existieren.
1
void NMI_Handler(void)
2
{
3
    while (1) {
4
    }
5
}
6
7
void HardFault_Handler(void)
8
{
9
    while (1) {
10
    }
11
}
12
13
void MemManage_Handler(void)
14
...
15
void BusFault_Handler(void)
16
17
...usw.


Den µC habe ich dann im Debug-Mode laufen lassen (JTAG), bis der Fehler 
aufgetretten ist.

Zu beachten ist, dass du es aber als Release (mit optimierung) laufen 
lässt aber im Projekt den Debug Level (-g3) z.b. aktivierst.

Da der Fehler evtl. gar nicht auftritt, wenn die Optimierung im Debug 
Fall deaktiviert ist (Bufferoverflow, Anordnung der Variablen).

Als der µC dann hängen geblieben ist, konnte ich im Fenster 
"Aufrufliste" sehen, an welcher Stelle er in den HardFault gesprungen 
ist.

Dies ergab erst mal kein Sinn, aber wenn du nun noch die *.map Datei zur 
hilfe nimmst, kannst du sehen welche Variablen vor oder nach der liegen, 
wo er in HardFault gesprungen ist.

Bei mir war es ein Bufferoverflow (mittels Pointer) der mir danach ein 
Funktionszeiger auf 0x00 gesetzt hat - tja, don't try to call a NULL 
function pointer :-D

Aber somit war der Fehler gefunden.

Ich habe es auch erst mit den Bits in den Registern versucht, aber das 
ist oft nur ein Hinweis.

Edit:
Sorry, hatte die letzten 2 beiträge nicht mitbekommen ;)

Was hatte der PC als Wert?

PRECISERR: Precise data bus errorWhen the processor sets this bit is 1, 
it writes the faulting address to the BFAR.
0: No precise data bus error
1: A data bus error has occurred, and the PC value stacked for the 
exception return points to the instruction that caused the fault.

von Holger (Gast)


Lesenswert?

Vielen Dank für deine ausführliche Antwort.

Der PC hat: 0x8005276

Dies führt zu diesem C-Code:
1
USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev)
2
{
3
  if (pdev->dev_state == USBD_STATE_CONFIGURED)
4
  {
5
    if (pdev->pClass->SOF != NULL)
6
    {
7
      pdev->pClass->SOF(pdev);
8
    }
9
  }
10
11
  return USBD_OK;
12
}

Und zwar an diese Stelle:
1
if (pdev->dev_state == USBD_STATE_CONFIGURED)

von Adam P. (adamap)


Lesenswert?

Kannst du sehen welchen Wert dev_state hat?

Es ist ja "nur" ein Vergleich, aber wenn pdev auf einen falschen 
Speicherbereich zeigen würde, dann könnte der Vergleich nicht 
durchgeführt werden. Z.B. auf einen Speicherbereich der gar nicht 
existiert.

von Holger (Gast)


Lesenswert?

Das habe ich mir auch gedacht. Ich glaube aber, dass ich dev-state mit 
dem Debugger nicht mehr analysieren kann.

Ich könnte jedoch den Wert von DevState zuvor in einer Variable 
speichern...

von Adam P. (adamap)


Lesenswert?

Wäre halt interessant zu wissen wohin pdev zeigt.

von pegel (Gast)


Lesenswert?

Nur so eine Idee:
wenn ein Fehler nach gewisser Laufzeit auftritt, mache ich als ersten 
den Stack-Bereich grösser. Wenn es dann länger dauert, oder gar nicht 
passiert, wird es daran gelegen haben.

von Adam P. (adamap)


Lesenswert?

Tritt der Fehler auch auf, wenn du das Touch nicht nutzt?

In der Datei "usbd_core.c" werden diverse USB Aktionen behandelt.
Was ist wenn der Host dein Device vom Bus schmeißt oder etwas anderes 
provoziert (bedingt durch intensive Nutzung).

Dann könnte ja pdev evtl. gar nicht initialisert sein:
1
/* Free Class Resources */
2
pdev->pClass->DeInit(pdev, (uint8_t)pdev->dev_config);

Dann funktioniert es auch nicht mit dem Vergleich.

Dies ist auch nur so eine Idee, hab die HAL nicht aufm Rechner, deshalb 
gestaltet es sich recht schwer, die ganzen Zuordnungen zu finden :-)

von Holger (Gast)


Lesenswert?

Adam P. schrieb:
> Wäre halt interessant zu wissen wohin pdev zeigt.

Definitiv. Da arbeite ich noch dran eine Lösung zu finden, um den Wert 
anzuzeigen.

pegel schrieb:
> Nur so eine Idee:
> wenn ein Fehler nach gewisser Laufzeit auftritt, mache ich als ersten
> den Stack-Bereich grösser. Wenn es dann länger dauert, oder gar nicht
> passiert, wird es daran gelegen haben.

Es ist nicht die reine "Zeit" welche hier entscheidend ist. Es scheint 
mir, als ob die Anzahl an übertragenen USB-Paketen entscheidend sind.

Denn ich muss den Touch mit fünf Fingern (Multitouch) verwenden und 
"herumspielen" damit dann nach ca. 3-5 Sekunden ein Fault eintritt.

Benutze ich den Touch nicht und lasse ihn herumliegen, funktioniert er 
auch noch nach einigen Minuten... Wenn ich dann nur wenige Eingaben 
mache, gibt es auch keine Probleme. Nur bei "intensiver" Nutzung.

von Adam P. (adamap)


Lesenswert?

Holger schrieb:
> Da arbeite ich noch dran eine Lösung zu finden, um den Wert
> anzuzeigen.

Kannst du es nicht einfach so machen?
1
/* Globale Variable (Kopie) */
2
USBD_HandleTypeDef *my_pdev;
3
4
5
USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev)
6
{
7
  my_pdev = pdev;
8
  
9
  if (pdev->dev_state == USBD_STATE_CONFIGURED)
10
  {
11
    if (pdev->pClass->SOF != NULL)
12
    {
13
      pdev->pClass->SOF(pdev);
14
    }
15
  }
16
17
  return USBD_OK;
18
}

Dann setzt ein Break Point und schaust welche Adresse er im normal 
Betrieb hat...dann lässt es weiter laufen bis der Fehler auftritt.

Und schaust was my_pdev sagt...solltest du da so nicht herankommen, dann 
schau im *.map File wo dein my_pdev im RAM liegt und schau was da drin 
steht.

Oder?

von Holger (Gast)


Angehängte Dateien:

Lesenswert?

Danke für deinen Vorschlag.

Habe dies nun so umgesetzt. Nach dem Hardfault kann ich über die 
Expressions nicht mehr auf die Variable zugreifen. Da kommt:
1
Multiple errors reported.
2
3
1) Unable to create variable object
4
5
2) Failed to execute MI command:
6
-data-evaluate-expression pdev
7
Error message from debugger back end:
8
No symbol "pdev" in current context.
9
10
3) Failed to execute MI command:
11
-var-create - * pdev
12
Error message from debugger back end:
13
-var-create: unable to create variable object
14
15
4) Failed to execute MI command:
16
-var-create - * pdev
17
Error message from debugger back end:
18
-var-create: unable to create variable object

hab mal ein paar screenshorts gemacht.
Frage zu addrC.jpg. Soll ich im Speicher an Adresse 0x20000444 
nachschauen? Wenn ja, dann ist addrA.jpg das passende Bild dazu.

addrD.jpg zeigt den Inhalt des speichers im korrekten Zustand. Bei 
addrA.jpg ist der Inhalt beim fault zu sehen.

von Holger (Gast)


Lesenswert?

Ok. Hab vorhin übersehen, dass ich anstatt my_pdev nur pdev gewatcht 
habe.

Nun habe ich my_pdev überwacht und, siehe da, der wert ist nun 0x2ec0030 
im fault fall. und nicht mehr 0x20000444

von Holger (Gast)


Lesenswert?

Habe nun mal einen Vergleich hinzugefügt:
1
USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef *pdev)
2
{
3
  my_pdev = pdev;
4
  if((uint32_t)my_pdev != 0x20000444)
5
  {
6
    my_pdev = pdev;
7
  }

Er trappt erfolgreich, bevor ein hardfault eintritt, in die 
if-Bedingung.

Bleibt die Frage, woher der falsche Pointer kommt.

von Adam P. (adamap)


Lesenswert?

Was liegt denn bitte an 0x2ec0030 bzw. 0x02ec0030...

ist das nicht Code (Flash) Bereich? sehr komisch.

Schau mal in die *.map

von Nop (Gast)


Lesenswert?

Holger schrieb:

> Bleibt die Frage, woher der falsche Pointer kommt.

Wahrscheinlich ein buffer overflow eines Buffers, der physikalisch vor 
Deinem Pointer liegt. Guck da mal ins Mapfile rein, welche Variablen 
davor liegen.

Hast Du Breakpoints mit write-watch? Wenn ja, dann setz mal einen 
ebensolchen auf Deinen Pointer und guck, wo der überschrieben wird.

von Holger (Gast)


Lesenswert?

Liegt in der Nähe des RAMs

RAM  0x0000000020000000 0x0000000000005000 xrw

Aber eben doch auserhalb

von Holger (Gast)


Lesenswert?

Ich bin nun etwas weiter.


Nach geraumer Zeit, wechselt der Status des USB von 
USBD_STATE_CONFIGURED zu USBD_STATE_DEFAULT
1
                                 uint16_t len)
2
{
3
  USBD_HID_HandleTypeDef     *hhid = (USBD_HID_HandleTypeDef*)pdev->pClassData;
4
5
  if (pdev->dev_state == USBD_STATE_CONFIGURED )
6
  {
7
    if(hhid->state == HID_IDLE)

Somit wird dieser Code-Teil schonmal nicht mehr ausgeführt.
Warum der USB-Status wechselt, habe ich jedoch noch nicht 
herausgefunden.

von Holger (Gast)


Lesenswert?

Der Fehler kann nur provoziert werden, wenn mind. 4 Finger auf dem Touch 
bewegt werden. Und auch nur bei schnellen Bewegungen.

Hab inzwischen das I2C von Software zu HW gewechselt. Ohne veränderungen 
auf das Fehlerbild.

von Holger (Gast)


Lesenswert?

Nop schrieb:
> Hast Du Breakpoints mit write-watch? Wenn ja, dann setz mal einen

Ich glaube der STM32 hat sowas (tolles) leider nicht.

Nun habe ich einen neuen witzigen Fehler.
1
  if (pdev->dev_state == USBD_STATE_CONFIGURED )
2
  {
3
    if(hhid->state == HID_IDLE)
4
    {
5
      hhid->state = HID_BUSY;
6
      USBD_LL_Transmit (pdev,
7
                        HID_EPIN_ADDR,
8
                        report,
9
                        len);
10
    }

pdev->dev_state ist nun = 5.

Ich glaube ich sehe langsam das Fehler bild.
Nun gab es keinen HardFault. Warum? Weil bei einem Wert von 5 keine 
Daten mehr gesendet werden, da USBD_STATE_CONFIGURED = 3 ist.

Interessanterweise ist jedoch 5 kein gültiger Wert für dev_state. Also 
wurde wieder irgendwo ein Speicherbereich überschrieben.

Könnte ich nun einen Write-Watch auf diese Adresse legen? Vorausgesetzt, 
der STM hätte sowas?

von Nop (Gast)


Lesenswert?

Holger schrieb:

> Hab inzwischen das I2C von Software zu HW gewechselt. Ohne veränderungen
> auf das Fehlerbild.

Methodisch vorgehen wie angeregt magst Du also nicht. Naja, dann bleibt 
halt nur Rumprobieren.

von Nop (Gast)


Lesenswert?

Holger schrieb:

> Könnte ich nun einen Write-Watch auf diese Adresse legen? Vorausgesetzt,
> der STM hätte sowas?

Ah hat sich überschnitten. Das müßte im Debugger gehen, bei den 
Breakpoints. Ansonsten halt noch die Empfehlung mit dem Mapfile zur 
EIngrenzung der möglichen Buffer.

von Holger (Gast)


Lesenswert?

Danke für deine Antwort.

Leider finde ich bei Atollic nirgends eine entsprechende Möglichkeit für 
einen write-watch...

von Nop (Gast)


Lesenswert?

Holger schrieb:

> Leider finde ich bei Atollic nirgends eine entsprechende Möglichkeit für
> einen write-watch...

Wenn man nach stm32 atollic breakpoint write googelt, scheint sich das 
"Watchpoint" zu nennen - vielleicht hilft das weiter?

https://www.youtube.com/watch?v=Z3lQ2mFa1xI

von Holger (Gast)


Lesenswert?

Vielen Dank! Das werde ich ausprobieren und berichten.

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


Lesenswert?

Es empfielt sich wegen sowas immer etwas Software für die Faulthandler 
einzubauen: Beitrag "ARM Cortex-M3/4/7 Faulthandler"

Ansonsten hast du noch nicht geschrieben was zum touchen genutzt wird 
und wo das reinschreibt.
Das wird ja irgendwo nen Bufefr haben der speichert was eingegeben 
wurde.
Den auch mal im mapfile suchen ob der vor den Variablen des USB Stack 
liegt und dann mit Trace printfs (vllt sogar in schnell über SWO) gucken 
wie groß der wird.

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.