Guten Morgen!
Ich benutze Freescale Codewarrior 6.2 und programmiere einen
MC9S08-Controller in C.
Für ein Programm möchte ich globale Variablen benutzen.
Mein Vorgehen war folgendes:
1
volatileintGLOBALE_VARIABLE;
2
3
voidmain(void){
4
intLOKALE_VARIABLE;
5
//... beliebiger Code
6
}
Leider funktionierte mein Code nicht wie gewünscht. Mit Hilfe des
Debuggers stellte ich fest, dass sowohl die lokale Variable als auch die
globale Variable beide vom Kompiler an Speicheradresse 0x100 abgelegt
wurden und sich demnach gegenseitig beeinflussten.
Daher:
Wie macht man sowas richtig??
Danke & Grüße,
Alex
Alex Bürgel wrote:
> Wie macht man sowas richtig??
Erst einmal indem man alle relevante Information postet. Damit man das
selber nachvollziehen kann, und/oder damit man anhand des Quellcodes und
des Assembler-Listings erkennen kann was da abläuft. Nachvollziehen
können es aber nur Anwender dieses Compilers, und das sind hier m.W.
nicht allzu viele.
Mit der von dir geposteten Info können ausschliesslich jene etwas
anfangen, die schon einmal über exakt dieses Problem bei exakt diesem
Compiler gestolpert sind.
Merksatz: 99% aller sogenannten Compilerfehler sind Missverständnisse
oder Anwenderfehler.
>Merksatz: 99% aller sogenannten Compilerfehler sind Missverständnisse>oder Anwenderfehler.
Natürlich, dass vermute ich hier auch. Ich denke nicht, dass es sich um
einen Fehler des Compilers handelt, sondern, dass ich den Compiler
falsch anwende.
>Erst einmal indem man alle relevante Information postet.
Ich hatte gehofft diese Bedingung erfüllt zu haben. Welche weiteren
Informationen werden denn, deiner Meinung nach, benötigt?
Schöne Grüße,
Alex
Alex Bürgel wrote:
> Ich hatte gehofft diese Bedingung erfüllt zu haben. Welche weiteren> Informationen werden denn, deiner Meinung nach, benötigt?
Der komplette Quellcode der betreffenden Funktion, ggf. mit zusätzlicher
Information die notwendig ist um den Code nachzuvollziehen. Und zwar
soweit minimiert, dass nur ausschliesslich der Code drin ist, der der
mit dem Fehler zu tun hat und alles Überflüssige entfernt wurde. Dieser
Code muss aber noch compilierbar und der Fehler darin reproduzierbar
sein. Nennt man einen "Testcase".
Das Listing des so erzeugten Assembler-Codes.
Ein Map-File mit Adressbelegung der Variablen und der Speicherbereiche.
Und wenn sich dann doch herausstellt, dass es ein echter Compilerfehler
ist, dann war diese Arbeit erst recht nicht umsonst. Denn jeder Support
seitens des Herstellers oder Programmierers, ob von Freeware oder
Löhnware, wird eben genau diese Information benötigen. Die brauchen dann
aber noch die Information wie compiliert wurde, also Kommandozeile,
Optionen, ...
Super.
Und wir dürfen jetzt abspecken oder was?
Aber seis drum. Um welche Variablen handelt es sich und an welcher
Stelle bist du im Debugger an der du denkst, dass der Compiler 2
Variablen übereinandergelegt hat (was ich mir ehrlich gesagt überhaupt
nicht vorstellen kann, denn globale Variablen werden komplett anders
behandelt als lokale Variablen´)
Ganz konkret befinden sich hier die Variablen:
EQU_RESISTORS[1] (global, vor main() definiert)
und
i (lokal, in main() definiert)
beide an Speicheradresse 0x105.
Im Anhang befindet sich ein Screenshot der Debugging-Software.
Der Screenshot ist zusammengeschnitten aus zwei einzelnen, da man
jeweils nur eine Variable markieren kann.
Das Fenster "Data2" zeigt die in main() lokal angelegten Variablen, das
Fenster "Data1" zeigt die globalen Variablen.
Schöne Grüße,
Alex
>Und wir dürfen jetzt abspecken oder was?
Um dies zu vermeiden hatte ich zunächst nur den Pseudo-Code gepostet und
versucht das Problem aufs Wesentliche zu reduzieren. Jedoch hatte dann
jemand nach dem "echten" Quelltext gefragt...
Alex Bürgel wrote:
>>Und wir dürfen jetzt abspecken oder was?> Um dies zu vermeiden hatte ich zunächst nur den Pseudo-Code gepostet und> versucht das Problem aufs Wesentliche zu reduzieren. Jedoch hatte dann> jemand nach dem "echten" Quelltext gefragt...>>> Der komplette Quellcode der betreffenden Funktion, ggf. mit zusätzlicher>>> Information die notwendig ist um den Code nachzuvollziehen. Und zwar>>> soweit minimiert, dass nur ausschliesslich der Code drin ist, der der>>> mit dem Fehler zu tun hat und alles Überflüssige entfernt wurde. Dieser>>> Code muss aber noch compilierbar und der Fehler darin reproduzierbar>>> sein. Nennt man einen "Testcase".
Alex Bürgel wrote:
>>Und wir dürfen jetzt abspecken oder was?> Um dies zu vermeiden hatte ich zunächst nur den Pseudo-Code gepostet und> versucht das Problem aufs Wesentliche zu reduzieren. Jedoch hatte dann> jemand nach dem "echten" Quelltext gefragt...
'echten Quelltext', den du solange abspecken sollst, bis alles
Überflüssige raus ist, der Fehler aber noch drinnen ist!
Stell dir einfach vor, wir wären die Hotline deines Compiler-Herstellers
und du musst ins in die Lage versetzen, das selbst zu kompilieren. Und
zwar das einfachst mögliche Programm, welches den Fehler gerade noch
zeigt.
Drum ist Pseudocode ungeeignet.
Drum ist auch dein Programm in seiner Langform ungeeignet. Denn kein
Entwickler wird sich durch Unmengen von Assembler-Code quälen, der
überhaupt nichts zum Problem beiträgt.
Und wenn wir schon dabei sind:
Sind irgendwelche Optimierungen eingeschaltet?
Denk immer dran: Du musst uns in die Lage versetzen, zumindest
theoretisch, dein Program hier bei uns zu compilieren. Mit allen
Einstellungen! Wenn ich das hier compilieren würde, müsste ich genau das
gleiche EXE erhalten können wie du!
Mal gecheckt, ob der RAM-Speicher ausreicht. Meiner Erinnerung nach hat
der MC9S08 nur 256 byte RAM. Da könnte es knapp werden und der Heap mit
dem statischen Speicher kollidieren. Die Addresse 0x105 sieht irgendwie
verdächtig aus. Vielleicht irre ich mich, ich kenne den Controller zu
wenig.
Gruss
Mike
Hallo,
schau mal in dein PRM File nach STACKSIZE.
Lokale Variablen werden auf dem Stack abgelegt, also auch die aus der
Main Funktion.
unsigned int cellVoltages[40];
int i,j,k,error_counter;
Das sind dann 88 Bytes. die du dafür allein auf dem Stack brauchst.
Also entweder die Stacksize entsprechend anpassen oder STACKSIZE weg und
den Stackpointer per STACKTOP auf die oberste Speicherstelle setzen.
Eckhard
Der Controller MC9S08DZ60 besitzt 4096Byte RAM, von Speicheradresse 0x80
bis 0x107F.
Die anderen von mir angelegten Variablen befinden sich:
RS232RXCHAR 0x100
STATE 0x101 bis 0x102
EQURESISTORS 0x103 bis 0x10C
i 0x105 (=EQURESISTORS[1])
j 0x107 (=EQURESISTORS[2])
k 0x10B (=EQURESISTORS[4])
error_counter 0x109 (=EQURESISTORS[3])
>Also entweder die Stacksize entsprechend anpassen oder STACKSIZE weg und>den Stackpointer per STACKTOP auf die oberste Speicherstelle setzen.
Leider kenne ich mich mit der Funktionsweise von Compilern/Linkern und
dem Stack nicht so gut aus... hier ist die von Eckhard angesprochene
prm-Datei:
1
/* This is a linker parameter file for the mc9s08dz60 */
2
3
NAMES END /* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your own files too. */
4
5
SEGMENTS /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */
6
Z_RAM = READ_WRITE 0x0080 TO 0x00FF;
7
RAM = READ_WRITE 0x0100 TO 0x107F;
8
ROM = READ_ONLY 0x1900 TO 0xFFAD;
9
ROM1 = READ_ONLY 0x1080 TO 0x13FF;
10
EEPROM = READ_ONLY 0x1400 TO 0x17FF;
11
/* INTVECTS = READ_ONLY 0xFFC0 TO 0xFFFF; Reserved for Interrupt Vectors */
12
END
13
14
PLACEMENT /* Here all predefined and user segments are placed into the SEGMENTS defined above. */
15
DEFAULT_RAM /* non-zero page variables */
16
INTO RAM;
17
18
_PRESTART, /* startup code */
19
STARTUP, /* startup data structures */
20
ROM_VAR, /* constant variables */
21
STRINGS, /* string literals */
22
VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */
23
DEFAULT_ROM,
24
COPY /* copy down information: how to initialize variables */
25
INTO ROM; /* ,ROM1: To use "ROM1" as well, pass the option -OnB=b to the compiler */
26
27
_DATA_ZEROPAGE, /* zero page variables */
28
MY_ZEROPAGE INTO Z_RAM;
29
END
30
31
32
STACKSIZE 0x50
33
34
//VECTOR 0 _Startup /* Reset vector: this is the default entry point for an application. */
Darf ich nun einfach die STACKSIZE (beliebig) vergrößern?
Oder wäre eine andere Lösung zu bevorzugen?
Hallo,
bei dem Compiler funktioniert das so :
Oberhalb der Variablen wird der Stack in der angegebenen Größe gesetzt.
Hier also Hex 50 was 80 Byte sind. Der Stack kann natürlich nicht größer
als der RAM Speicher sein. Auf jeden Fall reichen die 80 Bayte nicht
aus. Du kannst die Größe aber ruhig ein wenig heraufschrauben. Bei 4K
ist da noch einiges an Luft. Nimm 0x180 als Stacksize und as sollte
passen.
Du könntest dann deien Globalen Variablen noch in die Zeropage packen.
#pragma DATA_SEG __MY_ZEROPAGE
//global variables
volatile char RS232RXCHAR /*@0x80*/ = 0;
volatile unsigned int STATE /*@0x81*/= IDLE;
volatile unsigned int EQU_RESISTORS[5] /*@0x83*/ = {0,0,0,0,0};
#pragma DATA_SEG DEFAULT
Eckhard
Hallo Eckhard,
Vielen Dank!!
Du hast damit meine Probleme gelöst und es mir so ausführlich erklärt,
dass ich die Ursache und die Lösung verstanden habe.
Eine kurze frage hätte ich noch:
Du erwähntest weiter oben den Befehl STACKTOP. Könntest du mir kurz
erläutern wozu er dient und wann es sinnvoll ist ihn anzuwenden?
Danke auch an alle Anderen die an dieser regen Diskussion teilgenommen
haben!
Schöne Grüße,
Alex
Hallo,
mit STACKTOP kannst Du den Stackpointer direkt an eine Adresse setzen,
z.B. an das obere Ende des RAMs. Wann genau das eine oder andere
vorteilhafter ist weiß ich aber auch nicht genau. Mit STACKTOP hat man
dann gleich die Maximale Größe des Stacks.
Eckhard
Alex Bürgel wrote:
> Um dies zu vermeiden hatte ich zunächst nur den Pseudo-Code gepostet und> versucht das Problem aufs Wesentliche zu reduzieren. Jedoch hatte dann> jemand nach dem "echten" Quelltext gefragt...
Wenn du zum Arzt gehst, und die Diagnose gleich mitbringst, dann hast du
exzellente Chancen auf eine falsche Behandlung.
Wenn du deinen Pseudocode oben nochmal ansiehst, dann wirst du
feststellen, dass alles was man zur Erkennung des Problems benötigte
darin fehlte.
ich möchte auch noch was lernen :-)
den Fehler oben verstehe ich so: der Compiler legt die globalen
Variablen im RAM ab und reserviert einen relativ kleinen Stack weil er
meint "is ja nur n kleines Programm...", wird dann in der main() mit dem
Feld
unsigned int cellVoltages[40];
überrascht und der Stack kollidiert daraufhin mit dem Bereich der
globalen Variablen.
Physikalisch liegt das aber alles im RAM des Controllers.
Die cellVoltages[40] werden im main() aber doch dauerhaft benutzt und
dieser Platz würde zur Laufzeit des Programms sowieso nie freigegeben
werden (oder hab ich hier nen Denkfehler?).
Wäre es da nicht sinnvoll cellVoltages[40] ebenfalls in die globalen
Variablen zu übernehmen und müßte damit der Fehler nicht ebenfalls weg
sein? Hätte das irgendwelche anderen Nachteile (von denen ich
zugegebenermaßen keine Ahnung habe)?
SoLaLa wrote:
> den Fehler oben verstehe ich so: der Compiler legt die globalen> Variablen im RAM ab und reserviert einen relativ kleinen Stack weil er> meint "is ja nur n kleines Programm...",
Die Programmgrösse ist ihm egal, den Stackverbrauch abzuschätzen ist
dein Problem, möglicherweise mit etwas Hilfestellung. Ich bin mir
ziemlich sicher, dass sich zu diesem Thema ein paar Worte in Handbuch
des Compilers finden lassen.
> Wäre es da nicht sinnvoll cellVoltages[40] ebenfalls in die globalen> Variablen zu übernehmen und müßte damit der Fehler nicht ebenfalls weg> sein?
Korrekt. Beim '08 wäre ich auch kein bischen überrascht, wenn das zu
besseren Code führt. Die meisten 8bitter tun sich mit global/statisch
allozierten Arrays ohnehin leichter. Kannst auch "static" davor
schreiben.
Hallo,
der Nachteil der Globalen Variable wäre eben genau das die Kapselung weg
ist.
Die Performance wird durch globale Variablen auch nicht besser. Der
HC(S)08 kann den Stackpointer zur Adressierung als Indexregister
benutzen und somit sehr effektiv auf die Lokalen Variablen zugreifen.
Eckhard
Deshalb ja auch der Tip mit "static" davor. Hat die gleiche Adressierung
wie global, ist aber gekapselt. Und dass main() rekursiv verwendet wird,
das kann man wohl ausschliessen ;-).
Und er kann zwar das Indexregister benutzen, aber ich glaube kaum, dass
der '08 eine Adresse aus der Summe zweier Indexregister (plus Konstante)
im Befehl bilden kann. Was er aber für ein normales lokales Array
braucht.
Bei Skalaren hast du recht, bei Arrays sieht das anders aus.
Globale/statische Arrays adressieren sich über die Summe vom Index und
der statischen Adresse. Und das wiederum kann er wahrscheinlich im
Befehl.