Forum: Mikrocontroller und Digitale Elektronik Kompiler vergibt Speicheradresse mehrmals?!


von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Lesenswert?

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
volatile int GLOBALE_VARIABLE;
2
3
void main(void){
4
   int LOKALE_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

von (prx) A. K. (prx)


Lesenswert?

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.

von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Lesenswert?

>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

von Peter (Gast)


Lesenswert?

einfach mal dem erzeugten Assembler code posten.

von (prx) A. K. (prx)


Lesenswert?

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, ...

von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Angehängte Dateien:

Lesenswert?

Nun gut, vielleicht hilft es ja...

C-Code in der main.c
1
//included files
2
#include <hidef.h> /* for EnableInterrupts macro */
3
#include "derivative.h" /* include peripheral declarations */
4
#include <string.h>
5
#include <stdio.h>
6
7
//definitions
8
#define BUS1          PTBD_PTBD6
9
#define BUS2          PTBD_PTBD7
10
#define BUS3          PTDD_PTDD0
11
#define BUS4          PTDD_PTDD1
12
#define BUS5          PTDD_PTDD2
13
#define BUS6          PTDD_PTDD3
14
#define BUS7          PTDD_PTDD4
15
#define BUS8          PTDD_PTDD5
16
#define BUS9          PTDD_PTDD6
17
#define BUS10         PTDD_PTDD7
18
#define MASTER_READY  PTBD_PTBD4
19
#define UN252         PTBD_PTBD3
20
#define LED1          PTAD_PTAD4
21
#define RESET_LINE    PTED_PTED0
22
23
//State-definitions
24
#define IDLE          0     //permanently measure cell-voltages, stack-voltage and temperatures
25
#define SET_EQU_RESISTORS 1 //Toggle equalization resistors on/off
26
27
//function declarations
28
void MCU_init(void);                            /* Device initialization function declaration */
29
int RS232_send_char(char);                      //Function to send one character
30
int RS232_send_string(char *);                  //Function to send a string
31
char RS232_rec_char(void);                      //Function to read one character
32
unsigned int get_adc_value(int);                //Function to read one ADC-value (select channel)
33
void pause(int);                                //Function to generate a pause for Nx125ns
34
char send_command_to_slave(char, char, char);   //Function to send one command to the slave 
35
void reset_slave(char);                         //Function to reset the slave
36
37
38
39
//global variables
40
volatile char RS232RXCHAR = 0;
41
volatile unsigned int STATE = IDLE;
42
volatile unsigned int EQU_RESISTORS[5] = {0,0,0,0,0};
43
44
45
//main function
46
void main(void) {
47
48
  unsigned int cellVoltages[40];
49
  int i,j,k,error_counter;
50
  
51
  MCU_init();       //call Device Initialization
52
  EnableInterrupts; //global interrupt enable
53
  
54
  for(i=0;i<40;i++)
55
    cellVoltages[i] = 0;   
56
  
57
  for(;;){
58
     
59
    if(RS232RXCHAR != 0){
60
      
61
      RS232_send_char(RS232RXCHAR);   
62
      RS232RXCHAR = 0;
63
      STATE = IDLE;
64
    }
65
     //State-Machine
66
      switch(STATE){
67
        case IDLE:{
68
          for(i=1;i<39;i++){
69
            if(!send_command_to_slave(i, 1, 0)){
70
              cellVoltages[i] = get_adc_value(0); 
71
            } 
72
          }    
73
          break;  
74
        }
75
        case SET_EQU_RESISTORS:{
76
          error_counter = 0;
77
          k = 1;
78
          for(i=0;i<5;i++){
79
            for(j=1;j<=128;j<<=1){
80
              if(j&EQU_RESISTORS[i]){                
81
                if(send_command_to_slave((i+1)*k, 1, 1)){
82
                  error_counter++;
83
                }
84
              } else{
85
                if(send_command_to_slave((i+1)*k, 0, 1)){
86
                  error_counter++;
87
                }
88
              }
89
                
90
            }
91
            k++;
92
          }
93
          STATE = IDLE;
94
          break;
95
        }
96
        default:{
97
          break;
98
        }
99
      }  
100
               
101
    __RESET_WATCHDOG(); //reset the watchdog
102
  }
103
}

Im Anhang ein Map-File des Projekts.

Leider weis ich nicht, wie ich den Assembler-Code bzw. das 
Memory-Mapping kopieren könnte...

Grüße,
Alex

von Karl H. (kbuchegg)


Lesenswert?

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´)

von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Angehängte Dateien:

Lesenswert?

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

von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Lesenswert?

>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...

von Severino R. (severino)


Lesenswert?

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".

von Karl H. (kbuchegg)


Lesenswert?

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!

von Karl H. (kbuchegg)


Lesenswert?

Auf welchen Adressen liegen eigentlich j und k
(nur um mal ein Gefühl dafür zu bekommen, welcher Wertebereich für i 
angemessen wäre).

von Mike (Gast)


Lesenswert?

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

von Eckhard (Gast)


Lesenswert?

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

von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Lesenswert?

Ok, reduzierter Code:
1
//included files
2
#include <hidef.h> /* for EnableInterrupts macro */
3
#include "derivative.h" /* include peripheral declarations */
4
#include <string.h>
5
#include <stdio.h>
6
7
8
//global variables
9
volatile char RS232RXCHAR /*@0x80*/ = 0;
10
volatile unsigned int STATE /*@0x81*/= IDLE;
11
volatile unsigned int EQU_RESISTORS[5] /*@0x83*/ = {0,0,0,0,0};
12
13
14
//main function
15
void main(void) {
16
17
  unsigned int cellVoltages[40];
18
  int i,j,k,error_counter;
19
  
20
  for(;;);
21
}

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])

von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Lesenswert?

>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?

von Eckhard (Gast)


Lesenswert?

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

von Alex B. (Firma: Ucore Fotografie www.ucore.de) (alex22) Benutzerseite


Lesenswert?

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

von Eckhard (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von SoLaLa (Gast)


Lesenswert?

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)?

von (prx) A. K. (prx)


Lesenswert?

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.

von Eckhard (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

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.