Hallo, ich habe eine Frage bezüglich dem HardFault Handler auf einem ARM Mikrocontroller. Wann kommt dieser zum Einsatz (Grund)?
LEON schrieb: > ann kommt dieser zum Einsatz (Grund)? http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Babcefea.html PDF-Version ist besser lesbar.
:
Bearbeitet durch User
Der HardFault ist die letzte Eskalationsstufe eines Faults (Mem, Bus, Secure, ...). Es gibt ein paar verschiedene, je nach System. Bei einigen MCUs landest du im HF, wenn z.B. ein nicht eingeschaltetes Peripheral angesprochen wird. Bei Stack- und Pointerverletzungen/Array, welche den Programmablauf beeinflussen, landest du auch im HF (z.B. Programmcode überschrieben, Adresse nicht zugreifbar). Ggf. kannst du noch recovern, im Allgemeinen wird man wohl noch letzte Daten speichern oder eine Verbindung schliessen, und dann das Dingen resetten (NVIC System Reset). Die Fault Handler sind in den meissten startup files vordefiniert (z.B. mit "b ." im ASM startup der MDK-ARM IDE), und können durch eigene Implementierungen überschrieben werden (da [weak]). Im Falle eines HF wird dann deine Funktion aufgerufen.
TL;DR Ich mache in den HardFault Handler gerne ein LED Blinken rein, Ursache vom Faults sind eigentlich immer Programmierfehler.
Random .. schrieb: > Der HardFault ist die letzte Eskalationsstufe eines Faults Leider falsch. Die letzte Stufe ist der Lockup - wenn ein Fault im HardFault Handler selbst auftritt. Je nach Gutdünken des Herstellers hängt sich der µC dann auf oder wird resettet.
Jim M. schrieb: > Random .. schrieb: >> Der HardFault ist die letzte Eskalationsstufe eines Faults > > Leider falsch. Die letzte Stufe ist der Lockup - wenn ein Fault im > HardFault Handler selbst auftritt. Stimmt :-) Aber so weit bin ich noch nie gekommen *grins+
LEON schrieb: > Wann kommt dieser zum Einsatz (Grund)? "Zum Einsatz" kommt der hoffentlich nie :D Er passiert aber leider immer wieder. Eine Ursache ist z.B. ein ungültig ausgerichteter Lese-/Schreibzugriff auf eine Speicheradresse. Wird beispielsweise der Befehl LDR auf die Adresse 0x08000001 ausgeführt, so wird's scheppern und du landest im Hard Fault. Da LDR ein 32-Bit Wort in ein Register lädt, muss die Adresse, auf die zugegriffen wird, an einer 32-Bit-Grenze beginnen (also 0x..00, 0x..04, 0x..08, 0x..0C). Da nun 0x08000001 ein Byte neben einer gültigen Adresse liegt, knallt dein Programm. Analog sieht das natürlich auch für den LDRH-Befehl für 16-Bit Datenworte aus. Dieses Beispiel trifft natürlich nur auf Prozessoren zu, die keinen sogenannten "Unaligned Memory Access" zulassen, wie z.B. die Cortex-M0-Reihe. Daher sind HardFaults auch so fies: man muss oft in das Assembler-Listing reinschauen und Befehl für Befehl durchgehen, um den Übeltäter zu ermitteln - vorausgesetzt natürlich, dass man schon eine Idee hat, wo man zu suchen hat. Da gibt es auch Tools am Markt, die einem diese Suche sehr vereinfachen. Als Hobbyist kann ich mir die aber nicht leisten :D
:
Bearbeitet durch User
A.. P. schrieb: > Dieses Beispiel > trifft natürlich nur auf Prozessoren zu, die keinen sogenannten > "Unaligned Memory Access" zulassen, wie z.B. die Cortex-M0-Reihe. Die Falle bei Cortex-M3 und -M4 ist LDRD/STRD, und den Befehl generiert der Compiler schon mal wenn er 2 32-Bit Werte hintereinander lesen kann. Anders als LDR besteht er hier auf striktes alignment.
Es gibt im Cortex Kern Register, die beim Einkreisen eines Hard Faults hilfreich sind. Trotzdem gibt es immer noch Szenarien, die etwas mehr Einsicht erfordern (z.B. imprecise faults, die nicht direkt beim letzten verarbeiteten Maschinenbefehl vor dem fault aufgetreten sind, sondern eine in der Regel kleine Anzahl von Zyklen vorher). Das hilfreichste tool ist ein trace und etwas Erfahrung, die einem die x wahrscheinlichsten Gründe im Hinterkopf als Combobox anbietet (u.A. Stack Overflow, falsch konfigurierte Interrupthierarcheit).
:
Bearbeitet durch User
A.. P. schrieb: > Da gibt es auch Tools am Markt, die > einem diese Suche sehr vereinfachen. Als Hobbyist kann ich mir die aber > nicht leisten :D Wieso leisten? Du gehst mit den Debugger rein und schaust wo er hergekommen ist. Dazu hilft ein Blick in die Doku welche Register man sich anschauen muss und wie man die interpretiert. Was hat das mit "leisten" zu tun?
Bernd K. schrieb: > Wieso leisten? > > Du gehst mit den Debugger rein und schaust wo er hergekommen ist. Dazu > hilft ein Blick in die Doku welche Register man sich anschauen muss und > wie man die interpretiert. Was hat das mit "leisten" zu tun? Damit meinte ich keine einfachen Debugger, sondern Zusatzmodule für Entwicklungsumgebungen oder gar spezielle Tools (Software), die mit verschiedensten Methoden (Analyse des Codes, statistische Verteilungen, Einbindung von Betriebssystemobjekten, automatische Rückverfolgung des Stack-Trace und was es da nicht sonst so geben könnte) einem solchen HardFault viel schneller auf die Schliche kommen, als der Weg zu Fuß. Und da solche Tools aufgrund der damit einhergehenden Zeitersparnis vor allem auf den industriellen Bereich abzielen, findet man sie nicht in kostenlosen Versionen von Entwicklungsumgebungen und muss sie häufig für teuer Geld dazukaufen. Das hat es mit "leisten" zu tun. Dass man jeden Bug in seinem Code auch mit einem 5€-Debugger ermitteln kann, ist ja schon klar.
A.. P. schrieb: > Und da solche Tools aufgrund der damit einhergehenden Zeitersparnis Also ich produzier ungefähr alle 6 Monate einen Hardfault bei dem ich länger als 2 Sekunden nachdenken muss und nur zwei davon haben mich wirklich nennenswert Zeit gekostet: * Den ersten hatte ich drei Tage nachdem ich zum allerersten Mal mit ARM angefangen habe, die Ursache war irgendeine fehlerhafte/unsachgemäße Initialisierung meinerseits von irgendwas in irgendeinem STM32 HAL Init Struct und er ist dann irgendwo anders scheinbar grundlos ins Nirvana gesprungen. Danach hab ich übrigens den HAL-Mist komplett über Bord geworfen und nochmal frisch angefangen, es hat nur 3 Tage gedauert um zu dieser Erkenntnis zu gelangen. * Beim zweiten (es war 2 Wochen nach meinem ARM-Einstieg, immer noch blutiger Anfänger) war es ein fehlendes align im Linkerscript. Das war aber relativ einfach denn es hat immer an der selben Stelle gekracht, in der flash-to-ram loop und war durch simples Durchsteppen und Beobachten der Registerinhalte leicht zu identifizieren. Es hat nur deshalb so lange gedauert weil ich nicht damit gerechnet habe daß das Linkerscript eines erfahrenen ARM-Gurus von dem ich es hatte fehlerhaft sein könne und weil ich erst 2 Wochen damit zu tun hatte. * die anderen 100 Hardfaults die mir später noch begegnet sind waren nach kurzem nachdenken oder grobem einkreisen der Stelle sofort offensichtlich, mit steigender Erfahrung immer schneller zu finden und weniger häufig, in absteigender Reihenfolge der Häufigkeit: * (98) Peripherietakt nicht eingeschaltet und trotzdem darauf zugegriffen * (2) char* nach long* gecastet und nicht ans alignment gedacht.
Bernd K. schrieb: > Also ich produzier ungefähr alle 6 Monate einen Hardfault bei dem ich > länger als 2 Sekunden nachdenken muss und nur zwei davon haben mich > wirklich nennenswert Zeit gekostet: Du musst ja ein wirklich seltenes Naturtalent sein, ein Savant sozusagen. Aber du solltest nicht von dir auf andere schließen…
:
Bearbeitet durch User
Ach quatsch, Bernd hat recht. Im "gdb" gibt man "bt" ein und in 90% der Fälle sieht man dann schon die Zeile an der es hapert. Etwas tückisch ist nur dass meist die nächste Zeile angezeigt wird (die Rücksprungadresse der ISR). In hartnäckigeren Fällen steppt man das Programm durch bis es kracht. Wenn es ein Timing-Kritisches Problem ist macht man die Breakpoints sukzessive an späteren Punkten im Ablauf und resettet halt jedes Mal. Bernd K. schrieb: > * (2) char* nach long* gecastet und nicht ans alignment gedacht. Das ist ja auch verb0ten ;-) ... hatte ich auchmal, in einer Library von NXP. Der Keil machte da irgendeine Magic aufgrund der es doch ging, aber der GCC nicht... Bernd K. schrieb: > * Beim zweiten (es war 2 Wochen nach meinem ARM-Einstieg, immer noch > blutiger Anfänger) war es ein fehlendes align im Linkerscript. Das ist/war in den Beispiel-Linker-Scripten von ST auch falsch. Also ist nicht notwendigerweise dein Guru schuld...
Hardfault Beispiel: Interrupt aktiviert, aber Interrupt Service Routine im Programmcode fehlt.
Beitrag #5338026 wurde von einem Moderator gelöscht.
Dr. Sommer schrieb: > Ach quatsch, Bernd hat recht. Im "gdb" gibt man "bt" ein und in 90% der > Fälle sieht man dann schon die Zeile an der es hapert. Die Zeile zu kennen, die den Hardfault auslöst, ist nicht immer gleichzusetzen damit, die Ursache zu kennen.
P. Loetmichel schrieb im Beitrag #5338026: > BASCOM kennt keine Hardfaults. Und das soll ein Vorteil sein, dass man die Fehler nicht gezeigt bekommt? Was macht BASCOM denn im Fehlerfall, einfach nur abstürzen? Walter T. schrieb: > Dr. Sommer schrieb: > Ach quatsch, Bernd hat recht. Im "gdb" gibt man "bt" ein und in 90% der > Fälle sieht man dann schon die Zeile an der es hapert. > > Die Zeile zu kennen, die den Hardfault auslöst, ist nicht immer > gleichzusetzen damit, die Ursache zu kennen. Korrekt. Aber oft. Zumindest kann man ab da weitersuchen.
Dr. Sommer schrieb: > P. Loetmichel schrieb im Beitrag #5338026: >> BASCOM kennt keine Hardfaults. > > Und das soll ein Vorteil sein, dass man die Fehler nicht gezeigt > bekommt? Was macht BASCOM denn im Fehlerfall, einfach nur abstürzen? > > Walter T. schrieb: >> Dr. Sommer schrieb: >> Ach quatsch, Bernd hat recht. Im "gdb" gibt man "bt" ein und in 90% der >> Fälle sieht man dann schon die Zeile an der es hapert. >> >> Die Zeile zu kennen, die den Hardfault auslöst, ist nicht immer >> gleichzusetzen damit, die Ursache zu kennen. > > Korrekt. Aber oft. Zumindest kann man ab da weitersuchen. Uhm... jein. Na. Definiere "oft..." ;-) Ein in meinem Umfeld relativ häufig auftretender Ursprung für einen Hard Fault ist ein übergelaufener Stack in einer von einem RTOS gemanagten task. Der Code der Task übertrampelt damit den Speicher von irgendjemand Anders, was heisst, dass der Fault von einer völlig Anderen Instanz ausgelöst wird (wenn also z.B. der übertrampelte Speicher der Stack einer anderen task ist). Da können schon Mal ein paar Context Switches dazwischen liegen. Es ist in der Regel völlig aussichtslos, hier an der Faultadresse anzufangen zu suchen. Wenn das Szenario deterministisch ist, helfen DWT breakpoints. Deswegen ist hier die Erfahrung relativ wichtig; wenn man seine "Pappenheimer kennt," hat man nach erster Analyse des fault stack frames schon die drei wichtigsten Ursprungsszenarien im Hinterkopf und kann dann gezielter das Gesamtsystem beleuchten und den (an Anderer Stelle auftauchenden) Fehler einkreisen. Also wenn der TO unter einem RTOS läuft, sollte der zweite Schritt beim Analyiseren eines Hard Faults sein (erster Schritt natürlich Analyse des Fault stack frames), die stacks sämtlicher tasks auf Überlauf zu untersuchen. Viele IDEs bieten dafür Plugins an.
Sollte das RTOS nicht mithilfe der MPU so etwas verhindern? Ein Watchpoint auf der höchsten Stack Adresse könnte dann helfen.
A.. P. schrieb: > Du musst ja ein wirklich seltenes Naturtalent sein Übertreibungen erhöhen die Anschaulichkeit.
Dr. Sommer schrieb: > Sollte das RTOS nicht mithilfe der MPU so etwas verhindern? Wenn man eine hat...
Wenn man ein RTOS auf einem Controller nutzt der dafür nicht geeignet ist, ist man natürlich selber schuld.
derjaeger schrieb: > Hardfault Beispiel: > > Interrupt aktiviert, aber Interrupt Service Routine im Programmcode > fehlt. Bei den üblichen Startup codes die man so sieht springt er dann aber in den jeweiligen default handler. Es sei denn man hat um Platz zu sparen alle Vektoren auf den selben default handler gesetzt, dann müsste man im SCB nachschauen welche IRQ-Nummer das eigentlich ist, oder noch extremer man hat die Vektortabelle bis auf die ersten 2 Einträge weggelassen um nochmal 200 Byte rauszuquetschen, aber wenn man sowas macht hat man das Anfängerlevel schon lange hinter sich und weiß was man macht denn die mitgelieferten startup codes der Hersteller die ich bisher gesehen habe machen keine solchen Sachen.
Dr. Sommer schrieb: > Sollte das RTOS nicht mithilfe der MPU so etwas verhindern? Ein > Watchpoint auf der höchsten Stack Adresse könnte dann helfen. Meintest Du MPU oder MMU? Die Cortex MPU kann nur Zugriffsschutz auf Handler/Threadmode Granularität verteilen, also User Mode code Zugriff auf ISR "eigene" Speicherbereiche oder umgekehrt regeln. Von verschiedenen logischen threads (oder tasks) weiss die MPU natürlich níchts. Ja, ein Watch BP auf einer der höchsten Stackadressen kann helfen, muss aber nicht (wenn die aufzurufende Subroutine also z.B. x Byte für lokale Variablen weg reserviert, aber auf die nicht alle schreibt, dafür aber auf darüber liegende Adressen, schlägt der WP nicht zu). Aus demselben Grunde ist Stackanalysecode vom RTOS (das füllt typischerweise beim Start einer tasks die mit einer Signatur und prüft beim Contextswitch den top of stack auf diese Signatur) auch nicht 100% zuverlässig. Davon abgesehen weiss man ja a priori nicht, welcher stack überläuft, also müsste man in jedem Stack so einen WP platzeren, und so viele hat man in der Regel nicht. Aber der Satz "Wenn man ein RTOS auf einem Controller nutzt der dafür nicht geeignet ist, ist man natürlich selber schuld" ist nun etwas deplaziert. Nur weil eine Fehlersuche erschwert wird, ist deswegen die Plattform nicht dafür untauglich. Es lassen sich z.B. mit gewissen Codierungseinschränkungen ja auch (beweisbar) beschränkte Stacktiefen einfordern. Ausserdem können einem ja die o.b. Plugins recht genaue Informationen darüber geben, welche Stacks Probleme haben. Wie gesagt macht man diesen Fehler genau einmal und guckt dann beim nächsten HF zunächst mal alle stacks an. Und natürlich könnte man jetzt philosophische Grundsatzüberlegungen anstellen, ob und wann RTOS jetzt überhaupt nötig sind und/oder ihre potentiellen Fehlerpotentiale die Gewinne aufwiegen, die sie bringen, aber (auch) das ist i.W. eine Glaubens- bzw. Einzelfallfrage, die in diesem Forum schon des Öfteren in aller Ausführlichkeit diskutiert wurde...
:
Bearbeitet durch User
Ruediger A. schrieb: > Von verschiedenen logischen threads (oder tasks) weiss die MPU natürlich > níchts. Deswegen muss das RTOS beim Kontextwechsel die MPU entsprechend einstellen. Wenn die Aufgabenstellung so komplex ist dass man ein RTOS braucht, wird der Controller wahrscheinlich auch etwas größer sein und eine MPU haben. Auf so als 8-Bit-Ersatz konzipierten Cortex-M0 die keine MPU haben wird man wohl eh keine derart komplizierten Dinge tun.
Dr. Sommer schrieb: > Ruediger A. schrieb: >> Von verschiedenen logischen threads (oder tasks) weiss die MPU natürlich >> níchts. > > Deswegen muss das RTOS beim Kontextwechsel die MPU entsprechend > einstellen. > So wie ich die MPU verstehe, kann die NICHT dazu verwendet werden, verschiedene im Thread mode ablaufende Handlungsstränge gegeneinander abzugrenzen, sondern nur thread mode gegen handler mode (also nicht-ISR gegen ISR code). Selbst wenn das ginge, wüsste ich gar nicht, nach welchen Kriterien ein RTOS entscheiden sollte, wie es dem Kern mitteilen kann, was ein gültiger und was ein ungültiger Zugriff ist. Man kann es definitiv nicht am ausgeführten Code festmachen. Code kann ja und wird oft zwischen tasks geteilt, also wenn es legitim ist, dass dieselbe Funktion in mehreren Kontexten läuft, wie könnte dann ein RTOS dem Kern kommunizieren, dass derselbe Code im Kontext der Task x auf Speicher y, aber nicht auf Speicher z zugreifen darf, im Kontext der Task w aber genau umgekehrt? Ich sehe im MPU Registersatz nichts, was so eine Funktionaltität umsetzen könnte. Selbst wenn das ginge, wäre es m.M. nach so komplex, dass es fast unvermeidlich einen Heisenberg Effekt nach sich zieht (also durch den Code, der zur Verwaltung dieser Dinge nötig ist, mehr Fehlerpotential erzeugt). Bist Du immer noch sicher, dass Du nicht MMU statt MPU schreiben wolltest? Eine MMU lässt ja per Definition vom OS verwaltete verschiedene gegeneinander abgegrenzte Speicherbereiche zu. > Wenn die Aufgabenstellung so komplex ist dass man ein RTOS braucht, wird > der Controller wahrscheinlich auch etwas größer sein und eine MPU haben. > Auf so als 8-Bit-Ersatz konzipierten Cortex-M0 die keine MPU haben wird > man wohl eh keine derart komplizierten Dinge tun. Doch. Kunden von mir haben von mir entwickelte Geräte auf Cortex M0+ und M3 Basis mit 128k Flash und 32K RAM im Einsatz, die mehrere Dinge nebenläufig machen und deswegen enorm von einem RTOS profitieren. Das geht mit modernen RTOS (in diesem Fall FreeRTOS) einschliesslich Bootloader problemlos. Andere Kunden haben eine ähnliche Problemstellung versucht ohne RTOS zu lösen, und das ist ein schrecklicher Alptraum.
Ruediger A. schrieb: > Andere Kunden haben eine ähnliche Problemstellung > versucht ohne RTOS zu lösen, und das ist ein schrecklicher Alptraum. Was für eine Problemstellung (konkretes Beispiel). "Mehrere Dinge" ist zu schwammig.
Ja ich meinte die MPU, nicht MMU. Die meisten Cortex-M3 sollten eine MPU haben. Wenn man einen Task/Thread startet, ruft man eine Funktion im OS auf. Die legt einen neuen Process Control Block an, also einen Datensatz mit Informationen zum Task, inklusive dessen Stack-Adresse&Größe, Priorität usw. Bei einem Kontextwechsel muss das OS auf diesen Block zugreifen um dort die Register/Stack Pointer hinein zu sichern bzw. wiederherzustellen. Bei der Gelegenheit kann es auch die Informationen zur Gesamt-Stack-Größe abrufen, und die MPU so einstellen dass der gesamte Bereich außerhalb des eigenen Stacks geschützt ist, oder einfach nur der "nächste" Stack. Dazu muss das RTOS überhaupt nicht wissen was die Tasks für Funktionen aufrufen, es geht nur darum welcher Task aktiv ist und welcher Stack-Bereich ihm zugewiesen ist. Wenn das ständige Umstellen zu langsam ist schaltet man es halt nur zum Debuggen ein. Ruediger A. schrieb: > und das ist ein schrecklicher Alptraum. Da kann wohl jemand keine endlichen Automaten designen...
Beitrag #5338292 wurde von einem Moderator gelöscht.
Dr. Sommer schrieb: > Ja ich meinte die MPU, nicht MMU. Die meisten Cortex-M3 sollten eine MPU > haben. > > Wenn man einen Task/Thread startet, ruft man eine Funktion im OS auf. > Die legt einen neuen Process Control Block an, also einen Datensatz mit > Informationen zum Task, inklusive dessen Stack-Adresse&Größe, Priorität > usw. Bei einem Kontextwechsel muss das OS auf diesen Block zugreifen um > dort die Register/Stack Pointer hinein zu sichern bzw. > wiederherzustellen. Bei der Gelegenheit kann es auch die Informationen > zur Gesamt-Stack-Größe abrufen... ACK. > und die MPU so einstellen dass der > gesamte Bereich außerhalb des eigenen Stacks geschützt ist, oder einfach > nur der "nächste" Stack. Dazu muss das RTOS überhaupt nicht wissen was > die Tasks für Funktionen aufrufen, es geht nur darum welcher Task aktiv > ist und welcher Stack-Bereich ihm zugewiesen ist. Wenn das ständige > Umstellen zu langsam ist schaltet man es halt nur zum Debuggen ein. > Da hätte ich gerne (Pseudo)code gesehen. Alles was m.M. nach der Kern kann ist zwischen Handler mode (im Kontext von ISRs) und thread mode (im Kontext grob gesagt der main() Funktion, was sämtliche vom RTOS kontrollierte tasks beinhaltet) zu unterschieden - d.h. den Zugriff auf Speicher nur im thread mode oder im handler mode zu erlauben. Aber sämtliche von einem RTOS gemanagte threads laufen im thread mode, d.h. das RTOS müsste dem Kern sowas wie ein Konzept eines (unser mode) Kontexts "beibringen," der aber von RTOS zu RTOS frei definiert sein kann. Ich lasse mich sehr gerne eines Besseren belehren, aber ich sehe nicht, wie eine MPU das leisten kann. > Ruediger A. schrieb: >> und das ist ein schrecklicher Alptraum. > Da kann wohl jemand keine endlichen Automaten designen...
Ruediger A. schrieb: > Da hätte ich gerne (Pseudo)code gesehen. Alles was m.M. nach der Kern > kann ist zwischen Handler mode (im Kontext von ISRs) und thread mode (im > Kontext grob gesagt der main() Funktion, was sämtliche vom RTOS > kontrollierte tasks beinhaltet) zu unterschieden Die MPU kann Speicher Zugriffe verbieten. Das RTOS kann die MPU bei jedem Kontext Wechsel umprogrammieren sodass nur Zugriff auf den eigenen Stack möglich ist. Die MPU muss nichts von unterschiedlich Threads wissen, sie weiß nur "jetzt gerade nur Zugriff auf Bereich X-Y" weil das RTOS im letzten Kontext Wechsel nur diesen Bereich eingestellt hat. Habe keinen Beispiel Code, aber wenn du in professionelle RTOS guckst wird's da garantiert was geben. Warum sonst hat die MPU so fein granulare Speicher Bereiche?
https://www.freertos.org/FreeRTOS-MPU-memory-protection-unit.html "User mode tasks can only access their own stack and up to three user definable memory regions (three per task)"
Dr. Sommer schrieb: > https://www.freertos.org/FreeRTOS-MPU-memory-protection-unit.html > > "User mode tasks can only access their own stack and up to three user > definable memory regions (three per task)" jope, interessanter Ansatz. Das funktioniert in der Tat, hatte einen Knoten im Hirn. Schönen Dank!
:
Bearbeitet durch User
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.