Forum: Compiler & IDEs WinAVR zickt: Array wird von alleine gefüllt


von Ron (Gast)


Lesenswert?

Hallo

Ich habe ein merkwürdiges Problem:
Ich habe neben vielem anderen ein uint8_t Array. Das Initialisisere ich 
mit 0en und schreibe dann in einzelne Felder eine 1
Jetzt nutze ich das Array linear und prüfe, ob eine 1 oder 0 steht. Wenn 
0, dann zähle ich den Feldzeiger weiter hoch, bis eine 1 gefunden ist. 
Das funktioniert auch eine Weile, aber irgendwann zwischendrin wird das 
ganze Array wie von Zauberhand gefüllt (mit 55)

Befremdlich für mich daran ist, das es mit einem ca. 2 Jahre alten 
WinAVR (Version habe ich jetzt nicht mehr im Kopf) compiliert keine 
Probleme gibt, nur mit dem, was ich gerade frisch eingespielt habe. Am 
makefile habe ich nichts gerüttelt.

Kann irgendwer vermuten, was dafür die Ursache sein kann?

von Uhu U. (uhu)


Lesenswert?

Könnte das irgend ein Adressierfehler sein? Wird vielleicht igend ein 
uninitalisierter Pointer benutzt?

Solche Effekte können auch vom benutzten Compiler abhängen, je nachdem, 
was der jeweilige Code für Garbage hinterläßt.

Tipp zum fangen des Übeltäters: Wenn der Prozessor wenigstens einen 
Hardware-Breakpoint unterstützt, kannst du feststellen, 'wer' die 55 in 
dein Array geschrieben hat.

Solche Probleme zu finden, kann jedoch beliebig schwer sein...

von Ron (Gast)


Lesenswert?

>Könnte das irgend ein Adressierfehler sein? Wird vielleicht igend ein
>uninitalisierter Pointer benutzt?

Pointer nutze ich nur wenig. Primär, um Strings aus dem EEPROM zu laden 
und für Textausgaben auf das LCD.

>Solche Effekte können auch vom benutzten Compiler abhängen, je nachdem,
>was der jeweilige Code für Garbage hinterläßt.

Das verstehe ich nicht, was Du damit meinst.

>Tipp zum fangen des Übeltäters: Wenn der Prozessor wenigstens einen
>Hardware-Breakpoint unterstützt, kannst du feststellen, 'wer' die 55 in
>dein Array geschrieben hat.

Ich muß leider gestehen, daß ich vom "richtigen" Debuggen keine Ahnung 
habe. Ich mach das nach Try and Error und mit vielen Ausgaben auf meinem 
LCD.

>Solche Probleme zu finden, kann jedoch beliebig schwer sein...

Ja, hat mich gestern schon alleine einen Tag gekostet herauszufinden, 
daß ich da wohl nicht ganz alleine Schuld d'ran bin, sondern es mit dem 
Compiler zusammenhängen muß. Habe verzweifelt in meinem Code erst mal 
suchen müssen, an welcher Stelle überhaupt die Ursache liegt.

Jetzt habe ich noch mal avr-gcc 3.4.6 (2006) installiert und siehe: nach 
dem compilieren läuft es einwandfrei. Nur der Code, den der aktuelle 
4.1.2 produzietr macht den Fehler.
Jetzt bin ich natürlich am Grübeln, wo da meine Schuld liegt...

von Jens (Gast)


Lesenswert?

>Kann irgendwer vermuten, was dafür die Ursache sein kann?

Vermutlich wieder irgendein Geraffel im Compiler. Wäre nicht das erste 
mal, dass bei der "Weiterentwicklung" von avrgcc eher 3 Schritte 
rückwärts gegangen wurde.

von Andreas K. (a-k)


Lesenswert?

Wenn zwei Compilerversionen unterschiedliche Ergebnisse bringen kann es 
natürlich am Compiler liegen. Nur sind ausserhalb spezieller Foren 99% 
aller Postings zum Thema Compilerfehler letztlich Anwenderfehler. Und so 
kann hier auch daran liegen, dass die neue Version korrekten aber 
anderen Code erzeugt, der einen vorher vorborgen gebliebenen 
Programmfehler aufdeckt.

Überschriebener Speicher ist auch häufig ein Stack-Problem. Nur steht 
dann selten 55 drin.

Alles weitere ist jetzt mangels Information reines Kaffeesatzlesen.

von Andreas K. (a-k)


Lesenswert?

> Vermutlich wieder irgendein Geraffel im Compiler.

Wie oft bist du schon über Compilerfehler gestolpert? Also welche die 
nachweislich am Compiler lagen, nicht bloss von der Art "es muss der 
Compiler sein, denn der Code ist zigmal von mir kontrolliert 100% 
korrekt".

von Uhu U. (uhu)


Lesenswert?

Ron wrote:
>>Könnte das irgend ein Adressierfehler sein? Wird vielleicht igend ein
>>uninitalisierter Pointer benutzt?
>
> Pointer nutze ich nur wenig. Primär, um Strings aus dem EEPROM zu laden
> und für Textausgaben auf das LCD.

Das ist auch nicht unbedingt nötig: Der Compiler ist clever genug, sowas 
im Hintergrund zu machen, um effizienteren Code zu generieren.

>
>>Solche Effekte können auch vom benutzten Compiler abhängen, je nachdem,
>>was der jeweilige Code für Garbage hinterläßt.
>
> Das verstehe ich nicht, was Du damit meinst.

Garbage nennt man den Datenmüll, der im Speicher von einer früheren 
Nutzung zurückbleibt.

Angenommen, du rufst zuerst eine Funktion f1() auf, und anschließend als 
nächsten Befehl die Funktion f2(). Dann passiert folgendes:

Nach dem Aufruf von f1() (per call-Maschinenbefehl) reserviert der 
Prolog von f1() so viel Speicher auf dem Stack, wie f1() für seine 
lokalen Variablen braucht. Dann macht f1() irgendwas und schreibt Daten 
in seine lokalen Variablen.

Bevor f1() (mit dem ret-Maschinenbefehl) an den Befehl nach dem call 
f1() zurückkehrt, läuft der Epilog von f1() ab. Er ist u.a. dafür 
verantwortlich, daß der für f1() reservierte Stackbereich wieder 
freigegeben wird. In aller Regel überschreibt der Epilog diese Daten 
jedoch nicht, sondern läßt einfach den Garbage stehen.

Nun wird f2() aufgerufen, und dessen Prolog reserviert ab derselben 
Adresse, ab der der von f1() vorher reserviert hatte, den lokalen 
Speicher für f2().

Da in diesem Bereich aber noch der Garbage von f1() steht, hat ein 
Lesezugriff auf eine lokale uninitialisierte Variable in f2() den 
Effekt, daß auf Garbage von f1() zugegriffen wird.

Das heißt: Wenn man vergißt, irgendeine Variable zu initialisieren und 
dort zufällig immer was 'gutartiges' drinsteht - also die Benutzung von 
Datenmüll zufällig keine gravierenden Folgen hat, wird man den Fehler 
häufig garnicht entdecken.

Wenn man nun aber das Programm mit einer anderen Compilerversion 
übersetzt, kann es passieren, daß z.B. das Layout der Stackframes von 
f1() und/oder f2() sich ändert und dadurch plötzlich 'bösartiger' 
Datenmüll in der Variablen steht und das Programm dann mehr oder weniger 
schnell abschmiert - meist weniger -, oder völligen Unsinn berechnet.

Der Quelltext hat sich dabei absolut nicht geändert...

Solche Überraschungen kann man übrigens auch erleben, wenn man ein 
PC-Programm von einer Windows-Version auf eine andere bringt. Dazu muß 
man es nochnichteinmal neu compilieren...

Prolog und Epilog einer Funktion werden vom Compiler automatisch 
generiert. Sie bestehen meist nur aus wenigen Maschinenbefehlen.
>
>>Tipp zum fangen des Übeltäters: Wenn der Prozessor wenigstens einen
>>Hardware-Breakpoint unterstützt, kannst du feststellen, 'wer' die 55 in
>>dein Array geschrieben hat.
>
> Ich muß leider gestehen, daß ich vom "richtigen" Debuggen keine Ahnung
> habe. Ich mach das nach Try and Error und mit vielen Ausgaben auf meinem
> LCD.

Das ist keine sehr effiziente Methode. Befasse dich mit dem Debugger. 
Das spart viel, viel Zeit und außerdem lernt man dabei viel schneller, 
als bei der Testausgabemethode, was eigentlich im Rechner abläuft.

> Jetzt bin ich natürlich am Grübeln, wo da meine Schuld liegt...

Grübel hilft nichts.

Du solltest dir aber auf jeden Fall deinen Code sehr genau daraufhin 
ansehen, ob irgendwo der Inhalt von Variablen benutzt wird, ohne daß sie 
vorher initialisiert wurden.

Eine gute Idee ist es auch, den höchsten Warnungslevel des Compilers zu 
aktivieren - viele Compiler geben dann Warnungen bei der Benutzung 
uninitialisierter Variablen aus.

von Jan Mayer (Gast)


Lesenswert?

Hallo,

sorry das ich mich hier als Neuling reinklinke:

Uhu Uhuhu: "Eine gute Idee ist es auch, den höchsten Warnungslevel des 
Compilers zu aktivieren"

Kann man das beim AVR Studio irgendwo einstellen? Habe nämlich noch 
Platz auf meiner To-Do Liste ;=)

Danke und lasst euch nicht stören,

Jan

von Ron (Gast)


Lesenswert?

Uninitialisierte Variablen in C? AFAIK sind die doch bei avr-gcc immer 
initialisiert - meine so was mal gelesen zu haben. Ich will jetzt keinen 
Eid d'auf schwören, denke aber trotzdem stets einen Wert 
reinzuschreiben, bevor ich das erste mal lese.

JTAG steht auch bei mir auf der Wunschliste. Aber bisher ging es auch 
ohne und mich stört daran, daß man dann mehr Leitungen vom µC auf einen 
Stecker legen muß, als für ISP nötig (zugegeben: kein gutes Argument)

von Uhu U. (uhu)


Lesenswert?

Ron wrote:
> Uninitialisierte Variablen in C? AFAIK sind die doch bei avr-gcc immer
> initialisiert - meine so was mal gelesen zu haben.

Nein, es Standard bei C, das nicht zu tun - aber du kannst es ja ganz 
einfach ausprobieren; auch ohne Debugger.

In C++ kann man sowas im Konstruktor einer Klasse explizit manchen, aber 
die primitiven Datentypen werden auch dort nicht automatisch 
initialisiert.

> Ich will jetzt keinen Eid d'auf schwören, denke aber trotzdem stets einen
> Wert reinzuschreiben, bevor ich das erste mal lese.

Wir sind hier nicht vor Gericht und Prozessoren sind unbestechlich und 
rechthaberisch...

@ Jan Mayer:
> Kann man das beim AVR Studio irgendwo einstellen? Habe nämlich noch
> Platz auf meiner To-Do Liste ;=)

Das weiß ich leider nicht - ich habe zwar schonmal ein Programm 
geschrieben, aber nicht mit gcc ;-)

Sieh dir einfach mal die Optionsliste des Compilers durch, dort wirst du 
sicherlich fündig...

von Philipp B. (philipp_burch)


Lesenswert?

> Uhu Uhuhu: "Eine gute Idee ist es auch, den höchsten Warnungslevel des
> Compilers zu aktivieren"
>
> Kann man das beim AVR Studio irgendwo einstellen? Habe nämlich noch
> Platz auf meiner To-Do Liste ;=)

Gibt's noch mehr Warnungen als mit -Wall?

von Rolf Magnus (Gast)


Lesenswert?

>> AFAIK sind die doch bei avr-gcc immer initialisiert - meine so was mal
>> gelesen zu haben.
>
> Nein, es Standard bei C, das nicht zu tun - aber du kannst es ja ganz
> einfach ausprobieren; auch ohne Debugger.

Kommt auf die Variable an. Wenn sie global oder static ist, dann 
schreibt die C-Norm eine implizite Initialisierung mit 0 vor. Für andere 
Variablen ist es nicht vorgeschrieben, aber auch nicht verboten. GCC tut 
es da aus Effizienzgründen nicht, warnt aber bei entsprechender 
Warnungs-Einstellung, wenn man eine Variable ausliest, bevor sie das 
erste Mal geschrieben wurde.

> JTAG steht auch bei mir auf der Wunschliste. Aber bisher ging es auch
> ohne und mich stört daran, daß man dann mehr Leitungen vom µC auf einen
> Stecker legen muß, als für ISP nötig (zugegeben: kein gutes Argument)

Da wäre DebugWire interessant. Da geschieht die ganze Programmierung und 
das Debugging komplett über einen einzelnen Pin, der sogar schon auf dem 
ISP-Stecker vorhanden ist, nämlich den RESET-Pin.

> Gibt's noch mehr Warnungen als mit -Wall?

Ja, eine Menge. Ich arbeite fast immer mit -ansi -pedantic -Wall -Wextra 
als Minimum.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ron wrote:

> JTAG steht auch bei mir auf der Wunschliste. Aber bisher ging es auch
> ohne und mich stört daran, daß man dann mehr Leitungen vom µC auf einen
> Stecker legen muß, als für ISP nötig (zugegeben: kein gutes Argument)

Gar kein Argument. ;-)  JTAG braucht unbedingt: Masse, TCK, TDI, TDO
und TMS.  ISP braucht unbedingt: Masse, /RESET, SCK, MISO, MISO.  Sind
gleich viel. :)  Beide brauchen außerdem noch VTarget zwingend, wenn
man mit den originalen Atmel-Geräten (AVRISP, JTAG ICE) arbeiten will,
wobei die Spannung benutzt wird, um den Levelshiftern die entsprechende
Information mitzugeben und um festzustellen, ob das Target überhaupt
eingeschaltet ist.  Bei JTAG würde /RESET (nTRST) nur optional benötigt,
um eine Applikation über JTAG neu flashen zu können, die das JTD-Bit
setzt.  Wenn man das nicht hat/braucht, kann man diese Leitung 
weglassen.

von Simon K. (simon) Benutzerseite


Lesenswert?

Jörg Wunsch wrote:
> Ron wrote:
>
>> JTAG steht auch bei mir auf der Wunschliste. Aber bisher ging es auch
>> ohne und mich stört daran, daß man dann mehr Leitungen vom µC auf einen
>> Stecker legen muß, als für ISP nötig (zugegeben: kein gutes Argument)
>
> Gar kein Argument. ;-)  JTAG braucht unbedingt: Masse, TCK, TDI, TDO
> und TMS.  ISP braucht unbedingt: Masse, /RESET, SCK, MISO, MISO.  Sind
> gleich viel. :)  Beide brauchen außerdem noch VTarget zwingend, wenn
> man mit den originalen Atmel-Geräten (AVRISP, JTAG ICE) arbeiten will,
> wobei die Spannung benutzt wird, um den Levelshiftern die entsprechende
> Information mitzugeben und um festzustellen, ob das Target überhaupt
> eingeschaltet ist.  Bei JTAG würde /RESET (nTRST) nur optional benötigt,
> um eine Applikation über JTAG neu flashen zu können, die das JTD-Bit
> setzt.  Wenn man das nicht hat/braucht, kann man diese Leitung
> weglassen.

Ui, das klingt interessant. Mit dem nTRST kann man ja dann sogar die 
JTAG Pins anderweitig belegen.

Gibts auch eine offizielle Pfostenstecker-Belegung für JTAG von Atmel? 
An meinem mkII sind vorne nur 10pol Wannenstecker dran - etwas dick 
eventuell :|

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Simon Küppers wrote:

> Ui, das klingt interessant. Mit dem nTRST kann man ja dann sogar die
> JTAG Pins anderweitig belegen.

Allerdings kannst du natürlich nicht mehr (sinnvoll) debuggen damit.
Sowie die Applikation das JTD setzt, ist das JTAG-Interface tot.
Kann man eigentlich nur nehmen, um via JTAG eine Firmware reinzubringen,
die dann kein JTD mehr setzt und diese debuggen.

> Gibts auch eine offizielle Pfostenstecker-Belegung für JTAG von Atmel?
> An meinem mkII sind vorne nur 10pol Wannenstecker dran - etwas dick
> eventuell :|

Die 10polige Belegung ist die offizielle Atmel-Variante.  Sie
beinhaltet außer den genannten 7 Leitungen noch nSRST (kompatibel
mit JTAG, aber von Atmel nicht genutzt, wenn ich das recht verstanden
haben), ein zweites GND sowie eine Versorgungsleitung, aus der man
wohl theoretisch die JTAG-Hardware versorgen könnte.

Vielleicht skalierst du das ja auf irgeneinen mechanisch kleineren
Stecker runter und nimmst einen Adapter.

von Ron (Gast)


Lesenswert?

> Gar kein Argument. ;-)  JTAG braucht unbedingt: Masse, TCK, TDI, TDO
> und TMS.  ISP braucht unbedingt: Masse, /RESET, SCK, MISO, MISO.  Sind
> gleich viel. :)

Ja, aber eben andere. So daß man zwei Stecker haben muß... Aber wie 
gesagt: ist ja auf meiner Wunschliste und irgendwann werde ich mir den 
hier wohl mal nachbauen: http://aquaticus.info/jtag

Bis dahin habe ich noch mal dem compiler alle Warnmeldungen aktiviert 
und bekomme bis auf eine nicht mehr ausgespuckt. Na mal sehen, wann ich 
dem mystischen Array auf die Schliche komme und bis dahin tut's die alte 
WinAVR Version.

>Kommt auf die Variable an. Wenn sie global oder static ist, dann
>schreibt die C-Norm eine implizite Initialisierung mit 0 vor. Für andere
>Variablen ist es nicht vorgeschrieben, aber auch nicht verboten.

Genau das war's was ich mal gelesen hatte.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Ja, aber eben andere. So daß man zwei Stecker haben muß...

Nö, wenn du JTAG hast, brauchst du kein ISP.  Programmieren via JTAG
geht sowieso drastisch schneller als via ISP.

> irgendwann werde ich mir den
> hier wohl mal nachbauen: http://aquaticus.info/jtag

Lohnt nicht mehr wirklich.  Du bekommst keinen aktuellen AVR mehr
debuggt damit.  Kauf dir entweder einen AVR Dragon oder sieh' zu,
dass du noch so'n Promo-Bundle mit STK500 und JTAG ICE mkII bekommen
kannst.  Letzteres wäre die Wahl für CPUs mit mehr als 32 KiB ROM.

von Andreas K. (a-k)


Lesenswert?

> Lohnt nicht mehr wirklich.  Du bekommst keinen aktuellen AVR mehr
> debuggt damit.

Kommt auf den Preis an. Selbstgebaut kostet so ein Teil fast nichts. Und 
obzwar man Bauteile der neueren Generation damit nicht debuggen kann: 
Man kann ein Projekt anfangs mit passenden Controllern beginnen, ein 
Mega32 ist ja kein Exot. Wenn man das soweit hat, dass man Funktionen 
oder Speicherkapazitäten benötigt, die damit nicht mehr handhabbar sind, 
dann braucht man meist keinen Debugger mehr.

von Ron (Gast)


Lesenswert?

JTAG ICE mkII ist mir dann doch zu abgedreht teuer und mit dem Drachen 
kann ich meine 644er nicht beackern. Da dann doch lieber die LowCost DIY 
Lösung.

>Nö, wenn du JTAG hast, brauchst du kein ISP.

Stimmt, hatte ich übersehen. Macht die Sache natürlich noch 
interessanter und schiebt sie auf meiner Wunschliste weiter nach oben. 
Also mal so einen "schrägen" Quarz kaufen.

von Simon K. (simon) Benutzerseite


Lesenswert?

Wie schon erwähnt, den JTAGICE mkII gabs vor kurzem zusammen mit einem 
STK500 für 150€ (Da habe ich auch mal zugeschlagen, da ich den mkII für 
mein AVR32-Board brauchte ;))

von Uhu U. (uhu)


Lesenswert?

Ron wrote:
>>Kommt auf die Variable an. Wenn sie global oder static ist, dann
>>schreibt die C-Norm eine implizite Initialisierung mit 0 vor. Für andere
>>Variablen ist es nicht vorgeschrieben, aber auch nicht verboten.
>
> Genau das war's was ich mal gelesen hatte.

Statische Variable sind in dem Zusammenhang nicht das Problem. Das 
'Leben' im Rechner spielt sich im wesentlichen auf dem Stack ab und dort 
wird eben üblicherweise nichts automatisch initialisiert.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ron wrote:

> JTAG ICE mkII ist mir dann doch zu abgedreht teuer

Daher ja der Hinweis auf die Sonderangebote.

> und mit dem Drachen
> kann ich meine 644er nicht beackern. Da dann doch lieber die LowCost DIY
> Lösung.

Und was macht die mit dem 644er?  Die kann ihn nichtmal flashen, was
der Drachen noch könnte.  Mit dem Drachen könntest du sogar den
644er debuggen, wenn du dich auf 32 KiB beschränkst.

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.