Hi, auf einem kleinen C++ - Projekt (ich verwende CodeSourcery g++) für den STM32F103RE habe ich den Versuch unternommen, einen eigenen globalen new-Handler zu schreiben (um nicht die Standard Libs einbinden zu müssen). Bislang klappt das Allokieren ganz gut. Allerdings klappt das Allokieren von Speicher im Konstruktor schlicht nicht. Ich habe das Problem in einem Beispielprogramm rekonstruiert (main.cpp). Das RAM liegt beim STM32F103RE im Bereich $20000000 bis $2000ffff. Die Allokation für myA klappt, für myB liefert er mir einen Zeiger vom Stack :-( -> Screen Shot: main_debug.jpg Wenn man den Konstruktor von B durchgeht, sieht man, daß das Objekt offenbar richtig allokiert wurde (this) und auch für myVector die Allokation geklappt hat. Nur beim Rücksprung geht die Info über this anscheinend wieder "verloren". -> Screen Shot: main_debug2.jpg Die Speicherallokation selbst wird in mini_cpp.cpp vorgenommen. Aber warum? Kann jemand helfen? Grüße schnack
Die Allokation klappt genau dann, wenn sie separat z.B. in einer init() Methode erfolgt. Letztlich ist mir unklar, wie in einem new der Zeiger auf den allokierten Speicher zurückgegeben wird, wenn nach der Allokation noch der Konstruktor durchlaufen wird, bevor der Rücksprung erfolgt. Grüße schnack
Ich könnte mir vorstellen, dass alles ordentlich funktioniert. Vielleicht ist es nur eine Fehlinterprtation? C. D. schrieb: > daß das Objekt > offenbar richtig allokiert wurde (this) und auch für myVector die > Allokation geklappt hat. Nur beim Rücksprung geht die Info über this > anscheinend wieder "verloren". Nach dem Rücksprung aus dem Konstruktor ist es ja nicht mehr 'this', dann ist es 'B'. 'this' ist dann was anderes. ???!
Hallo Ralf, ich fürchte, daß es keine Fehlinterpretation ist. Das Programm main.cpp ist auch nur die kondensierte Fassung des Problems. Tatsächlich haben sich die Probleme im eigentlichen Projekt beim Aufruf virtueller Funktionen gezeigt, die überhaupt nicht funktionierten (führten auf dem STM32 direkt zu einem 'Hard Fault'). Nach langer Sucherei habe ich dann gemerkt, daß der Aufruf für diejenigen Objekte fehlschlägt, bei denen das new() nicht funktioniert hat (weil sie im Konstruktor eigenen Speicher vom Heap allokieren) und dann dieses Kurzbeispiel zusammengesetzt. Ich habe inzwischen herausbekommen, wie der Wert für den Zeiger myB zu interpretieren ist: es ist die untere, aktuelle Stack-Grenze. Der Screen-Shot zeigt den Durchlauf durch p_malloc(), aufgerufen in
1 | myVector = new char[10]; |
. Der Wert für stack ist $2000ff94. Und genau diesen Wert hat nach dem Rücksprung auch myB. Wobei: warum das so ist, ist mir ein völliges Rätsel.. Grüße schnack
Ist eventuell schon eine Lösung gefunden worden? static char* heap_end = 0; lieber als volatile nutzen?
Hi, ich denke 'volatile' kann da nicht helfen (habs aber trotzdem ausprobiert- ohne Verbesserung), denn hier gibt es ja keine ISR's oder andere Funktionen, die "von der Seite reingrätschen". Meine "Lösung" besteht in dem workaround von oben, d.h. ich allokiere Speicher separat. Das ist keine wirklich erfreuliche Sache, denn ich kann auf diese Weise im eigentlichen Projekt die Allokation durch Vererbung NICHT automatisch erledigen lassen -> Ideal wäre es, wenn der Konstruktor der Oberklasse automatisch durchläuft und dabei den Speicher allokiert. So muß ich die Allokation durch die Oberklasse-Methode selbst aufrufen (was ich tue) oder in der Unterklasse das Ganze noch einmal implementieren. Grüße schnack
Du könntest es ja mal mit placement-new versuchen.
Hm, das verstehe ich nicht. Ist das ein anderer workaround? Mit placement-new übergebe ich die bereits die Speicheradresse - aber woher soll die kommen?
1 | // woher kommt myBAddress?
|
2 | myB = new (myBAddress) new B; |
Zum anderen: die Allokation klappt ja "weitgehend" - nur beim Rücksprung aus new() wird die Adresse verbaselt. Ich fände es sauberer, die Ursache für das Problem zu verstehen und dann nach einer geeignete Lösung zu suchen. Weiß jemand, was ein C++ Compiler mit der Adresse für den allokierten Speicher macht, wenn er vor dem Rücksprung die Konstuktoren durchläuft? "Eigentlich" sollte er diese Adresse via Stack zurückgeben - wie das bei Funktionen üblich ist. Meine Vermutung ist deshalb, daß irgendetwas mit dem Stack nicht mehr stimmt. Denn in p_malloc() wird die Variable stack:
1 | char* stack = (char*) __get_MSP(); |
angelegt - mit dem Wert des aktuellen Stackzeigers - geliefert von __get_MSP(). Diese Variable liegt ja ihrerseits auch auf dem Stack (sorry, ist schon verwirrend). Offenbar wird dieser Variableninhalt als Rückgabewert für new() verwendet.
Hm. Ich habe jetzt die Allokation etwas verändert und die Sicherheitsabfrage, ob heap und stack zusammengelaufen sind, ausgelassen. Und prompt klappt es... Das ist natürlich auch noch keine Lösung, aber immerhin.. Grüße schnack
C. D. schrieb: > Hm, das verstehe ich nicht. Ist das ein anderer workaround? Ja. Es ist die Art, die C++ vorsieht, um die Allokation von der Initialisierung zu trennen. Du allokierst erst deinen Speicher und übergibst den an placement-new. Das ruft dann nur noch die Konstruktoren auf. > Mit placement-new übergebe ich die bereits die Speicheradresse - aber > woher soll die kommen? Von deinem p_malloc. > Zum anderen: die Allokation klappt ja "weitgehend" - nur beim Rücksprung > aus new() wird die Adresse verbaselt. Ich fände es sauberer, die Ursache > für das Problem zu verstehen und dann nach einer geeignete Lösung zu > suchen. Natürlich, aber das scheint ja nicht so gut zu klappen, da du oben deinen Workaround mit der init()-Funktion beschrieben hast. placement-new wäre auch ein Workaround, aber einer, der vielleicht nicht ganz so unkomfortabel ist. > Weiß jemand, was ein C++ Compiler mit der Adresse für den allokierten > Speicher macht, wenn er vor dem Rücksprung die Konstuktoren durchläuft? Was soll er damit schon machen? Er übergibt ihn als this-Zeiger an den Konstruktor. > "Eigentlich" sollte er diese Adresse via Stack zurückgeben - wie das bei > Funktionen üblich ist. Meistens wird das eher über Register gemacht. > Meine Vermutung ist deshalb, daß irgendetwas mit dem Stack nicht mehr > stimmt. Denn in p_malloc() wird die Variable stack: > char* stack = (char*) __get_MSP(); > angelegt - mit dem Wert des aktuellen Stackzeigers - geliefert von > __get_MSP(). Bist du eigentlich sicher, daß __get_MSP() korrekt funktioniert? Ich kenne mich mit dem Prozessor nicht aus, aber es sieht für mich aus, als ob der asm-Teil das Register r0 verändert, ohne das dem Compiler mitzuteilen. > Diese Variable liegt ja ihrerseits auch auf dem Stack (sorry, ist schon verwirrend). Ich würde auch hier erwarten, daß sie in einem Register liegt.
Rolf Magnus schrieb: >> Meine Vermutung ist deshalb, daß irgendetwas mit dem Stack nicht mehr >> stimmt. Denn in p_malloc() wird die Variable stack: >> char* stack = (char*) __get_MSP(); >> angelegt - mit dem Wert des aktuellen Stackzeigers - geliefert von >> __get_MSP(). > > Bist du eigentlich sicher, daß __get_MSP() korrekt funktioniert? Ich > kenne mich mit dem Prozessor nicht aus, aber es sieht für mich aus, als > ob der asm-Teil das Register r0 verändert, ohne das dem Compiler > mitzuteilen. Nein, bin ich mir nicht. Ich habe die Funktion __get_MSP() aus core_cm3.c quasi blind übernommen. Wahrscheinlich liegt hier der Hund begraben.. Wenn r0 natürlich für den Rückgabewert benutzt wird, ist das nicht so pfiffig. Grüße schnack
Rolf Magnus schrieb: > als > ob der asm-Teil das Register r0 verändert, ohne das dem Compiler > mitzuteilen. Wie kann ich das dem Compiler mitteilen? Grüße schnack
Offenbar durch die Clobber List: http://www.rn-wissen.de/index.php/Inline-Assembler_in_avr-gcc#Clobbers Mal schauen..
Also, nun ist es vollbracht. @Rolf: danke - Dein Tip hat den richtigen Weg aufgezeigt! Ich die __get_MSP(void) Funktion ersetzt durch:
1 | uint32_t __get_MSP(void) |
2 | {
|
3 | register uint32_t result __asm ("r0") = 0; |
4 | |
5 | __asm volatile ("MRS %0, msp\n" |
6 | "BX lr \n" : "=r" (result) ); |
7 | return(result); |
8 | }
|
aus http://cdn.energymicro.com/dl/documentation/doxygen/core__cm3_8c-source.html. Damit hat es funktioniert. Zum Vergleich die alte Funktion, zusammen mit der Header-Info der core_cm3.c:
1 | /**************************************************************************//**
|
2 | * @file core_cm3.c
|
3 | * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Source File
|
4 | * @version V1.30
|
5 | * @date 30. October 2009
|
6 | |
7 | ...
|
8 | |
9 | * Return the current value of the MSP (main stack pointer)
|
10 | * Cortex processor register
|
11 | */
|
12 | uint32_t __get_MSP(void) __attribute__( ( naked ) ); |
13 | uint32_t __get_MSP(void) |
14 | {
|
15 | uint32_t result=0; |
16 | |
17 | __ASM volatile ("MRS %0, msp\n\t" |
18 | "MOV r0, %0 \n\t" |
19 | "BX lr \n\t" : "=r" (result) ); |
20 | return(result); |
21 | }
|
Grüße schnack
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.



