Hallo,
welche Möglichkeiten gibt es beim AVR-Debugging, eine konkrete
Speicherstelle zu überwachen und beim Schreiben dorthin mit einem Halt
abzubrechen damit man ermitteln kann welcher Code dazu führte?
Bei mir wird ein Stück Speicher falsch überschrieben und ich wüßte gern
von welcher Stelle eines recht umfangreichen Programms inkl. vieler
Interrupts.
Anton
Purzel H. schrieb:> Ich wuerd Funktionalitaeten abschalten und schauen, ob's immer noch> passiert.
Das Problem ist der Fehler tritt nur selten unter ungeklärten
Bedingungungen auf. Das so herauszufinden dauert zu lange.
Purzel H. schrieb:> Allenfalls einen Pin toggeln um zu erkennen, wo der Code durchgeht.
So kannst Du aber nicht herausfinden welcher Code schreibend auf eine
Speicherstelle zugreift.
Die oben erwähnten Data-Breakpoints scheinen das Mittel der Wahl. Leider
finden sich im Netz nur Hinweise dazu zum Support bei Mega/XMega und
nichts zu den neueren AVRxxDx... Habe momentan leider keinen Zugriff auf
die Hardware.
Funktionieren denn die Data-Breakpoints bei den AVRs?
Die müssen doch vom Prozessor unterstützt werden.
Selbst bei den XMegas hat das, so weit ich weis - nicht funktioniert.
Wie sieht das bei den SAMs aus?
Rückmeldung wäre schön.
Es ist aber schon klar, dass mit Hardware Debugging das
Echtzeitverhalten kaputt ist. Denn die MegaAVR haben keinen Hardware
Tracebuffer, oder Debugregister. Soweit ich weiss. Das laeuft dann ueber
JTAG, und dauerndes Lesen/Schreiben.
Anton schrieb:> Bei mir wird ein Stück Speicher falsch überschrieben
Typisch sind das Pointerfehler. Z.B. eine Funktion will 2 Byte
schreiben, der Pointer zeigt aber nur auf ein Bytearray. Oder das Array
ist zu klein (Puffer ohne Test auf Arraygrenzen). Oder ein Pointer wird
an einen Interrupt nicht atomar übergeben.
Die Indexschreibweise ist in der Regel weniger fehleranfällig, als die
Pointerschreibweise.
>>Purzel H. schrieb:>> Ich wuerd Funktionalitaeten abschalten und schauen, ob's immer noch>> passiert.>>Das Problem ist der Fehler tritt nur selten unter ungeklärten>Bedingungungen auf. Das so herauszufinden dauert zu lange.
Ja... ich habe schon Tage(Wochen) an einem Stueck verbracht um solche
Fehler zu finden. Ich wuensche viel Glueck.
Anton schrieb:> AVR128DBx Asm-programmiert
Schreib das Programm in C um, das eliminiert schonmal 90% der möglichen
Fehlerquellen.
Ich hab gemerkt, ab einer Größe von 4..8kB sind Assemblerprogramme nicht
mehr beherrschbar. Schon bei 2kB Größe fällt es schwer, nach einem Jahr
wieder durchzublicken.
Peter D. schrieb:> Anton schrieb:>>> Bei mir wird ein Stück Speicher falsch überschrieben>> Typisch sind ...
Ja, davon wird sicher etwas zutreffen. Fraglich nur an welcher Stelle.
Den Fall erschwert daß die Codeausführung maßgeblich von Input der über
serielle Schnittstellen reinkommt abhängt.
Purzel H. schrieb:> Ja... ich habe schon Tage(Wochen) an einem Stueck verbracht um solche> Fehler zu finden. Ich wuensche viel Glueck.
Danke. Das werde ich brauchen. Vor allem sollten die Data-Breakpoints
nicht funktionieren. Das Programm ist komplex, die Speicherstelle
kritisch, mit diversen Nebenwirkungen des Überschreibens die schlecht
überschaubar sind.
Anton schrieb:> Den Fall erschwert daß die Codeausführung maßgeblich von Input der über> serielle Schnittstellen reinkommt abhängt.
Dann kommt noch hinzu, ob Du auch wirklich ein sauberes fehlertolerantes
Protokoll definiert und implementiert hast. Alle Signale von außen
können gestört sein. Du benutzt natürlich einen Quarz und der
Baudratenfehler ist <1%?
Purzel H. schrieb:> Es ist aber schon klar, dass mit Hardware Debugging das> Echtzeitverhalten kaputt ist.
Zum Glück ist das in diesem Fall weitgehend egal.
Peter D. schrieb:> Schreib das Programm in C um, das eliminiert schonmal 90% der möglichen> Fehlerquellen.
Definitiv nicht. Auch wenn da sicher was dran ist. Soll hier aber jetzt
keine Grundsatz-Diskussion werden.
Peter D. schrieb:> Du benutzt natürlich einen Quarz und der Baudratenfehler ist <1%?
Nein. Die neueren AVRs sind bei den verwendeten Baudraten (9600/19200)
mit internem Oszillator hinreichend genau, der Input ist OK (kann man
sich ja im Debugger anschaun).
Peter D. schrieb:> Dann kommt noch hinzu, ob Du auch wirklich ein sauberes fehlertolerantes> Protokoll definiert und implementiert hast
Der Eingabestrom könnte Fehler enthalten die nicht berücksichtigt
werden. Da das nicht geändert werden kann soll das Programm darüber
hinwegsehen können.
Anton schrieb:> Purzel H. schrieb:>> Es ist aber schon klar, dass mit Hardware Debugging das>> Echtzeitverhalten kaputt ist.>> Zum Glück ist das in diesem Fall weitgehend egal.
Wenn auf die Speicherstelle bis zum Auftreten des Fehler schon ein paar
(tausend) Mal korrekt zugegriffen wird, ist das nicht mehr ganz so egal.
Oliver
Anton schrieb:> Nein. Die neueren AVRs sind bei den verwendeten Baudraten (9600/19200)> mit internem Oszillator hinreichend genauhttps://www.prosieben.de/tv/galileo/themen/mittwoch-17-dezember-2014/schlaumeier-pferd-vor-der-apotheke-kotzen-sehen
Vermutlich willst Du das Produkt nicht komerziell vermarkten.
Anton schrieb:> Der Eingabestrom könnte Fehler enthalten die nicht berücksichtigt> werden. Da das nicht geändert werden kann soll das Programm darüber> hinwegsehen können.
Dafür muß aber der Programmierer selber sorgen, z.B. mit einem
Protokoll. Außerdem müssen Pufferüberläufe in jedem Fall abgefangen
werden.
Oliver S. schrieb:> Wenn auf die Speicherstelle bis zum Auftreten des Fehler schon ein paar> (tausend) Mal korrekt zugegriffen wird, ist das nicht mehr ganz so egal.
Ja.
Im normalen Ablauf wird darauf aber nur lesend zugegriffen. Ob dieser
eine oder tausendfache falsche Schreib-Zugriff jetzt die Codeausführung
verzögert oder nicht ist beim Debugging zweitrangig da ich nur
herausfinden will von woher er kommt.
Peter D. schrieb:> Vermutlich willst Du das Produkt nicht komerziell vermarkten.
Nein. Ein Hobbyprojekt.
Peter D. schrieb:> Außerdem müssen Pufferüberläufe in jedem Fall abgefangen werden.
Ja. Diese gilt es auszuschließen. Den Speicher im Fehlerfall konnte ich
mir aber schon angesehen. Dabei waren keine Überläufe bei den UART
Empfangspuffern festzustellen.
Die große Frage ist, ob die Beobachtung in Echtzeit erfolgen muss.
Ohne einer Hardwareunzerstützung wid es in Echtzeit nicht möglich sein.
Speziell bei MCs ohne externen Adress und Datenbus.
1.) Früher baute man eine Bond-Out Version des Chips um direkt ins
Prozessorinnere sehen zu können.
https://en.m.wikipedia.org/wiki/Bond-out_processor
2.) Bei neueren Prozessoren gibt es fürs Debugging eine am Prozessorchip
implementierte Hardware. Stichwort: Hardware Breakpoints.
https://stackoverflow.com/questions/8878716/what-is-the-difference-between-hardware-and-software-breakpoints
In beiden Fällen ist es möglich eine Exeption(Interrupt) zu setzen wenn
die zu beobachtende Adresse benutzt wird. Bis an diese Stelle und nach
dieser Stelle kann der Code in Echtzeit ausgeführt werden. Beim
Auftreten der Adresse kann fürs Debugging durch die Exeption ein Code
eingeschoben werden, um Programmzähler und alle anderen internen
Register zwischendurch auslesen zu können.
Bei MCs ohne Hardwareinterrupt ist der Zugriff auf interne Register nur
mittels Singlestep möglich. Das Programm funktioniert dann nicht mehr in
Echtzeit, da gebe es auch die Möglichkeit das Programm auf einer anderen
Plattform zu simulieren.
Als ersten Schritt würde ich mich informieren ob der MC oder die CPU
über Hardwarebreakpoints verfügt. Dann könnte man wichtige Daten, wie
Programmzähler, Stackaufrufhistorie übrr den Exeptioncode in einem
unverlierbaren Speicherbereich aufheben oder über serielle Schnittstelle
ausgeben.
Anton schrieb:> Das Programm ist komplex
Von nix kommt nix. Natürlich neigen gerade "komplexe" (oder auch einfach
nur "komplizierte") Programme zu Fehlfunktionen. Besonders an Ecken, wo
man bei der Implementierung von fehlerfreier undgestörter Hardware
ausgegangen ist.
> die Speicherstelle kritisch
Öffne das Map-File. Finde heraus, wo diese "Speicherstelle" ist. Sieh
nach, was davor liegt. Gerne auch einige Bytes weiter vorne. Könnte bei
einer dort liegenden Variable/Array/Puffer so ein amoklaufender
Pointer/Index die nachfolgenden "Speicherstellen" überschreiben?
Wenn du nicht weiter kommst: sieh zu, dass du vor diese "Speicherstelle"
leeren Speicher platzierst. Schau dann und wann nach, ob da irgdenwer
irgendwas reingeschrieben hat. Versuche zu erkennen, was das für Daten
sind.
Lothar M. schrieb:> Öffne das Map-File. Finde heraus, wo diese "Speicherstelle" ist. Sieh> nach, was davor liegt.
Es ist Assembler Code.
Daher weiß ich genau und selbstbestimmt was wo ist.
> Könnte bei einer dort liegenden Variable/Array/Puffer so ein> amoklaufender Pointer/Index die nachfolgenden "Speicherstellen"> überschreiben?
Es schaut nicht danach aus. Vielmehr werden von irgendwoher (noch)
unbekannte Daten in einen wichtigen Sendepuffer reingeknallt. Da es sich
um Daten für ein Display handelt habe ich unmittelbare Rückmeldung.
> Besonders an Ecken, wo man bei der Implementierung von fehlerfreier> und gestörter Hardware ausgegangen ist.
An und für sich habe ich keinen konkreten Anlass an der Hardware zu
zweifeln, aber wer bin ich um da was sicher auszuschließen. Falls Du
auch auf das quarzlose UART abzielst kann ich meine problemlose
Erfahrung mit anderen Projekten gleicher/ähnlicher AVRs geltend machen.
Ein 4809+UART Übertragung arbeitet sogar außen in einer Wetterstation
unter Witterungsbedingungen absolut unauffällig.
Anton schrieb:> Vielmehr werden von irgendwoher (noch) unbekannte Daten in einen> wichtigen Sendepuffer reingeknallt.
Ist das ein Block oder nur einzelne Bytes? Mitten drin im Puffer oder
"am Rand"? Immer an der selben Stelle oder immer wieder beliebig
woanders?
> An und für sich habe ich keinen konkreten Anlass an der Hardware zu> zweifeln, aber wer bin ich um da was sicher auszuschließen.
Nein, ich meine, dass ich Schaltungen teste, indem ich mit einem
"Viehtreiber" entwicklungsbegleitend EMV-Tests mache wie im
Beitrag "Re: Probleme mit Störungen aus dem Netz"
beschrieben. Machst du solche ähnliche Störfestigkeitstests auch? Es
reicht z.B. bei einigen Schaltungen schon aus, wenn ich die Weller
Lötstatiom daneben stelle und die ein- und ausschalte...
> Falls Du auch auf das quarzlose UART abzielst
Nein, tu ich nicht. Ich hab keine Probleme mit so einer Frickelei. Ich
habe das auch mal gemacht. Aber ich baue jetzt immmer einen Quarz ein.
> arbeitet sogar außen in einer Wetterstation unter Witterungsbedingungen> absolut unauffällig.
Mein Glückwunsch.
Bei mir hatten halt ca. 2 von 1000 ab&zu Probleme. Und dann fährt ein
Monteur hin, sucht und flucht 2 Tage herum und tauscht dann das Modul.
Und das kostet letztlich so viel, da kann ich locker überall einen Quarz
ranbauen...
Anton schrieb:> Es ist Assembler Code.> Daher weiß ich genau und selbstbestimmt was wo ist.
Dann kannst Du doch testweise Bereiche an ne andere Stelle verschieben.
Du wirst ja nicht den gesamten RAM vollgeknallt haben.
Laß aber an der Originalstelle eine entsprechende Lücke. Wenn Du alles
gegeneinander verschiebst, wandert alles und Du bist nicht schlauer.
Schwer zu debuggen sind nicht atomare Zugriffe zwischen Main und
Interrupt. Wenn der Interrupt genau zwischen 2 Bytes zuschlägt, kriegt
er ein neues und ein altes Byte. Und schon hat er einen Pointer, der in
den Wald zeigt.
Also lieber zuviel atomar kapseln (CLI, Zugriffe, SEI), als einmal zu
wenig.
Anton schrieb:> An und für sich habe ich keinen konkreten Anlass an der Hardware zu> zweifeln
Stimmt. Zu 99,99% sitzt der Fehler vor dem Bildschirm.
Elektrolurch schrieb:> Funktionieren denn die Data-Breakpoints bei den AVRs?
Habe ich schon vor Jahren mit dem GDB genutzt, als irgendwelche Studios
das noch lange nicht konnten.
> Die müssen doch vom Prozessor unterstützt werden.
Ja, klar. Also: beim GDB nicht notwendigerweise, allerdings wird er
grottenlahm, wenn er das in Software emulieren muss. Dann wird nämlich
Einzelschritt gearbeitet und jedesmal die Speicherstelle gelesen.
> Selbst bei den XMegas hat das, so weit ich weis - nicht funktioniert.
Bei Xmegas kenne ich mich gerade nicht aus, aber da das schon bei den
alten Megas funktionierte, würde es mich wundern, wenn sie es dort
kaputt gemacht hätten.
> Wie sieht das bei den SAMs aus?
Funktioniert auch.
Ein Unterschied zu Code-Breakpoints ist natürlich, dass ein Data
Breakpoint (GDB nennt das "watchpoint") erst dann triggert, wenn die
Daten auch geändert worden sind. Ein Code-Breakpoint triggert vor der
Ausführung des Befehls.
Alter Native schrieb:> Deswegen läuft's ja auch so gut ....
Tja wenn das mal alles wär ;-)
Lothar M. schrieb:> Ist das ein Block oder nur einzelne Bytes? Mitten drin im Puffer oder> "am Rand"? Immer an der selben Stelle oder immer wieder beliebig> woanders?
Immer dasselbe, 28 Bytes, an derselben Stelle wild und quer über zwei
von mehreren Datenpuffern (deren Zeiger hintereinander an die
Senderoutine übergeben werden).
> Machst du solche ähnliche Störfestigkeitstests auch?
Nein. Vermutlich ist meine AVR Hardware da mit unter 10MHz unkritisch.
Hatte da noch nie Probleme (vom DCF77 Empfang mal abgesehen). Da am
augenblicklichen Entwicklerplatz (Monitore, Laptop, PC) aber im Äther
einiges los sein dürfte zumal die seriellen Daten über Funk reinkommen
möchte ich das im Hinterkopf behalten.
> Bei mir hatten halt ca. 2 von 1000 ab&zu Probleme.
Interessant. Neuere AVRs?
Ich mache mir immer auch die Mühe die tatsächliche Taktfrequenz zu
messen und fein abzustimmen.
> Und das kostet letztlich so viel, da kann ich locker überall einen Quarz> ranbauen...
Verständlich.
Peter D. schrieb:> Schwer zu debuggen sind nicht atomare Zugriffe zwischen Main und> Interrupt. Wenn der Interrupt genau zwischen 2 Bytes zuschlägt, kriegt> er ein neues und ein altes Byte. Und schon hat er einen Pointer, der in> den Wald zeigt.> Also lieber zuviel atomar kapseln (CLI, Zugriffe, SEI), als einmal zu> wenig.
Von der Programmstruktur her wird nichts von Main an Interrupts
übergeben. Der Datenempfang im Interrupt initialisiert gleichzeitig die
Interrupt-Ausgabe aufs Display. Das gesamte Handling spielt sich in
Interrupts ab die dann höchstens (natürlich vollständige) Daten für Main
zur Verfügung stellen.
> Stimmt. Zu 99,99% sitzt der Fehler vor dem Bildschirm.
Absolut. Und ich hoffe der Fehler ist nicht allzu peinlich. Momentan
hoffe ich hier aber noch drauf ob mir jemand funktionierende
Daten-Breakpoints für AVRxxDx bestätigen kann...
Anton schrieb:> Momentan> hoffe ich hier aber noch drauf ob mir jemand funktionierende> Daten-Breakpoints für AVRxxDx bestätigen kann...
Was spricht gegen einfach ausprobieren?
Oliver
welche Speicherzelle ist das ein Register oder ist das im SRAM. Laß dir
im Simulator das SRAM (in Memory1-Fenster) anzeigen, wenn das während
der Programmausführung immer voller wird, dann fehlt wahrscheinlich in
deinen Programm irgendwo ein ret, reti oder pop. Also entweder beendest
du ein Unterprogramm nicht das mit dem abspeichern einer
Rücksprungadresse angesprungen wird, du beendest einen Interrupt nicht,
welcher ebenfall eine Rücksprungsdresse ablegt oder du pusht aus den
eben genannten Gründen mehr als du popst.
Anton schrieb:> Der Datenempfang im Interrupt initialisiert gleichzeitig die> Interrupt-Ausgabe aufs Display. Das gesamte Handling spielt sich in> Interrupts ab
Das klingt sehr gefährlich. Hast Du bedacht, das Interfaces nicht
reentrant sind, d.h. das Main darf keine weitere Instanz aufmachen.
Ich mache für die UART immer 2 FIFO Interrupts und das Main kümmert sich
um das Parsen und die Ausgabe.
wenn das mit dem Simulator nicht so praktisch ist, da man keinen
Debugger hat. Kannst du in deine Main auch irgendwo einbauen das ein
Speicherzelle im RAM ausgelesen wird die 00 bzw. FF sein sollte und wenn
das abweicht, weil die Speicherzelle erreicht wurde kann du einen LED
einschalten, damit du weist, dass dein SRAM zu Neige geht.
Anton schrieb:> zumal die seriellen Daten über Funk reinkommen
Also brauchst Du erstmal ein ordentliches Protokoll mit CRC, um die
Störungen rauszufiltern.
Thomas O. schrieb:> wenn das während der Programmausführung immer voller wird
Ja es ist ein Bereich im SRAM des Controllers. Der Stackpointer bleibt
artig in seinem Terrain. Beim Debugging kann man ja in den Speicher
schauen. Mit "der Speicherzelle" meinte ich übrigens das 1.Byte des
immergleichen überschriebenen Bereichs.
Peter D. schrieb:> Das klingt sehr gefährlich. Hast Du bedacht, das Interfaces nicht> reentrant sind, d.h. das Main darf keine weitere Instanz aufmachen.
Es ist eine zugegeben komplexe Konstruktion. Aber wiegesagt, die Main
tut hier nichts zur Sache. Es arbeitet auch alles wie es soll und das
dauerhaft. Nur unter bestimmten, eingrenzbaren Bedingungen, die
bestimmtem seriellen Input zur Folge haben kommt es zum Fehler. Danke
für die Überlegungen zum möglichen Fehlerbild aber den Fall in Gänze
aufzudröseln würde das Thema hier vollends sprengen.
Peter D. schrieb:> Also brauchst Du erstmal ein ordentliches Protokoll mit CRC, um die> Störungen rauszufiltern.
Das bietet bereits das verwendete Funkmodul. Darüber hinaus erfolgt
übergeordnet ein Test über die 8Bit Summe aller Daten (Protokoll eines
eDip240-7). Die Daten fließen ja auch plausibel und auf Dauer. Es muss
an einem bestimmten Dateninhalt liegen. Stand meiner Analyse bis jetzt.
Anton schrieb:> Nur unter bestimmten, eingrenzbaren Bedingungen, die> bestimmtem seriellen Input zur Folge haben kommt es zum Fehler.
Klingt dann doch sehr nach einem Problem mit dem Protokoll und einem
Pufferüberlauf.
Die Entwicklung eines fehlertoleranten Protokolls ist keine einfache
Sache. Die Protokollstacks in Industriesteuerungen über RS-485 sind oft
sehr aufwendig und belegen viel Flash.
Peter D. schrieb:> Die Indexschreibweise ist in der Regel weniger fehleranfällig, als die> Pointerschreibweise.
Hier stimme ich nicht zu!
Die beiden Schreibweisen sind äquivalent.
Bieten somit exakt das gleiche Fehlerpotential.
Thomas O. schrieb:> es gibt im AVR-Studio einen Watch Funktion
Das Watch-Fenster wird nur aktualisiert, wenn du auf einem Breakpoint
stehst.
Und dass du auf einen Breakpoint kommst, dafür brauchst du den besagten
Data Breakpoint, der als spezielle Debug-Hardware im Controller
eingebaut ist und in jedem Maschinenzyklus die Pointerregister X
und/oder Y und/oder Z mit der gewünschten Speicheradresse vergleicht und
dann beim Schreiben und/oder Lesen triggert.
Lothar M. schrieb:> Thomas O. schrieb:>> es gibt im AVR-Studio einen Watch Funktion> Das Watch-Fenster wird nur aktualisiert, wenn du auf einem Breakpoint> stehst.
Das ist die Begriffsverwirrung zwischen GDB, der diese Art von
Datenüberwachung (egal ob in Hard- oder Software) schon immer
"watchpoint" genannt hat und den IDEs, die Variablen/Speicherstellen
gern "watchen".
Nach deinen Aussagen habe ich die Befürchtung, dass du in den
Interrupt-Routinen viel zu viel "machen" läßt. In der Regel macht die IR
nur das Nötigste, so dass die Main-Routine weiterkommt. Und bevor du
jetzt mit irgendwelchen Breakpoints herumhampelst, schreib dir doch ein
paar schlaue Testroutinen. Du kannst das Assemblerprogramm doch beliebig
verkleinern und wenn es sein muß jeden "Mist" einzeln testen. Viel Spass
jedenfalls...Rainer
rbx schrieb:> Tja, und wir haben leider keinen Zugriff auf dein Softwaretechnisches> Möglichkeitspotential.> Deswegen kann man eigentlich nur herumstochern,
Nicht unnütz rumstochern rbx, einfach die gestellte Eingangsfrage
beantworten- damit wär ich schon zufrieden ;-)
Rainer V. schrieb:> Nach deinen Aussagen habe ich die Befürchtung, dass du in den> Interrupt-Routinen viel zu viel "machen" läßt. In der Regel
... braucht man hier nichts zur Regel betonieren. Du kannst ein Programm
im Gegenteil auch nur aus Interrupts aufbauen und den Controller in Main
schlafen lassen. Ich habe keinen Hinweis auf ein 'viel zu viel'. Die
Interrupts werden round robin nacheinander abgearbeitet wenn jeweils
fertig. Die serielle E/A funktioniert, von dem speziellen Fehler
abgesehen. Dieses recht interessante Fehlerbild werde ich schon noch
auflösen.
Rainer V. schrieb:> bevor du jetzt mit irgendwelchen Breakpoints herumhampelst
Der Data-Breakpoint wär hier exakt das Mittel der Wahl. Da
offensichtlich noch niemand damit und den neuen AVRs gearbeitet hat bin
ich gespannt, was meine eigene Recherche demnächst ergibt.
Anton schrieb:> Da> offensichtlich noch niemand damit und den neuen AVRs gearbeitet hat bin> ich gespannt, was meine eigene Recherche demnächst ergibt.
OK, ich auch...
Rainer
Anton schrieb:> Der Data-Breakpoint wär hier exakt das Mittel der Wahl.
Pustekuchen.
Der erste Minuspunkt der neuen AVR Architektur. Zumindest im aktuellen
McStudio und mit einem AtmelICE.
Ich verstehe nicht, warum du dich so auf diese Breakpoints versteifst.
Du hättest doch schon längst eine schlaue Testroutine in dein Programm
einbauen können! Etwa so, ich nummeriere meine Unterprogramme durch und
schreibe nach jedem Programmaufruf die Nummer weg und vergleiche den
Inhalt des Speicherbereichs vorher/nachher. Dann hab ich schon mal den
Punkt im Programm wo was passiert. Dann kann man weitersehen...
Rainer
Rainer V. schrieb:> Ich verstehe nicht, warum du dich so auf diese Breakpoints versteifst.
Weil sie für selten und von außen getriggerte Ereignisse, die man
schlecht simulieren kann, die sinnvollste Methode sind?
Die Hersteller bauen die entsprechende Logik nicht umsonst da rein.
Jörg W. schrieb:> Die Hersteller bauen die entsprechende Logik nicht umsonst da rein.
Ja, ist schon klar...sein Controller kann das aber nu mal nicht...
Rainer V. schrieb:> Du hättest doch schon längst eine schlaue Testroutine in dein Programm> einbauen können!
Schlauer Vorschlag Rainer.
Ganz so simpel wie Du Dir das vorstellst ist das bei einem Programm mit
komplexerer Interrupt-Mechanik leider nicht. Da werden nicht einfach
statisch geordnet nacheinander irgendwelche abgeschlossenen Routinen
aufgerufen die man dann abtesten könnte. Was da womit wohin gespeichert
und wie weitergeleitet wird hängt im Programm maßgeblich vom seriellen
Input ab. Eine vertrackte Sache.
Dazu kommt im Moment ein ganz anderes "Problem": Den Fehler überhaupt zu
provozieren. Da wird noch eine Menge Geduld nötig sein.
Anton schrieb:> Dazu kommt im Moment ein ganz anderes "Problem": Den Fehler überhaupt zu> provozieren. Da wird noch eine Menge Geduld nötig sein.
Ja, sorry, du hast ja recht. Aber wenn es keine Debugging-Möglichkeit
gibt, dann ist halt das Elend groß. Natürlich kann ich großspurig
"schlau" schreiben, aber machen muß ichs ja nicht...ich wünsche
jedenfalls einen Erfolg!
Gruß Rainer
Anton schrieb:> Dazu kommt im Moment ein ganz anderes "Problem": Den Fehler überhaupt zu> provozieren.
Naja, vorher kommt noch die spannende Frage, wie gut kommst du
eigentlich mit Linux, dem GDB, Radare oder IDA - läuft auch ganz gut in
Wine - klar?
Rainer V. schrieb:> ich wünsche jedenfalls einen Erfolg!
Danke. Wird schon. Fehlende Breakpoints müssen dann einfach durch
Hartnäckigkeit ausgeglichen werden ;-)
Um mal kurz die Anwendung zu skizzieren: Ein Protokoll für die Anzeige
auf verschiedenen eDIP240-7 soll ins richtige Protokoll für ein
einzelnes uniTFT Display übersetzt und dessen Touchkey-Eingaben zurück
geleitet werden. Das umfasst im Kern:
-Entgegennahme der eDIP240-7 Daten und Quittierung
-Umwandlung in uniTFT Daten
-Ausgabe aufs uniTFT nach dessen Timing und Quittierung
-Einlesen des uniTFT Buffers bei vorhandenen Daten
-Umwandlung ins eDIP240-7 Format und Ausgabe zurück an das alles
steuernde System, hier aber nach dessen aktiver Daten-Anforderung.
um zu überprüfen ob der Stack voll läuft kann man doch in der
Haputschleife einfach nachsehen ob etwas am Anfang des SRAM geändert
wurde.
Der sieht ja normal so aus, geht z.B: von $0060-00DF und wird von unten
her beschrieben. Jetzt schau man halt ab und zu nach ob $0070 verändert
wurde und erkennt eine Hoche Stackbelastung was auf einen baldigen
Überlauf hinweist.
Wenn das also sein kann muss man schauen was man alles anspringt und
nicht wieder zurückspringt. Hier müsste man dann eben schauen ob jeder
Interrupt mit reti und jeder Aufruf(rcall) eines Unterprogramms auch mit
ret beendet wird. Das kann man normal selbst im Simulator mit ein paar
Breakspoints finden wenn man an und ab mal die Interrupts auslöst, man
braucht ja nur das Feld für den Interrupt zu setzen und schon verzweigt
er nach ein paar Takten. Wenn du so ein Unterprogramm nicht mehr verläßt
dann arbeite leiber mit jmp/rjmp anstatt mit call/rcall.
Anton schrieb:> Ganz so simpel wie Du Dir das vorstellst ist das bei einem Programm mit> komplexerer Interrupt-Mechanik leider nicht. Da werden nicht einfach> statisch geordnet nacheinander irgendwelche abgeschlossenen Routinen> aufgerufen die man dann abtesten könnte. Was da womit wohin gespeichert> und wie weitergeleitet wird hängt im Programm maßgeblich vom seriellen> Input ab.
Solche Anwendungen sind mein täglich Brot, aber nicht auf
Mikrocontrollern sondern auf Servern im Backend. Mangels Zugriff auf die
produktiven Maschinen kann ich dort keinen Debugger ansetzen. Außerdem
kann ich wie du auch nicht ewig darauf warten, dass der Fehler zufällig
auftritt während ich zugucke. Selbst wenn ich unendlich Zeit hätte,
kommen die Ereignisse schneller rein, als ich gucken kann.
Also setze ich auf Logfiles. Diese kann ich nach Auftreten des Fehlers
in aller Ruhe analysieren. Das kannst du auch. Gebe auf einem seriellen
Port Log-Meldungen aus, wo die Maschine fortlaufend berichtet, was sie
gerade warum tut. Diese Meldungen sendest du an einen PC, der sie mit
Zeitstempeln anreichert und in eine Datei schreibt.
Bei der Gelegenheit kann man auch zyklisch Hinweise zur
Speicher-Auslastung ausgeben. Das kontrolliere ich immer nach jeder
größeren Änderung, denn Speicher-Lecks die erst nach vielen
Tagen/Wochen/Monaten auftreten sind eklig.
rbx schrieb:> Naja, vorher kommt noch die spannende Frage, wie gut kommst du> eigentlich mit Linux, dem GDB
Hilft aber an der Stelle nix. AVaRICE kann mit diesen Chips noch nicht
umgehen.
Wenn es das Atmel Studio nicht kann, dann hilft das nicht viel, selbst
falls die Hardware es eigentlich könnte.
Thomas O. schrieb:> um zu überprüfen ob der Stack voll läuftAnton schrieb:> Der Stackpointer bleibt> artig in seinem Terrain.Stefan ⛄ F. schrieb:> Hinweise zur> Speicher-Auslastung
Der SRAM-Speicher ist bei weitem nicht ausgelastet.
Beim Debugging kann man sich den Speicher doch anschauen.
Entspricht den Vorgaben im Programm.
Stefan ⛄ F. schrieb:> Gebe auf einem seriellen> Port Log-Meldungen aus, wo die Maschine fortlaufend berichtet, was sie> gerade warum tut.
Das wär viel hier zu grobkörnig. Ich möchte doch nur den kurzen Moment
aufklären an dem etwas falsches an besagten Speicher geschrieben wird
und dann den verusachenden Code samt seiner Eingangsdaten untersuchen.
Anton schrieb:> Ich möchte doch nur den kurzen Moment> aufklären an dem etwas falsches an besagten Speicher geschrieben wird> und dann den verusachenden Code samt seiner Eingangsdaten untersuchen.
Die Betonung liegt wohl auf "nur" und gleichzeitig erzählst du von
absolut sporadischen Fehlern. So wie Stefan vorschlägt, könnte das was
werden. Das "sporadisch" zwingt dich geradezu, den ganzen Verkehr zu
protokollieren. Aber vielleicht fällt dir ja doch was besseres
(schnelleres!) ein...
Rainer
Rainer V. schrieb:> Das "sporadisch" zwingt dich geradezu, den ganzen Verkehr zu> protokollieren.
Nur, wenn man es versuchen will, per Simulation nachzustellen. Dann wäre
das ja die einzige Möglichkeit, um die externen Ereignisse in exakt
gleicher Form reproduzieren zu können.
Nichtsdestrotz hat das Einfügen von Debug-Code oft genug
Timing-Änderungen zur Folge, die u.U. das Problem dann auch gleich mal
komplett maskieren können. Es ist dann nicht weg (das ist die Gefahr),
tritt aber durch das geänderte Timing vielleicht viel seltener auf.
Ich kann mir immer noch nicht vorstellen, dass die Hardware dieser AVRs
das nicht können sollte, habe aber gerade keine Zeit, mir das selbst
anzusehen.
Anton schrieb:> Du kannst ein Programm im Gegenteil auch nur aus Interrupts aufbauen
Huh, diese Art von Programmen sind aus Erfahrung unbeherrschbar. Einmal
am Tag, in der Woche, Monat, Jahr tickt so ein Ding aus oder frisst sich
dank Prioritätsinversion fest. Kann sein, dass man das nicht merkt, aber
es passiert.
> Die Interrupts werden round robin nacheinander abgearbeitet wenn> jeweils fertig.
Eben das ist nicht das Konzept von Interrupts. Sie werden bestenfalls in
deinem Gedankenmodell aber niemals in der realität "zyklisch der Reihe
nach" (= ROUND robin) abgearbeitet. Sondern eben immer dann, wenn sie
dank ihrer Priorität und freier Rechenleistung drankommen. Und wenn ein
externer Signalgeber mit hoher Prio austickt, dann kommt dieser
Interrupt laufend. Bei uns war das z.B. ein Drehgeber in einer
ausgeleierten Maschine, der halt wie blöd hin- und herzappelte. So einen
Fehler hast du in der Firma nie, weil da nur neue Maschinen herumstehen.
Mein ideales µC-Programm ist eine Hauptschleife, die in <1ms durchläuft
und dabei alles macht (ggfs. müssen längere Rechnungen halt in mehrere
Zyklen aufgeteilt werden). Und die kurzen ISR speisen nur neue
Informationen in diese Mainloop ein.
Ein Supervisor (das ist ein besserer Dreizeiler, der einen Hardwaretimer
mit dem vorigen Timerstand vergleicht) überwacht die Laufzeit der
Mainloop und gibt Alarm, wenn die mal stark nach oben ausreißt.
Rainer V. schrieb:> Ich verstehe nicht, warum du dich so auf diese Breakpoints versteifst.
Wer in seinem Prozessor mal tatsächlich die Hardware für echte Data
Breakpoints hatte (wenigstens für einen einzigen), der wird sie
bitterlich vermissen, wenn er ein Problem wie das hier zu knacken hat.
Und wirklich elegant wird es, wenn diese Hardware es hergibt, nur dann
den Breakpoint zu triggern, wenn genau ein bestimmter Wert an eine
bestimmte Adresse geschrieben wird:
https://community.nxp.com/t5/ColdFire-68K-Microcontrollers/Setting-multiple-breakpoints-and-a-program-trace-on-MCF5216-V2/m-p/274479
Zurück zum Thema:
Anton schrieb:> Ich möchte doch nur den kurzen Moment aufklären> an dem etwas falsches an besagten Speicher geschrieben wird
Gibts nich weil is nich.
Du musst also wegkommen vom "kurzen Moment" hin zu statistischen
Methoden: wenn das immmer die selben korrupten 28 Adressen sind, musst
du dort eine Regelmäßigkeit finden.
Du musst dir also etwa solche Fragen ausdenken und die antworten darauf
finden:
Welche Abstände haben die korrupten Bytes? Sind die einezeln, gepaart
oder sonstige Cluster? steht immer der selbe Müll drin? Wann passiert
"es"? Passiert "es" immer an allen Speicherzellen gleichzeitig? Passiert
"es" bei vielen Geräten gleich häufig? Passiert "es" auch, wenn an du
bestimmte Hardwarekomponenten nichts anschließt, also bestimmte
Interruptquellen nicht verwendest?
Was auch etwas Licht ins Dunkel bringen kann ist eine statische Code
Analyse und die kritische Betrachtung der ausgegebenen Stellen.
Aus eigener Erfahrung kann ich sagen dass damit schon einige "Out of
Bound" aufgedeckt wurden.
Anton schrieb:> Das wär viel hier zu grobkörnig. Ich möchte doch nur den kurzen Moment> aufklären an dem etwas falsches an besagten Speicher geschrieben wird> und dann den verusachenden Code samt seiner Eingangsdaten untersuchen.
Tja, und ohne Databreakpoint geht das halt nur über eine ausführliche
Codeanalyse. Und weil du das für das ganze Programm nicht sinnvoll
machen kannst, musst du den Fehlerpunkt einkreisen. Dazu hilft alles,
was dir dabei hilft. Wenn logging dann am veränderten Timing scheitert,
ist dein ganzes Konzept Murks. Murks wegen zu großer Komplexität.
So furchtbar kompliziert klingt die Aufgabenstellung ja jetzt nicht.
Was ich bei deiner ganzen Geschichte allerdings sehr seltsam finde, ist,
daß ein Writepointer von den Eingangsdaten abhängen soll, ohne dass du
sagen kannst, wie, wo, und warum. Das kann doch eigentlich nicht sein.
Wenn’s nur ein klassischer Bufferüberlauf wäre, könntest (oder müsstest)
du einfach JEDEN Bufferzugriff gegen Überlauf absichern.
Oliver
Lothar M. schrieb:> Anton schrieb:>> Du kannst ein Programm im Gegenteil auch nur aus Interrupts aufbauen> Huh, diese Art von Programmen sind aus Erfahrung unbeherrschbar.
Ist auch meine Erfahrung. Man bestiehlt sich nur unnötig um einen
Ausführungslevel, der vieles einfacher macht.
Die zu große Komplexität Deines Programms ist also selbst verschuldet.
Bei den neuen AVRs kann man zwar round robin einstellen, statt fester
Priorität, aber das macht alles nur noch unvorhersehbarer. Man hat ein
völlig unterschiedliches Verhalten bei normaler und bei hoher
Interruptlast.
Einer Mainloop ist dagegen die Interruptlast völlig schnurz. Sie
arbeitet alles immer in der gleichen, von Dir bestimmten Reihenfolge ab
und damit gibt es auch kein Chaos und keine Komplexität.
Das Schlimmste, was passieren kann, eine serielle FIFO läuft voll und
schmeißt die überzähligen Bytes weg. Durch ein Protokoll mit
Paketnummernzähler läßt sich das erkennen und die verlorenen Pakete
können nochmal angefordert werden. Oder man erzeugt erstmal nur ne
Fehlermeldung, daß was im Argen liegt.
Anton schrieb:> Der SRAM-Speicher ist bei weitem nicht ausgelastet.
Warum machst Du dann nicht, was ich bereits vorgeschlagen hatte?
Die Daten der einzelnen Funktionen in freie Bereiche zu verschieben und
dann beobachten, bei welcher Funktion die ungewollten Änderungen mit
wandern.
EAF schrieb:> Die beiden Schreibweisen sind äquivalent.
Rein formal schon.
Nur speziell auf einem 8Bitter macht ein Index vieles einfacher. Ist das
Array <256 Byte, reicht ein Byte als Index und ich muß nichts atomar
kapseln. Auch kann ich den Index einfach auf Überlauf testen (mit sizeof
vergleichen), unterlaufen kann er ja nicht.
Anton schrieb:> Das wär viel hier zu grobkörnig. Ich möchte doch nur den kurzen Moment> aufklären an dem etwas falsches an besagten Speicher geschrieben wird> und dann den verusachenden Code samt seiner Eingangsdaten untersuchen.
Dann füge halt nur an den relevanten Stellen Logmeldungen ein.
Wenn du nicht im laufenden Betrieb hinein schauen kannst, muss er dir
anders mitteilen, was ab geht. Ansonsten bleibt es ein womöglich
erfolgloses Ratespiel.
Jörg W. schrieb:> Nur, wenn man es versuchen will, per Simulation nachzustellen. Dann wäre> das ja die einzige Möglichkeit, um die externen Ereignisse in exakt> gleicher Form reproduzieren zu können.
Das wird sowieso nötig sein, um den Erfolg der Korrektur zu belegen.
Ich darf keine Fehlerkorrektur ohne Nachweis abliefern. Heist im
Umkehrschluss: Wenn der Kunde/Betrieb keine detaillierte Anleitung
liefert, wie man den Fehler auslöst, beginnt meine Arbeit damit, genau
das heraus zu finden. Und zwar bevor ich ich die Fehlerursache suche.
Jörg W. schrieb:> Nichtsdestrotz hat das Einfügen von Debug-Code oft genug> Timing-Änderungen zur Folge, die u.U. das Problem dann auch gleich mal> komplett maskieren können.
Ja, das kann passieren und wäre wirklich bitter wenn es hier passiert.
Dagegen gibt es leider kein Allheilmittel.
Stefan ⛄ F. schrieb:> Das wird sowieso nötig sein, um den Erfolg der Korrektur zu belegen.
In deiner Umgebung offenbar, an anderen Stellen wird es auch genügen,
wenn man beim Debuggen die Ursache des Fehlers sauber erkennen konnte.
Jörg W. schrieb:> an anderen Stellen wird es auch genügen,> wenn man beim Debuggen die Ursache des Fehlers sauber erkennen konnte.
Das würde dann gerne zu zweit machen, um sicher zu gehen.
Wenn Programmierer alleine ihre eigenen Fehler untersuchen, übersehen
sie oft etwas.
Jörg W. schrieb:> Die Hersteller bauen die entsprechende Logik nicht umsonst da rein.
In manchen Fällen macht es Sinn Fehlerstatisiken in eine "fertige"
Software einzubauen um bei Kunden laufende Software auf Qualität zu
überprüfen. Manche Fehler treten sehr selten auf und sind im Labor nicht
zu erfassen, daher ist das Erfassen von Exeptions (z.B. Division durch
Null) und deren Speicherung sinnvoll.
Peter D. schrieb:> Bei den neuen AVRs kann man zwar round robin einstellen, statt fester> Priorität, aber das macht alles nur noch unvorhersehbarer. Man hat ein> völlig unterschiedliches Verhalten bei normaler und bei hoher> Interruptlast.
Ich denke hier werde ich mal als erstes ansetzen und die vorgesehene
Abarbeitung aller Interrupts mit der Wirklichkeit vergleichen, ob es
hier zu Unregelmäßigkeiten kommt bzw. ob einzelne Interrupts womöglich
und unbemerkterweise gar nicht zum Zuge kommen. Danke für die Beiträge
soweit.
Noch unerwähnt blieb die Nutzung des zweiten Interrupt Prio1 Levels der
neuen Controller zur unbedingten Sicherstellung des Empfangs der
Eingangsdaten.
Was die Abwesenheit der Data Breakpoints bei den AVRs erklären könnte
wäre evt. die verminderte Leistungsfähigkeit von UPDI (nur eine Leitung,
bei PDI zwei).
Anton schrieb:> Was die Abwesenheit der Data Breakpoints bei den AVRs erklären könnte> wäre evt. die verminderte Leistungsfähigkeit von UPDI (nur eine Leitung,> bei PDI zwei).
Nö, denn viele ARM Kontroller können es über SWD, das ebenfalls nur zwei
Leitungen hat. Die zwei Leitungen sind kein Hindernis.
Anton schrieb:> bei den AVRs
diesen neueren AVRs war gemeint. Die alten Megas/XMegas beherrschen Data
Breakpoints via PDI ja angeblich (hab ich selber dort noch nie
verwendet).
Stefan ⛄ F. schrieb:> Die alten ATmegas haben kein PDI.
Ja stimmt, lang ists her, aber da standen zum Debugging ja noch mehr
Leitungen zur Verfügung...
Anton schrieb:> Was die Abwesenheit der Data Breakpoints bei den AVRs erklären könnte> wäre evt. die verminderte Leistungsfähigkeit von UPDI (nur eine Leitung,> bei PDI zwei).
Eigentlich nicht. debugWIRE ist auch ein Eindrahtprotokoll. Gut, die
haben tatsächlich keine data breakpoints, aber das lag daran, dass sie
gar keine separate Debug-Hardware in den MCUs vorgesehen hatten.
debugWIRE war schlicht ein Monitor-ROM, also sowas, wie man schon vor
30+ Jahren seine Mikroprozessoren debuggen konnte. Code-Breakpoints
wurden dort durch Umflashen der jeweiligen Page eingefügt. Klingt
gruselig, dafür hat es wiederum gar nicht so schlecht funktioniert.
Ansonsten verursachen data breakpoints ja (im Gegensatz bspw. zu trace
data beim ARM) nicht per se mehr Traffic auf der Debug-Schnittstelle.
Peter D. schrieb:> EAF schrieb:>> Die beiden Schreibweisen sind äquivalent.> Rein formal schon.
Aber in der Praxis sieht das schon spannender aus. Wenn da steht:
erg = *ptr+i*3;
Dann stellt sich die Frage, ob das so sein muss, oder ob das der Grund
für den Fehler ist, den der Kollege gestern kurz vor Feierabend noch
eingebaut hat, obwohl er eigentlich sowas wollte:
erg = *(ptr+i*3);
So ist das eindeutig:
erg = ptr[0]+i*3;
Statt:
erg = ptr[i+3];
Ich arbeit auch mit Logs. Ein kWord als Buffer, wo man periodisch was
reinschreibt und per Protokol ausliest.
Wenn die Vermutung, die Kommunikation sei zustaenig in der luft liegt,
wuerd ich fehlerhafte Kommunikation einspeisen. Erst mal den
Kommunikationsbuffer erhoehen. Und dann immer laengere Meldungen senden.
Bis ans zulaessige Maximum.
Die Meldungen enthalten die Laenge selbst ?
Wie lang darf eine Meldung sein ?
Wird das ueberprueft ?
Was geschieht in diesem Fall, wo die Laenge in der Meldung als zu lange
angegeben wird ?
Wird die Meldung als Block oder byte fuer byte, dh Zustandmaschine,
verarbeitet ?
Anton schrieb:> Immer dasselbe, 28 Bytes, an derselben Stelle wild und quer über zwei> von mehreren Datenpuffern (deren Zeiger hintereinander an die> Senderoutine übergeben werden).
Bei 28 Bytes sollte doch eigentlich herauszukriegen sein, welche Task
diese erzeugt hat.
Peter D. schrieb:> Bei 28 Bytes sollte doch eigentlich herauszukriegen sein, welche Task> diese erzeugt hat.
Das wäre vermutlich nicht mal mit den vermissten Data Breakouts
gelungen.
Nachdem in einem Teil des Hauptprogramms dessen vielverwendetes Register
R16 wie von Zauberhand zuweilen einen unerklärlichen Wert bekam... naja,
der Profi wird sich das Ende der Geschichte denken können. Sowas ist
mindestens so übel in der Analyse wie unübersehbar in den Auswirkungen
und peinlich beim gestehen ;-)
Anton schrieb:> naja,> der Profi wird sich das Ende der Geschichte denken können.
Kennt der Profi nicht auch Circuit- oder System-Simulatoren, die
teilweise sogar auf einem Apple laufen?
Anton schrieb:> Nachdem in einem Teil des Hauptprogramms dessen vielverwendetes Register> R16 wie von Zauberhand zuweilen einen unerklärlichen Wert bekam...
Ja, das ist ein Problem von Assembler, man ist für die Registerrettung
selber zuständig.
Ich hab ~10 Jahre Assembler benutzt, ich weiß also ganz genau, warum ich
auf keinen Fall dahin zurück will. Ich weiß auch, daß der Wechsel schwer
fällt.
In C übernimmt alle Variablenverwaltung der Compiler und der kann das
viel besser als ein Mensch. Z.B. die Overlaytechnik beherrscht er
perfekt, d.h. Nutzung des selben Registers für verschiedene lokale
Variablen. Damit kann man sich in Assembler leicht ein Bein stellen.
Peter D. schrieb:> Ja, das ist ein Problem von Assembler...
Ja, ein besseres Beispiel als meines hättest Du zur Begründung nicht
wählen können. Gutes Programmieren kann aber, insbesondere wenn die
Produktivität (im Hobby) nicht an Nummer 1 steht, sehr viel mehr sein
als das bloße Vermeiden von Unachtsamkeiten. Es ist vielmehr eine
hochindividuelle Angelegenheit: Während der eine die maximale
Gestaltungsfreiheit bei minimalen Mitteln schätzt, liebt der andere
engere, dafür sichere Leitplanken. Viel mehr als mein
Flüchtigkeitsfehler ärgert mich, daß das UPDI Debugging der neuen AVRs
offensichtlich einen Rückschritt gemacht hat. So schön es auch ist,
dafür nur noch einen Pin opfern zu müssen.
Jörg W. schrieb:> Habe ich schon vor Jahren mit dem GDB genutzt, als irgendwelche Studios> das noch lange nicht konnten.
Das finde ich jetzt hochinteressant! Unterstützt der GDB immer
Data-Breakpoints? Sprich: Könnte ich mit dem GDB in der Konsole bei
jedem Debug-Adapter diese Breakpoints verwenden?
Das wäre mal der Ausschlag, sich mit der GDB-Konsole
auseinanderzusetzen. Letzte Jahr habe ich mal mit großer Verzweiflung
danach gesucht, welches Ereignis einen Wert in einem Register gesetzt
hat.
Anton schrieb:> Gutes Programmieren kann aber, insbesondere wenn die> Produktivität (im Hobby) nicht an Nummer 1 steht, sehr viel mehr sein> als das bloße Vermeiden von Unachtsamkeiten.
Naja, im Alter wird man bequemer und behält nicht mehr jedes Detail im
Kopf. Auch hat man noch andere Verpflichtungen und das Hobby bleibt mal
längere Zeit liegen.
Da ist es dann schön, wenn einem der Compiler viele monotone Arbeiten
abnimmt und man sich auf die eigentlichen Programmfunktionen und Abläufe
konzentrieren kann. Man kann dann auch leichter das Programm komplett
umstellen, wenn man in einer Sackgasse gelandet ist.
Anton schrieb:> Während der eine die maximale> Gestaltungsfreiheit bei minimalen Mitteln schätzt
Das kann man aber auch anders sehen. Ich habe unter C mehr Freiheiten,
etwas zu realisieren, was ich mich in Assembler nicht getraut hätte,
weil es zu komplex würde. Z.B. meinen Scheduler möchte ich nicht in
Assembler schreiben, obwohl ich es könnte.
Peter D. schrieb:> Das kann man aber auch anders sehen. Ich habe unter C mehr Freiheiten,> etwas zu realisieren, was ich mich in Assembler nicht getraut hätte,> weil es zu komplex würde. Z.B. meinen Scheduler möchte ich nicht in> Assembler schreiben, obwohl ich es könnte.
Das kann ich gut nachvollziehen.
Aber auch mit Assembler kann man sich an komplexere Dinge herantasten.
Das gelingt durch eine durchdachte funktionelle Grundstruktur und
konsequente Wiederverwendung bewährten Codes (namentlich zur
Schnittstellen-Bedienung). Aber zugegeben: Es braucht schon etwas mehr
Überlegung und Umsicht, fortgeschrittenes Alter ist da womöglich ein
wesentlicher Kontraindikator, ich spür's ja selber ;-)
Walter T. schrieb:> Könnte ich mit dem GDB in der Konsole bei jedem Debug-Adapter diese> Breakpoints verwenden?
Würde mich persönlich auch interessieren.
Walter T. schrieb:> Unterstützt der GDB immer Data-Breakpoints?
Der GDB erst einmal immer, nennt sich "watchpoint".
Inwiefern er es aber in Hardware abbilden (lassen) kann, das braucht
natürlich entsprechende Unterstützung durch die Hardware selbst. Auf dem
PC muss dafür das OS die Möglichkeiten der Hardware nach oben reichen
(habe ich zusammen mit dem Autor von AVRDUDE, Brian Dean, mal vor vielen
Jahren für FreeBSD gebaut, weil ich in irgendwelchem Tcl/Tk-Code nicht
anders eine data corruption zu finden in der Lage war), oder aber eben
bei einem Embedded-Target muss das der entsprechende Adapter zur
Anbdindung ans Target bieten.
Hat man das nicht, wie beschrieben, gibt's einen Software-Watchpoint.
Wenn er es in Hardware abbilden kann, wird es beim Einrichten auch als
"hardware watchpoint" ausgewiesen. Je nach Hardware muss man u.U. mit
etwas Zeiger-Jongliererei herumopern um den zu überwachenden Bereich in
eine Größe zu zwängen, die die Hardware unterstützt (meist nur 2^N Bytes
mit N <= 3 oder so).
Ohne Hardwaresupport muss man sich halt selbst was basteln.
Ich empfehle log Meldungen. An allen Stellen, wo die Variable geändert
wird logge ich "update x from a to b, because ...".
Wenn sich die Variable unerwartet ohne Logmeldung ändert, habe ich
entweder einen Zeiger der aus dem Ruder gelaufen ist, oder einen
Speicherüberlauf. Speicherüberlauf kann man durch zyklische Kontrollen
und Logging erkennen.
Stefan ⛄ F. schrieb:> Ich empfehle log Meldungen.
Wenn das zu langsam ist, "Meldungen" über Bitmuster an freien GPIO-Pins
ausgeben und mit dem LA überwachen.
Stefan ⛄ F. schrieb:> An allen Stellen, wo die Variable geändert wird
Hier ging es aber um Amok im Speicher und keine geordnete Änderung einer
fixen Variable von wohlbekannten Codestellen.
Jörg W. schrieb:> Wenn das zu langsam ist, "Meldungen" über Bitmuster an freien GPIO-Pins> ausgeben
Zum Glück hatte ich zwei freie Pins und ein schnelles Picoscope. Das
ermöglicht zwar immer noch keine Echtzeit-Überwachung einer
Speicherstelle aber man kann sich so immerhin an merkwürdige Aktivitäten
herantasten. Ist der Fehler dann noch so plump wie in meinem Fall fallen
die Tomaten ganz schnell von den Augen.
Stefan ⛄ F. schrieb:> Dann logge hat alle schreibenden Speicherzugriffe, wo Pointer und> Array-Indexe verwendet werden.
Theoretisch ja aber praktisch kann das bei größeren Programmen ganz
schnell einen riesigen Aufwand bedeuten. Hast Du den Log-Speicher nicht
im System wirds noch grauenvoller. Von den Timing-Verlusten ganz
abgesehen. Data-Brakpoints gibts schon nicht ganz umsonst finde ich.
Anton schrieb:> Data-Brakpoints gibts schon nicht ganz umsonst finde ich.
Wenn man keine hat, nützt es nichts, sich welche zu wünschen. Dann muss
man eben nutzen, was da ist.
Stefan ⛄ F. schrieb:> Wenn man keine hat, nützt es nichts, sich welche zu wünschen. Dann muss> man eben nutzen, was da ist.
Da hast Du Recht obwohl ich nach Beitrag 2 von Lothar noch guter Dinge
war. Da hat man schon modernste AVRs, den besten Debugger und die
entsprechenden Menüpunkte im MicrochipStudio- und dann bleibt trotzdem
alles grau.
Anton schrieb:> Data-Brakpoints gibts schon nicht ganz umsonst finde ich.
Manchmal kann man auch anrufen, oder email schreiben. Bei so
Treibergeschichten sind wohl eher Anrufe besser.
..oder eben die Aussschau nach Alternativen, oder nach Möglichkeiten der
Prävention, wie z.B. bei Assembler, eine gute Fehlerliste. Bei C ist die
nicht so nötig, da nimmt man die Probleme eher beim Aufnehmen mit, denn
sonst kommt man ja beim Lernen kaum auf einen grünen Zweig.
GDB und radare sind auch so eine Sache. Besser eine Liste mit
Arbeitstechniken und Befehlen erstellen, und die Liste sollte nicht
allzuweit weg vom Workflow sein.
Auf YT gibt es ein nettes Video vom radare-Entwickler:
https://www.youtube.com/watch?v=fnpBy3wWabA
Anton schrieb:> Ich möchte doch nur den kurzen Moment> aufklären an dem etwas falsches an besagten Speicher geschrieben wird
Ich habe ein ähnliches Problem in einem Windows-Programm gehabt. Für
eine Betriebsdatenerfassung senden viele Maschinen ihre Daten, die in
der Entwicklungsphase zu unerwarteten Fehlern führten. Ein
Windows-Programm stürzt leider bei nicht abgefangenden Fehlern einfach
ab.
Zur Analyse habe ich eine "Trace-Datei" geschrieben, die den Weg des
zyklischen Programms aufzeichnet. Nach jedem Zyklus wird die Datei
gelöscht. Im Fehlerfall sehe ich dann wie weit das Programm fehlerfrei
gelaufen ist.
Die Übertragung der Trace-Aufzeichnung auf den AVR hängt von deinen
Möglichkeiten ab. Als erstes muss die Aufzeichnung im Fehlerfall
gestoppt werden. So ist der letzte Weg durch alle Interrups
festgehalten. Je nach Ausgabemöglichkeit kann man den Trace in einen
intakten RAM-Bereich schreiben oder über eine serielle Schnittstelle
senden.
Ich sehe meistens einen Menübereich im Display vor, in dem ich
Analysedaten ausgebe.
Hermann W. schrieb:> Die Übertragung der Trace-Aufzeichnung auf den AVR hängt von deinen> Möglichkeiten ab. Als erstes muss die Aufzeichnung im Fehlerfall> gestoppt werden. So ist der letzte Weg durch alle Interrups> festgehalten.
Zwischen Windows-Programmen am PC und einem auf kleinen Controller-Chips
liegen Welten. Vielleicht könntest Du genauer erläutern, wie sich der
Programmlauf im Detail, über jeden Befehl und Speicherzugriff
praktikabel aufzeichnen lassen soll. Ich sehe hier höchstens die
Möglichkeit einzelne Wegmarken an verschiedenen Stellen des Programms zu
postieren und von dort den Status zu loggen. Das dürfte bei
verschiedenen Störungen aber oft zu grobkörnig sein, bei meinem
Fehlerbild hier sogar eher verwirrend. Da hilft manchmal
rbx schrieb:> eine gute Fehlerliste
schon besser, wenn man schon nicht alles mehr im Kopf halten kann.
Hermann W. schrieb:> Ich sehe meistens einen Menübereich im Display vor, in dem ich> Analysedaten ausgebe.
Gute Idee. In meinem Fall ist mit der richtigen Display-Ausgabe aber
bereits die beste Rückmeldung vorhanden, es geht ja hier nur um eine
solche.
Anton schrieb:> Ich sehe hier höchstens die Möglichkeit einzelne Wegmarken
Genau das meine ich. Damit ist man zuerst grob an der Fehlerstelle. Man
sieht schon mal die Abfolge der Interrupts. Danach kann man das
schrittweise verfeinern. Bis man am Schluss (wenn es denn sein muss) am
kritischen Assemblerbefehl ist.
Anton schrieb:> In meinem Fall ist mit der richtigen Display-Ausgabe aber> bereits die beste Rückmeldung vorhanden
Das verstehe ich nicht. Die beste Rückmeldung kann es nicht sein, sonst
hättes du die Fehlerstelle. Wichtig ist natürlich vor jedem
Trace-Eintrag den korrupten RAM-Bereich zu prüfen und im Fehlerfall die
Aufzeichnung zu stoppen, damit sicher ist, das bis zum letzten Trace
alles ok ist.
Anton schrieb:> dort den Status zu loggen
?? nichts loggen
Ich meine das so:
-RAM prüfen
-wenn korrupt, Trace stoppen (Stop=1)
-wenn Stop=0 Trace schreiben
Das jeweils erst an groben Wegmarken, dann an der kritischen Stelle
immer feiner. Der Trace endet dann an vor der kritischen Stelle.
Hermann W. schrieb:> Man> sieht schon mal die Abfolge der Interrupts.
Zu diesem Zweck und für Fehler die damit zusammenhängen wirklich gut.
Hermann W. schrieb:> Bis man am Schluss (wenn es denn sein muss) am> kritischen Assemblerbefehl ist
Wenn es diesen einen Befehl aber gar nicht gibt?
Die eine fehlerhafte Codestelle?
Bereits ein Fehler bei der Register-Rettung kann auf einem komplexen
System mit vielen unregelmäßigen Interrupts zu hoffnungslos chaotischen
Fehlerbildern führen. Wenn viele Einflüsse von außen hinzukommen von dem
Programmzweige und Datenzugriffe abhängen bliebe eigentlich nichts
anderes übrig als zu versuchen, dieses Gesamt-System zu "loggen".
Hermann W. schrieb:> Die beste Rückmeldung kann es nicht sein, sonst> hättes du die Fehlerstelle
Insofern die beste Rückmeldung als daß sich quasi alles um die
Display-Ausgabe dreht und dort jedwede Störung sofort ersichtlich wäre.
Anton schrieb:> Wenn viele Einflüsse von außen hinzukommen von dem> Programmzweige und Datenzugriffe abhängen bliebe eigentlich nichts> anderes übrig als zu versuchen, dieses Gesamt-System zu "loggen".
War mein Vorschlag schon vor einiger Zeit. Trotzdem kannst du ja auch
versuchen, sinnvolle Teilprogramme zu "isolieren". Das es einfach gehen
sollte, hat ja keiner gesagt :-)
Gruß Rainer
Anton schrieb:> Wenn es diesen einen Befehl aber gar nicht gibt?
Dann ist es eben kein Programmfehler. Wenn es denn bei der
Registerrettung passiert, dann siehst du ja, dass zu Beginn der
Interruptroutine der Fehler da ist und du weißt, die Registerrettung
geht schief. Dann ist also der Stack nicht richtig dimensioniert.
Hermann W. schrieb:> Ich meine das so:> -RAM prüfen> -wenn korrupt
So einfach ist das an dieser Stelle nicht.
Im Grunde wäre dafür der zulässige Wertebereich jeder RAM-Zelle zu
prüfen.
Der auch wieder zeitabhängig sein kann. Das klingt nicht gerade
praktikabel.
Rainer V. schrieb:> War mein Vorschlag schon vor einiger Zeit.
Nun ja, dafür gilt gleiches. Ein hoher Aufwand.
Rainer V. schrieb:> versuchen, sinnvolle Teilprogramme zu "isolieren".
Klar kann man das. Das würde bei diesem und jenem Fehlerbild ganz
sinnvoll sein. Nur eben lange nicht bei jedem.
Hermann W. schrieb:> Dann ist es eben kein Programmfehler. Wenn es denn bei der> Registerrettung passiert, dann siehst du ja, dass zu Beginn der> Interruptroutine der Fehler da ist und du weißt, die Registerrettung> geht schief. Dann ist also der Stack nicht richtig dimensioniert.
Nein. Ein einzelnes Register nicht bei Eintritt in den Interrupt, wo
es dann wiederverwendet wird zu sichern ist sowohl ein kapitaler
Programmfehler als auch nirgendwo konkret "sichtbar"- außer in
chaotischen Programmläufen. Das hat rein gar nichts mit der
Dimensionierung des Stacks zu tun. Hier hilft nur Augen offenhalten und
solche Dinge schlicht vermeiden.
Anton schrieb:> Im Grunde wäre dafür der zulässige Wertebereich jeder RAM-Zelle zu> prüfen
Kennst du nicht wenigstens den Bereich im RAM, der überschrieben wird?
Dann könntest du den Bereich durch verschieben deiner Variablen frei
halten und dann einfach prüfen, ob was geschrieben wird. Du musst
irgendwie den Fehler erkennen, sonst geht die Methode nicht.
Hermann W. schrieb:> Kennst du nicht wenigstens den Bereich im RAM, der überschrieben wird?
Ja. Der war sogar fix und mit immer den gleichen/ähnlichen Inhalten. Das
kann jedoch bloßer Zufall sein. Ich kann und will das jetzt auch nicht
mehr nachvollziehen nachdem besagter Register-Fehler behoben ist.
Peter D. schrieb:> Anton schrieb:>> Nachdem in einem Teil des Hauptprogramms dessen vielverwendetes Register>> R16 wie von Zauberhand zuweilen einen unerklärlichen Wert bekam...> Ja, das ist ein Problem von Assembler, man ist für die Registerrettung> selber zuständig.
Vor einigen Jahren hab ich mir ein Programm geschrieben, das alle
verwendeten Register auflistet und ob sie gepoppt und gepusht wurden.
Hat mich gewundert, dass es sowas nicht gab, bzw. dass es nicht
verbreitet ist. Bis ich dann ASM endlich hinter mir gelassen habe.
> Ich hab ~10 Jahre Assembler benutzt, ich weiß also ganz genau, warum ich> auf keinen Fall dahin zurück will. Ich weiß auch, daß der Wechsel schwer> fällt.
Peyer schrieb:> Vor einigen Jahren hab ich mir ein Programm geschrieben, das alle> verwendeten Register auflistet und ob sie gepoppt und gepusht wurden.
Das seh ich jetzt (überraschenderweise) eigentlich als geringes Problem
an.
Mit einem Mindestmaß an Übersicht sollte das bei den paar Registern
nicht passieren. So einen blöden Fehler hat man nur einmal, dann ist man
geheilt ;-)
Kritisch ist vor allem, wenn einzelne Programmteile register-unbesehen
von Main nach Interrupt verlagert werden, so wie es mir passiert ist.
eigentlich sollte man wissen welche Register eine Interruptroutine
verwendet, dann pushed und popt man eben nur diese. Ich nehme mir in der
Interruptroutine soviel Zeit und lege die Ergebnisse an eine feste
Stelle im SRAM ab um nicht unnötig Register dafür zu benutzen die ich
später wiederum pushen/poppen muss. Klar wenn man sich unsicher ist kann
man alle 32 Register auf den Stack legen und später wieder herstellen.
Deaktiviertst du die Intterupts wärend du in einer Interruptroutine
bist? Wenn nicht funkt dir vielleicht ein anderer Interrupt dazwischen.
Ich würde auch schauen, das Programm so zu gestallten, dass ein paar
Statusmeldungen ausgegeben werden, wie schon jemand empfohlen hat. z.B.
einen Zahler wie oft ein Interrupt schon aufgetreten ist. Oder sogar
über einige Pins das Auswählbar zu machen also mit einem kleinen DIP
Schalter vorzugeben was du sehen möchtest.
Thomas O. schrieb:> eigentlich sollte man wissen welche Register eine Interruptroutine> verwendet, dann pushed und popt man eben nur diese.
Sowieso.
Hier war aber das Problem daß ein Register nicht gesichert wurde, im
Interrupt verwendet und dann (heimlich) verändert wieder im
Hauptprogramm auftauchte.
Thomas O. schrieb:> Deaktiviertst du die Intterupts wärend du in einer Interruptroutine> bist?
Bei Interrupts mit nur einer Prioritätsstufe braucht man das nicht.
Jeder Interrupt wird vollständig abgearbeitet und bleibt bis reti für
weitere gesperrt. Bei mehreren Prioritätsstufen kann der höhere den
niedrigeren Interrupt unterbrechen. Das wird man nicht verhindern wollen
da die höheren Interrupts ja vom Programmierer nicht ohne Grund
eingesetzt werden. Entweder sind die verwendeten Register dabei wiederum
auf dem Stack zu sichern oder man wählt dafür separate Register.
Anton schrieb:> Da hat man schon modernste AVRs
Nun ja, habe mir die Datenblätter gerade mal angesehen.
UPDI stammt von der vorhergehenden Tiny-Familie (Tiny817 etc.). Tinys
sollen billig sein und dafür macht man den Chip halt klein. Da werden
dann eben auch mal Features "abgespeckt", und so sind für UPDI nur noch
zwei Hardware-Breakpoints beschrieben statt früher (beim JTAG der
klassischen Megas und beim PDI der Xmegas) vier. Für einen data
breakpoint muss man jedoch stets ein Paar Breakpoint-Register benutzen
bei der AVR-Architektur (eins für die Adresse, das zweite für die
Länge). Damit ist klar, dass mit den zwei Breakpoint-Registern des UPDI
keine data breakpoints gehen.
Warum man nun für die dann wieder "Mega" genannte Weiterentwicklung bei
den Limitierungen geblieben ist, werden dir nur Insider verraten können.
Vermutung: so ließ sich durch Weiterbenutzung existierender
Hardwareblöcke Entwicklungszeit sparen, auch die existierenden Tests
lassen sich 1:1 weiter verwenden, genauso die Softwareblöcke im Debugger
des Studios. "Time to market" ist in der Halbleiterei ohnehin schon
immer eine gefühlte Ewigkeit (allein ein Fab-Durchlauf dauert oft schon
ein Vierteljahr), ein paar eingesparte Wochen können da schon relevant
sein und der Grund, auf ein paar seltener genutzte Features zu
verzichten.
Ähnliche "als Mega getarnte Tinys" gab es auch schon zuvor: ATmega88 &
Verwandte. Auch die haben beispielsweise kein JTAG bekommen sondern nur
debugWIRE. Da sie aber andererseits die Multiplikationsbefehle der Megas
bekommen haben, hat man sie wohl auch "Mega" nennen wollen.
Bliebe für den aktuellen Fall noch die Frage, ob man den gleichen Code
auch auf älteren, "klassischen" Megas oder Xmegas laufen lassen könnte,
auf denen man die nötige Hardwareunterstützung hat.
Anton schrieb:> Wenn es diesen einen Befehl aber gar nicht gibt?> Die eine fehlerhafte Codestelle?
Und wenn doch?
Solange man keinen besseren Plan hat, ist der Versuch
erfolgversprechender als gar nichts zu tun.
Jörg W. schrieb:> Bliebe für den aktuellen Fall noch die Frage, ob man den gleichen Code> auch auf älteren, "klassischen" Megas oder Xmegas laufen lassen könnte
Natürlich. Alles eine Frage des Aufwands, was ein neues PCB mit
einschließt.
Die neuen leistungsfähigeren AVRs haben aber schon zuviele Vorteile und
neue Features als daß ich
> Da werden> dann eben auch mal Features "abgespeckt"
für tragfähig halte. Aber was ist das schon anderes als Rumstochern im
Nebel. Es wird schon so sein, der UPDI samt seiner Debug-Hardware hats
nicht drauf, wurde bei seinem zentralen "nur 1 Leitung" Vorteil aber
dennoch von den billigeren Tinys übernommen.
Anton schrieb:> wurde bei seinem zentralen "nur 1 Leitung" Vorteil aber dennoch von den> billigeren Tinys übernommen.
Das muss keineswegs der einzige Vorteil sein. Dazu müsste man sich die
Entscheidungsträger anhören …
Anton schrieb:> Mit einem Mindestmaß an Übersicht sollte das bei den paar Registern> nicht passieren. So einen blöden Fehler hat man nur einmal, dann ist man> geheilt ;-)
Aus Erfahrung kann ich sagen, solche Fehler passieren immer wieder.
Man müßte sich ein Tool schreiben, was solche Fehler aufdecken hilft.
Einen schönen Fehler hatte ich mal, indem ich die Push/Pop Reihenfolge
vertauschte, d.h. 2 Register waren nach dem Interrupt vertauscht. Da
grübelt man auch ewig.
Die Fehlermöglichkeiten in Assembler sind also ungleich höher, als in C.
Es passiert schon mal, daß ein Compiler Fehler macht. Aber der Vorteil
ist, daß Millionen Leute ihn benutzen und solche Fehler schnell
auffallen. Ich meine, auch der WINAVR hatte mal einen Registerfehler.
Peter D. schrieb:> Aus Erfahrung kann ich sagen, solche Fehler passieren immer wieder.
Das vermeidet man durch
Anton schrieb:> eine durchdachte funktionelle Grundstruktur
was z.B. eine Spezialisierung einzelner Register auf bestimmte Aufgaben
bedeuten kann. "Standard" Interrupt-Rümpfe bestehen bei mir
beispielsweise aus einem fixen Register zur Flag-Sicherung und der
Sicherung von bis (alle) 3 PointerRegister in 6 weitere fixe Register.
Braucht man im Interrupt wirklich noch mehr Register sichert man in
diesem i.d.R. besonderen Fall auf den Stack, behält sie in dieser
seltenen Verwendung aber auch im Hinterkopf. Obigen "Verbrauch" von 7
fixen Registern kann man sich leisten da der AVR eigentlich mehr als
genug davon hat. Sinnvollerweise sind das welche mit sonst
eingeschränkter Funktionalität, im Bereich von 3-15.
Gefährlich wirds nur wenn nun einfach irgendwelcher Code in Interrupts
kopiert wurde ohne die Register-Verwendung genau zu prüfen.
Anton schrieb:> welche Möglichkeiten gibt es beim AVR-Debugging, eine konkrete> Speicherstelle zu überwachen und beim Schreiben dorthin mit einem Halt> abzubrechen damit man ermitteln kann welcher Code dazu führte?
Vielleicht so:
Am Anfang als erstes und am Ende (vor reti) aus jedem Interrupt eine
kleine Routine aufrufen. (rcall check_Speicherstelle). Hat sich der
Inhalt geändert, Rücksprungadresse von Stack holen, anzeigen (LCD) und
stop.
Damit würdest du zumindest die Stelle bekommen, wo die Änderung
detektiert wurde. Ist das vorne beim Interrupt, dann hat das
Hauptprogramm die Speicherstelle geändert. Wurde das vor reti entdeckt,
dann hat die Interruptroutine reingeschrieben. Damit könnte man den
Fehler wenigstens eingrenzen und dort weiter suchen. Wäre das ein Weg?
Ozvald K. schrieb:> Am Anfang als erstes und am Ende (vor reti) aus jedem Interrupt eine> kleine Routine aufrufen... Wäre das ein Weg
Ein Anfang allemal. Ausgeschlossen bleiben die Aktivitäten des
Hauptprogramms. Wenn dann aber nur vergessen wurde ein Register des
Hauptprogramms vor Weiterverwendung im Interrupt zu sichern haben wir
eine ganz neue Fehler-Qualität, die sich nicht mit dem Aufspüren einer
bestimmten faulen Code-Stelle bewältigen lässt. Jenes nicht gesicherte
R16 ist in meinem Fall im Hauptprogramm z.B. auch Teil eines Pointers
geworden. Damit sind wilden Schreibereien im SRAM Tür und Tor geöffnet.
Oliver S. schrieb:> Du wolltest maximale Freiheit, du hast sie bekommen.
So schauts aus. Über die Kehrseite einer schönen Medaille darf man sich
nicht beschweren.
Anton schrieb:> So schauts aus. Über die Kehrseite einer schönen Medaille darf man sich> nicht beschweren.
Immerhin konntest du zumindest die Fehlerursache finden. Die
Auswirkungen sind natürlich verhehrend und durch einfaches
"Rückschließen" wohl kaum aufzuspüren. Es hätte aber auch ein
sporadischer, wirklich ekliger Fehler ala Störungen auf irgendwelchen
Pins oder gar auf der Versorgungsspannung sein können! Deshalb kann man
wohl mit Recht sagen, is noch immer joot gejangen :-)
Gruß Rainer
Rainer V. schrieb:> Es hätte aber auch ein sporadischer, wirklich ekliger Fehler ala> Störungen auf irgendwelchen Pins oder gar auf der Versorgungsspannung> sein können!
Vor dem Problem stünden dann alle Programmiersprachen. Meine Erfahrung
mit den AVRs ist aber, daß sie recht robuste, verlässliche Teile sind.
Anton schrieb:> Vor dem Problem stünden dann alle Programmiersprachen
Ja klar...genau genommen jeder beliebiger Controller...Ich hatte gerade
nur im Sinn: ekliger Programmierfehler gegen eklige Hardwareprobleme...
Gruß Rainer
Zwar hätte es hier wenig geholfen, aber für die allgemeine Fehlersuche
bei der Ausführung von Interrupts und grober Einkreisung von
Speicherfehlern hab ich basierend auf der Idee von
Ozvald K. schrieb:> Am Anfang als erstes und am Ende (vor reti) aus jedem Interrupt eine> kleine Routine aufrufen. (rcall check_Speicherstelle). Hat sich der> Inhalt geändert, Rücksprungadresse von Stack holen, anzeigen (LCD) und> stop.> Damit würdest du zumindest die Stelle bekommen, wo die Änderung> detektiert wurde. Ist das vorne beim Interrupt, dann hat das> Hauptprogramm die Speicherstelle geändert. Wurde das vor reti entdeckt,> dann hat die Interruptroutine reingeschrieben. Damit könnte man den> Fehler wenigstens eingrenzen und dort weiter suchen.
jetzt eine kleine AVR128DBxx Routine
1
intlog: lds ZL,LOGP
2
lds ZH,LOGP+1 ;SAVE CURRENT INT NUMBER
3
st Z+,XL
4
lds XL,(MEMADDR)
5
st Z+,XL ;SAVE (MONITORED MEMORY)
6
lds XL,TCA0_SINGLE_CNT
7
lds XH,TCA0_SINGLE_CNT+1
8
st Z+,XL
9
st Z+,XH
10
cpi ZH,$70
11
brne intlog1
12
ldi ZH,$50
13
intlog1: sts LOGP,ZL
14
sts LOGP+1,ZH
15
ret
16
17
.DSEG
18
LOGP: .BYTE 2 ;LOG POINTER
19
20
.org $5000
21
LOGBUF: .BYTE 8192 ;2000H LOG MEMORY
im Einsatz die zusammen mit einem 1 MHz UpCounter in TCA0
(TCA0_SINGLE_CTRLA =7 bei 8MHz) und an Anfang und Ende jedes Interrupts
aufgerufen jetzt folgendes leistet:
- Überwachung einer Speicherstelle in jedem Interrupt und zuvor in Main
- Überwachung welche Interrupts wann auftreten
- Überwachung der Länge der Interrupts und des zuvor durchlaufenen
Main-Abschnitts in ca. 1us Einheiten.
Der hier zugewiesene (Ring)Speicher hätte dann Platz für die letzten
8192/4/2= 1024 Interrupts und kann beim Debugging eingesehen oder
zyklisch nach außen gesendet werden. Eventuell könnte man den gefüllten
Puffer gleich noch im System nach eigenen Kriterien auswerten!
P.S. Logpointer am Anfang noch mit LOGBUF initialisieren und auf die
Sicherung der verwendeten Register im Interrupt achten ;-)