mikrocontroller.net

Forum: Compiler & IDEs Compilerfehler bei mem-check


Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich bekomme einen Compilerfehler:

mem-check.c:28: internal compiler error: in start_function, at 
c-decl.c:6035


weiß jemand Rat?


#include <avr/io.h>  // RAMEND
#include "mem-check.h"

// Mask to init SRAM and check against
#define MASK 0xaa

// From linker script
extern unsigned char __heap_start;

unsigned short get_mem_unused (void)
{
   unsigned short unused = 0;
   unsigned char *p = &__heap_start;

   do
   {
      if (*p++ != MASK)
         break;

      unused++;
   } while (p <= (unsigned char*) RAMEND);

      return unused;
}

/* !!! never call this function !!! */
void _attribute_ ((naked, section (".init8")))__init8_mem (void)
{  !!!hier ist der Compilerfehler!!!
   __asm volatile (
      "ldi r30, lo8 (__heap_start)"  "\n\t"
      "ldi r31, hi8 (__heap_start)"  "\n\t"
      "ldi r24, %0"                  "\n\t"
      "ldi r25, hi8 (%1)"            "\n"
      "0:"                           "\n\t"
      "st  Z+,  r24"                 "\n\t"
      "cpi r30, lo8 (%1)"            "\n\t"
      "cpc r31, r25"                 "\n\t"
      "brlo 0b"
         :
         : "i" (MASK), "i" (RAMEND+1)
   );
}

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Known issue, tritt seltsamerweise nur beim WinAVR-Build des Compilers
auf.

Workaround: entkopple die Deklaration und die Definition der Funktion.
void __attribute__ ((naked, section (".init8")))__init8_mem (void);
void __init8_mem (void)
{
   /* code goes here */
}

Zusätzliche Bemerkungen: benenne die Funktion bitte init_mem, nicht
__init8_mem.  Solche Namen sind für Compiler und Systembibliothek
reserviert.  Außerdem kannst du das gut und gern bereits in .init3
abfackeln und es gibt keinen Grund, unlesbaren inline asm Code
dafür zu schreiben.  Das geht auch prima in C:
void __attribute__((naked, section(".init3"))) init_mem(void);
void init_mem(void)
{
        extern uint8_t __heap_start;
        uint8_t *cp;

        for (cp = &__heap_start; cp < (uint8_t *)RAMEND; cp++)
                *cp = 0x42;
}

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK besten Dank so läufts.


ich habe sporadisch abstürze und möchte mit dieser Funktion prüfen ob es 
einen Stacküberlauf gibt.

get_mem_unused ();
sollte jetzt den freien RAM im whorst case anzeigen, ist das richtig?
Momentan kommt 0 zurück.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich kenne deine Implementierung für get_mem_unused() nicht, aber
es hört sich so an, als sollte das ungefähr so benutzt werden, ja.

Autor: ... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

@Jörg: Die Implementierung von get_mem_unused steht im ersten Post :)

Dürfte allerdings so nicht funktionieren und immer 0 zurückliefern. Mann 
muß vom aktuellen Stackpointer ausgehend "nach unten" suchen um den 
Abstand zum "heap end" zu ermittteln. Bei "heap start" anzufangen und 
"nach oben" zu suchen ist normalerweise ziemlich für die Katz ...

CU

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Gast,


richtig da kommt immer 0 zurück,
kannst Du die geänderte Suchfunktion einstellen?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du wirst doch wohl noch die Schleife umdrehen können :-)

Fang bei RAMEND an zu immer kleineren Werten hin.
Solange du im Speicher kein 0xaa vorfindest, bist
du sicher noch im Stack. Leider gilt das umgekehrte nicht :-)
Wenn du ein 0xaa vorfindest, heist das nicht, dass du den
Stack durch hast, denn am Stack kann ja auch zufällig ein
0xaa liegen. Ist sogar recht wahrscheinlich, dass du auf eines
stossen wirst. Von daher ist es sicherlich besser, wenn du
das Muster auf eine etwas längere Sequenz ausdehnen würdest.
Der berühmte 0xAFFE bietet sich an. Nun kann zwar auch zufällig
am Stack ein 0xAFFE liegen, das ist aber deutlich unwahrscheinlicher
als ein einzenles 0xaa.

Hast du dann den ersten 0xAFFE gefunden, geht es in der nächsten
Schleife weiter, bis der 0xAFFE wieder nicht mehr gefunden werden
kann. Das bedeutet dann, dass du durch den 'freien' Speicher durch
bist und den Bereich der Heap Allokierungen erreicht hast.

Du weist jetzt also wo der Stack endet und wo der Heap anfängt.
Die Differenz davon ist der noch freie Speicher zwischen Heap
und Stack.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Man könnte auch bei __brkval anfangen und von dort nach oben gucken.
In dieser Variablen steht die Adresse, bis zu der malloc() den
Speicher bereits in Beschlag genommen hat.  Darüber kommt dann
freier Platz im Stack.

Man muss allerdings dann den Sonderfall behandeln, dass __brkval
noch NULL ist, dann wurde noch kein einziges malloc() aufgerufen und
man müsste bei __heap_start starten.

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

so habe ich es geändert, als einfache Version ohne Prüfung auf 0xAFFE.
Jetzt erhalte ich Werte zwischen 30 und 600.

#include <avr/io.h>  // RAMEND
#include "mem-check.h"

// Mask to init SRAM and check against
#define MASK 0xaa

// From linker script
extern unsigned char __heap_start;

unsigned short get_mem_unused (void)
{
   unsigned short unused = 0;

   unsigned char *p = (unsigned char*) RAMEND;

   do
   {
      if (*p-- == MASK)
         break;

      unused++;
   } while (p >= &__heap_start);

      return unused;
}


void __attribute__((naked, section(".init3"))) init_mem(void);
void init_mem(void)
{
        extern uint8_t __heap_start;
        uint8_t *cp;

        for (cp = &__heap_start; cp < (uint8_t *)RAMEND; cp++)
                *cp = 0x42;

}

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael wrote:

> so habe ich es geändert, als einfache Version ohne Prüfung auf
> 0xAFFE.  Jetzt erhalte ich Werte zwischen 30 und 600.

Naja...

> #define MASK 0xaa

>       if (*p-- == MASK)

>                 *cp = 0x42;

Du solltest die Werte wohl schon anpassen. :-/

Außerdem: wenn du von RAMEND nach unten guckst, erfasst du zuerst den
Stack.  Der sollte immer ein wenig benutzten RAM haben...  Wie Karl
Heinz schon andeutete, ist die Erkennung dort nicht ganz trivial, da
deine MASK (oder deine 0x42, je nachdem, wofür du dich entscheidest)
ja auch normaler Inhalt des Stacks sein könnte.

Erst, nachdem du die Stackbelegung bewertet hast, sollte dann der
freie RAM folgen, der mit MASK gefüllt ist.  Wenn du dann noch weiter
nach unten läufst, kommt entweder der Heap oder die statischen
Variablen (falls kein malloc() benutzt wird).

Daher war ja mein Vorschlag, dass du vom Ende des Heap nach oben
gehst.

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wenn ich jetzt einfach den Stackpointer von __brkval abziehe und dieser 
>0 ist? Passt das?

extern unsigned char __brkval;

uint16_t momentan_frei = SP - (uint16_t) &__brkval;

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael wrote:

> wenn ich jetzt einfach den Stackpointer von __brkval abziehe und dieser
> >0 ist? Passt das?

Damit hast du den aktuell freien RAM.  Das Ziel der Mustersuche dagegen
ist es, den über die bisherige Laufzeit minimal vorhandenen freien
RAM zu ermitteln.

Autor: G. L. (sprintersb)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi, die urspüngliche Implementierung findet sich in 
http://www.roboternetz.de/wissen/index.php/Speiche...

-- Ich hatte Inline Assembler gewählt, weil der Code ansonsten bei -O0 
nicht lauffähig ist (weiß jetzt nicht mehr genau, warum).
-- Gedacht war der Code, wenn ohne malloc() etc gearbeitet wird, weil 
man das auf AVR möglichst vermeiden sollte, zumindest bei den kleineren 
Derivaten -- und idR auch vermeiden kann.

== Edit ==

Ah ja. Der Grund ist, daß man nicht garantieren kann, daß GCC alle 
Variablen in Registern hält und evtl. auto-Variablen im Frame anlegt, 
die dann von der init-Routine überschreiben würden. Man darf dann also 
maximal bis SP initialisieren und nicht bis RAMEND.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>get_mem_unused ();
>sollte jetzt den freien RAM im whorst case anzeigen, ist das richtig?
>Momentan kommt 0 zurück.

Wenn man printf() aus der avr-libc benutzt kommt tatsächlich
immer 0 zurück. printf() benutzt malloc(). Damit wird der
Heap verändert und get_mem_unused (); funktioniert nicht mehr.

Autor: holger (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Meine Testprogramme.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
holger wrote:

> printf() benutzt malloc().

Schon lange nicht mehr.  fdevopen() benutzt malloc(), aber es gibt ja
mittlerweile fdev_setup_stream() und FDEV_SETUP_STREAM() als
Alternativen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.