Hallo, ich hab da mal ein Problem. Es ist echt zum verzweifeln.
ich habe ein Programm unter AVR-Studio in C für meinen ATMEGA128, in dem
irgendwann eine Funktion angesprungen wird, die über lese und schreib
char pointer einen Text in einen am Funktionsbeginn definierten Puffer
schreiben soll. Das ganze passiert in einer 'for' Schleife, die z.b. 7
mal durchlaufen wird(also 7 Zeichen kopieren). Den Schleifenzähler (ein
int) setze ich vor der Schleife auf 0. Nun das Problem: Sobald ich im am
Break point in der Zeile des Schleifenbeginns anhalte ist der Zähler auf
0. Wen ich einen Schritt weiter gehe, überspringt er die Schleife und
setzt den Zähler auf 1799.
Ich habe schon die ganze geschichte in eine andere Funktion
nachgeschrieben mit garantiert nur einmalig vorkommenden Variablen und
erhalte den gleichen Effekt.
Hat jemand eine Ahnung was das sein kann? Wofür ist den 1799 eine
typische Zahl?
bin für jeden Hinweis dankbar
Gruß
Christian
stimmt, jetzt sehe ichs :). OK hier das eigentlich triviale
Code-Schnipsel:
int WriteTab(char *value, int row)
{
int m_c;
int m_x;
char m_text[]="Das ist der Text zum Test";
char m_buf[64];
char* bp;
m_x = 5;
m_c = 0;
bp = m_buf;
cli(); // testweise wegen Fehlersuche
for (m_c=0; m_c < m_x; m_c++) //hier ist m_c noch 0. Ein
// Step weiter überspringt die
Schleife
{
*bp = m_text[m_c];
bp++;
}
sei(); //Testweise wegen Fehlersuche
*bp=0; // hier springt er hin und m_c=1799?????
bp++;
//ab hier folgt noch anderer Code
}
Das ist natürlich nur ein Teil des gesamten Programmes und ich kann mir
garnicht vorstellen, dass der Fehler in diesen Zeilen lieg.
Christian
Die komplette Schleife hat eine feste Funktion, die bereits zur
Compilezeit aufgelöst werden kann. Der Optimizer kann also im
Extremfall ermitteln, dass er "Das i" in die ersten 5 Bytes von
m_buf[] schreiben kann, und damit hat sich's.
Trau bitte dem Compiler einfach mal übern Weg und versuch nicht,
viel zu weit vereinfachte Testfälle zu ,,debuggen''.
>Trau bitte dem Compiler einfach mal übern Weg und versuch nicht,>viel zu weit vereinfachte Testfälle zu ,,debuggen''.
Erklärungsversuch von mir:
Wenn die Optimierung eingeschaltet ist kann der Compiler
Schleifenzähler wegoptimieren. Er benutzt dann zum
Beispiel einfach den aktuellen Wert von Pointer bp als
Abbruchbedingung. Die Variable m_c existiert im optimierten
Code einfach nicht mehr, weil sie nicht benötigt wird.
Debuggen sollte man immer OHNE Optimierung.
> Wenn die Optimierung eingeschaltet ist kann der Compiler> Schleifenzähler wegoptimieren.
Hätte ich auch vermutet, aber ich kenne es eigentlich so, daß der
Debugger dann keinen Müll ausgibt, sondern explizit hinschreibt, daß die
Variable an der Stelle wegoptimiert wurde.
> Debuggen sollte man immer OHNE Optimierung.
Die Aussage ist so pauschal falsch. Debuggen sollte man immer den Code,
der nachher auf dem System läuft und nicht irgendwelchen anderen.
Optimierungen bringen oft noch einige Fehler im Code hervor, die man
ohne Optimierungen gar nicht erkennt. Daher muß das Programm auf jeden
Fall mit den Einstellungen gedebuggt werden, mit denen es auch nachher
laufen soll. Im ersten Schritt kann man aber - sofern das Programm so
überhaupt sinnvoll läuft (Stichwort Timing) - auch ohne Optimierungen
debuggen, da das einfacher ist.
Rolf Magnus wrote:
> Hätte ich auch vermutet, aber ich kenne es eigentlich so, daß der> Debugger dann keinen Müll ausgibt, sondern explizit hinschreibt, daß die> Variable an der Stelle wegoptimiert wurde.
Wenn es die Variable gar nicht mehr gibt, bekommt sie auch keine
Debuginformation, sodass der Debugger sie nicht anzeigen kann.
Was aber passieren kann ist, dass die Variable an anderer Stelle
noch benutzt wird und zum Beispiel auf ein Register(paar) gemappt
ist. Dann zeigt der Debugger den Inhalt des Registers als
vermeintliche Variable an dieser Stelle an, was auch immer gerade
drin steht -- der Compiler kann (und sollte) an dieser Stelle die
entsprechenden Register ja freizügig für andere Dinge benutzen.
Es gibt glaub ich Ansätze, die Debuginformation nur für Teile einer
Funktion als zutreffend zu deklarieren, aber 1.) ist das meines
Wissens selbst für die mainstream targets des GCC noch in den
Anfängen, und 2.) denke ich, dass es noch ein Weilchen dauer wird,
bis auch ein Debugger wie AVR Studio derartige Features dann
unterstützen wird.
vielen Dank erstmal für die Diskussion,
ich habe das Problem im Moment nicht mehr, da ich anderen Code auslasse,
d.h. wenn ich den problematischen Funktionsaufruf zu Begin des
Programmes ausführe läuft es fehlerfrei. So nach ca. 10 Zeilen
verschiedenster Initialisierungs routinen Aufrufe lässt sich die
Funktion nicht mehr aufrufen. Scheint mir so, als würde ich irgendwas
innerhalb dieser Routinen anrichten, was dann an anderer Stelle zum
Problem führt. Die jetzige Lösung ist also recht unsicher, da ich ja die
Ursache sicher nicht beseitigt habe.
Ich hab bei der Gelegenheit mal ne sicher recht einfache Frage, zu der
ich aber keine Antwort finden kann. Wie ist denn der Speicher im
ATMEGA128 organisiert (adressmäßig) und wie kann ich im AVRStudio die
entsprechende Speichernutzung anzeigen lassen. Jeder scheint es zu tun
und es ist wohl so einfach, dass keiner darüber spricht wie es
geht...ich kann es nicht finden.
Christian Rothe
Christian Rothe wrote:
> ich aber keine Antwort finden kann. Wie ist denn der Speicher im> ATMEGA128 organisiert (adressmäßig) und wie kann ich im AVRStudio die> entsprechende Speichernutzung anzeigen lassen. Jeder scheint es zu tun> und es ist wohl so einfach,
Nö.
> dass keiner darüber spricht wie es
Das hat einen einfachen Grund: Es interessiert praktisch nie.
Das ist das Bier des Compilers wie er den Speicher einsetzt, was
soll ich mich da einmischen.
Natürlich gibts Ausnahmen: Wenn der Speicher so voll wird, dass
der Heap in den Stack wandert, dann gibts Ärger. Aber abgesehen
davon, soll der Compiler die Variablen in den Speicher legen
wie es ihmam besten in den Kram passt. Ist mir relativ egal
wie er das macht.
Also auch wenn es nervt, ich komme hier nicht weiter. folgender Code:
int WriteTab(char *value)
{
int l=0; //hier ist l=3341
int sz; //hier ist sz=2814
//string length without EOS
l=strlen(value) - 1; //hier wird l=4, das stimmt
sz=0; //hier wird sz=3314 HAARERAUF...
//was ist denn da los???
//if there is enough space at dbuf0
if (dbwp0+l < dbuf0+TextBufSize)
{
//write string to buffer
while(1<l)
{
*dbwp0=value[sz];
dbwp0++;
// sz++;
}
//add string end '0'
*dbwp0 = 0;
dbwp0++;
}
else
//return error
return 0;
//return OK
return 1;
}
Was passiert denn mit der Variablen sz? Ich habe Eure Hinweise ja
schonmal beachtet und deshalb sz von jeglicher Funktion befreit,
abgesehen von deklaration und initialisierung. Es passiert jetzt
unabhängig von der Schleife. Ist auch keine 'for' Schleife mehr und
mittlerweile sogar eine Endlosschleife zum Test. Es geht nix.
Oh wei, ich scrolle gerade im Fenster mit den Build Textausgaben.
Da Steht:
AVR Memory Usage
----------------
Device: atmega128
Program: 18400 bytes (14.0% Full)
(.text + .data + .bootloader)
Data: 17628 bytes (430.4% Full)
(.data + .bss + .noinit)
EEPROM: 510 bytes (12.5% Full)
(.eeprom)
Build succeeded with 0 Warnings...
430% im Data Speicher ist wohl etwas viel, sehe ich das richtig??
Christian
Uebrigens, while(1<l) ist eine eher ungluecklich gewaehlte
Abbruchbedingung. Ich musste drei Mal hinschauen um zu sehen, dass da 1
< length steht (1 als Zahl).
Variablennamen darfst du ruhig aussagekraeftig machen, denn die Namen
tauchen im kompilierten Programm sowieso nicht mehr auf und belegen
daher keinen Speicher. Variablen wie dbwp0, dbuf0, sz und eben l
(kleines L) sind nicht aussagekraeftig genug.
Hi Christoph,
nein, wie gesaget wunderts mich seit ich das gesehen habe nicht mehr.
Ich habe dieses Output-Fenster so klein, dass ich nur immer 'Build
succeeded with 0 Warnings...' gesehen habe. Das fand ich soweit gut, was
offensichtlich aber nicht ausreichend Information war.
Zum Code muss ich sagen, die Schleife mit dem kleinen 'l' sah natürlich
ursprünglich anders aus. Während der Fehlersuche hab ich Stück für Stück
an Code reduziert. Hier ist ja garkeine sinnvolle Funktion mehr drinnen.
Zur Schreibweise muss ich von mir sagen, ich stehe hal auf kurzen Code.
Wenn es der eigene ist finde ich das übersichtlicher. Das sieht bei mir
z.B. so aus:
/////////////////////////////////////////////////////////////
// WriteFifo
/////////////////////////////////////////////////////////////
int WriteFifo0(char c)
{
if (FFLG & FF0)
return 0; //fifo is full
*wp0 = (char*)c;
wp0++;
if (wp0==(fifo0+FSIZE)) //reset wp at fifo end
{
wp0=fifo0;
FFLG |= CA0; //set CARRY0 flag
}
FFLG |= DATAV0; //set Data available flag
if((wp0 == rp0)&&(FFLG & CA0))
FFLG |= FF0; //set fifo full flag
return 1;
}
Ich brauche immer viel Code-Inhalt auf einen Blick, weshalb mich die
ganzen Entwicklungsumgebungen mit zwölf gleichzeitigen Fenstern nerven.
Mit einem 24" Bildschirm wärs vielleicht auch für mich toll, hab aber
halt keinen.
So, nun also danke an Alle für die Mühe. Jetzt muss ich den Controller
erstmal mit RAM erweitern
frohes Schaffen
Christian Rothe
www.rabenkopf-studio.de