Forum: Compiler & IDEs Externes Speicherinterface Hintergrundwissen


von Thomas Wiemken (Gast)


Angehängte Dateien:

Lesenswert?

Hi Leute,
Ich verwende einen ATmega128, der mit dem externen Speicherinterface
zwei SRAM-Bausteine a 512kB anspricht. Dazu verwende ich drei weitere
Port Pins als Adressleitungen und zwei Port Pins um die SRAM-Bausteine
mittels Chip Enable zu de- / aktivieren.

Um den Speicher zu testen, habe ich ein kleines Testprogramm
geschrieben, das unten dargestellt ist. Das ganze klappt auch wunderbar
und ist soweit alles in Ordnung, ich habe nun eine Frage zu der
Initialisierung des externen Speicherinterface.

Im speziellen habe habe ich ne Frage zu dieser Codezeile

<c>
void xram(void) _attribute_ ((naked)) _attribute_ ((section
(".init1")));
</c>

So wie ich es verstanden habe sorgt man somit dafür, dass das
Speicherinterface frühzeitig initialisiert wird. Somit wird dem
Compiler? oder Linker? mitgeteilt, das er den externen Speicher nutzen
kann.

Oder sehe ich das falsch?

Ich habe das ganze auch mal ohne diese vorzeitige Initialisierung
gemacht, sondern die Funktion void xram(void) ganz normal in der
main()aufgerufen. Das Ergebnis war, dass es auch so funktionierte.

Deshalb meine weitere Frage, wozu macht man es dann und was kann
passieren, wenn mann es nicht macht?

Mir geht es darum, den Hintergrund zu verstehen, da ich es für eine
Ausarbeitung gebrauche und den Quellcode begründen soll.

Vielleicht hat der werte Herr Jörg Wunsch Zeit und Möglichkeit mir
darauf zu antworten, da ich diesen Code bzw. diese Befehlszeile in
einen Beitrag von ihn gefunden habe.

Hier nun mein kleines nicht sehr anspruchvolles, aber funktionierendes
Testprogramm

<c>
#include"lcd_i2c.h"
#include"i2cmaster.h"
#include "define.h"
#include <avr/io.h>
#include <stdio.h>



#define OFFSET    0x1100

#define SET_SRAM1  {PORTF |= (1<<PF1); PORTF &= ~(1<<PF0);}
#define SET_SRAM2  {PORTF |= (1<<PF0); PORTF &= ~(1<<PF1);}

#define BANK_0    {PORTF &= ~(1<<PF4); PORTF &= ~(1<<PF3); PORTF &=
~(1<<PF2);}
#define BANK_1    {PORTF &= ~(1<<PF4); PORTF &= ~(1<<PF3); PORTF |=
(1<<PF2);}
#define BANK_2    {PORTF &= ~(1<<PF4); PORTF |= (1<<PF3); PORTF &=
~(1<<PF2);}
#define BANK_3    {PORTF &= ~(1<<PF4); PORTF |= (1<<PF3); PORTF |=
(1<<PF2);}
#define BANK_4    {PORTF |= (1<<PF4); PORTF &= ~(1<<PF3); PORTF &=
~(1<<PF2);}
#define BANK_5    {PORTF |= (1<<PF4); PORTF &= ~(1<<PF3); PORTF |=
(1<<PF2);}
#define BANK_6    {PORTF |= (1<<PF4); PORTF |= (1<<PF3); PORTF &=
~(1<<PF2);}
#define BANK_7    {PORTF |= (1<<PF4); PORTF |= (1<<PF3); PORTF |=
(1<<PF2);}


void xram(void) _attribute_ ((naked)) _attribute_ ((section
(".init1")));

void xram(void)
{
  MCUCR |= (1<<SRE)|(1<<SRW10);    // The SRE Bit will activate the 
memory
interface of the ATmega128
                    // The SRW11 and SRW10 bits control
  XMCRA |= (1<<SRW11);        // the number of wait-states

  XMCRB |= (1<<XMBK);        // Enable the buskeeper, witch ensure a
definied logic level
                    // on the lines AD7:0, otherwise they will be 
tri-stated
                    // Remember: if you disable the memory Interface, 
don´t
forget
                    // to disable the buskeeper too.
}

//********************************************************************** 
********************************************
//The function memwrite fills up a memorybank of the selected SRAM
//alternate with 0x55 and 0xaa
//********************************************************************** 
********************************************
void memwrite(void)
{
  unsigned char *pwrite = (unsigned char *) (OFFSET + 0);  // Pointer to
a unsigned char with the adress 0x1100
  unsigned int count = 0;
  for(count=0; count<= (0xFFFF-OFFSET); count++)  // until the end of 
the
memory
  {
    if(count%2)  // odd memorycells
      pwrite[count]=0x55;
    else  // even memorycells
      pwrite[count]=0xaa;
  }
}

//********************************************************************** 
********************************************
//The function memread checks a memorybank of the selected SRAM
//if the cells has alternate values of 0x55 and 0xaa
//********************************************************************** 
********************************************
unsigned int memread(void)
{
  unsigned char *pread = (unsigned char *) (OFFSET + 0);  // Pointer to 
a
unsigned char with the adress 0x1100
  unsigned int count = 0;
  unsigned char error = 0;
  do
  {
    if(count%2)  // odd memorycells
    {
      if(pread[count]!=0x55)  // Memorycell dosen´t contain the desired
value
        error = 1;

    }
    else  // even memorycells
    {
      if(pread[count]!=0xaa)  // Memorycell dosen´t contain the desired
value
        error = 1;

    }
    count++;
  }while((count<=(0xFFFF-OFFSET))&&(error == 0));  // Ends with error or
the last memorycell

  if(error)
  {
    return(count);  // Memorycell "count" has a wrong value
            // 1 <= count <= 0xef00
  }
  else
  {
    return(0);    // no problems at all
  }
}

//********************************************************************** 
********************************************
//The function memtest uses the functions memwrite and memread.
//If the cells has a wrong value, it returns the number of the cell
//If there is no problem it returns a Zero
//********************************************************************** 
********************************************
unsigned int memtest(void)
{
  unsigned int i = 0;
  memwrite();
  i = memread();
  return(i);
}

//********************************************************************** 
********************************************
//The function error_diplay only shows the memorycell with the wrong
value inside
//********************************************************************** 
********************************************
void error_display(unsigned char SRAM, unsigned char BANK, unsigned int
MEMORYCELL)
{
  lcdi2c_ausgabexy("SRAM   -> Bank  ",0,0);
  lcdi2c_ausgabenummer(SRAM, 5, 0);
  lcdi2c_ausgabenummer(BANK, 15, 0);
  lcdi2c_ausgabexy("Fehler beim Auslesen",0,1);
  lcdi2c_ausgabexy("Speicherzelle: ",0,2);
  lcdi2c_ausgabenummer(MEMORYCELL, 20, 2);
}

int main(void)
{
  UCHAR x = 0;
  unsigned int MEMORYCELL = 0;

  i2c_init();  // initialitze the I2C-Bus

  x = lcdi2c_init(); // checks the display

  PORTF |=
(0<<PF7)|(0<<PF6)|(0<<PF5)|(0<<PF4)|(0<<PF3)|(0<<PF2)|(1<<PF1)|(1<<PF0);
  DDRF |=
(0<<PF7)|(0<<PF6)|(0<<PF5)|(1<<PF4)|(1<<PF3)|(1<<PF2)|(1<<PF1)|(1<<PF0);
  // Belegung PORTF
  //
  // PF0: /CE SRAM 1
  // PF1: /CE SRAM 2
  // PF2: Adressleitung 16
  // PF3: Adressleitung 17
  // PF4: Adressleitung 18
  // PF5: Frei
  // PF6: Frei
  // PF7: Frei

  SET_SRAM1
  BANK_0
  MEMORYCELL = memtest();
  if(MEMORYCELL)
    error_display( 1,0 , MEMORYCELL);
  else
  {
    BANK_1
    MEMORYCELL = memtest();
    if(MEMORYCELL)
      error_display( 1,1 , MEMORYCELL);
    else
    {
      BANK_2
      MEMORYCELL = memtest();
      if(MEMORYCELL)
        error_display( 1,2 , MEMORYCELL);
      else
      {
        BANK_3
        MEMORYCELL = memtest();
        if(MEMORYCELL)
          error_display( 1,3 , MEMORYCELL);
        else
        {
          BANK_4
          MEMORYCELL = memtest();
          if(MEMORYCELL)
            error_display( 1,4 , MEMORYCELL);
          else
          {
            BANK_5
            MEMORYCELL = memtest();
            if(MEMORYCELL)
              error_display( 1,5 , MEMORYCELL);
            else
            {
              BANK_6
              MEMORYCELL = memtest();
              if(MEMORYCELL)
                error_display( 1,6 , MEMORYCELL);
              else
              {
                BANK_7
                MEMORYCELL = memtest();
                if(MEMORYCELL)
                  error_display( 1,7 , MEMORYCELL);
                else
                {
                  SET_SRAM2
                  BANK_0
                  MEMORYCELL = memtest();
                  if(MEMORYCELL)
                    error_display( 2,0 , MEMORYCELL);
                  else
                  {
                    BANK_1
                    MEMORYCELL = memtest();
                    if(MEMORYCELL)
                      error_display( 2,1 , MEMORYCELL);
                    else
                    {
                      BANK_2
                      MEMORYCELL = memtest();
                      if(MEMORYCELL)
                        error_display( 2,2 , MEMORYCELL);
                      else
                      {
                        BANK_3
                        MEMORYCELL = memtest();
                        if(MEMORYCELL)
                          error_display( 2,3 , MEMORYCELL);
                        else
                        {
                          BANK_4
                          MEMORYCELL = memtest();
                          if(MEMORYCELL)
                            error_display( 2,4 , MEMORYCELL);
                          else
                          {
                            BANK_5
                            MEMORYCELL = memtest();
                            if(MEMORYCELL)
                              error_display( 2,5 , MEMORYCELL);
                            else
                            {
                              BANK_6
                              MEMORYCELL = memtest();
                              if(MEMORYCELL)
                                error_display( 2,6 , MEMORYCELL);
                              else
                              {
                                BANK_7
                                MEMORYCELL = memtest();
                                if(MEMORYCELL)
                                  error_display( 2,7 , MEMORYCELL);
                                else
                                {
                                  lcdi2c_ausgabexy("Speicherzellen
OK!",0,0);    //Output on display
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }



  while(1)
  {

  }
}
</c>

von Thomas Wiemken (Gast)


Lesenswert?

Schade, das das mit den Tags nicht so will, aber ich habe den C-Code als
Dateianhang beigefügt.

Gruß und Freude
Thomas

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Die Tags stehen in eckigen Klammern, außerdem wird das C groß
geschrieben.

Zur Hälfte hast du dir das schon richtig gedacht: das frühzeitige
Aktivieren des XRAM-Interfaces in .init3 ist notwendig, damit
dort normaler Variablenspeicher untergebracht werden kann.  In
.init4 erfolgt dann die Initialisierung der Variablen (Kopieren
der ROM-Daten für .data, Ausnullen von .bss), wenn man also
Variablen in den externen RAM legen möchte, muss man selbigen
vorher aktiviert haben.

Zur Hälfte nur deshalb: das schafft die Voraussetzung, dass
man das überhaupt machen kann, aber es aktiviert diesen Bereich
noch lange nicht für Variablen.  Das erfolgt durch entsprechende
Linker-Anweisungen.

Damit sollte auch klar sein dass, wenn man dort keine Variablen
unterbringen möchte, die Aktivierung und Deaktivierung des XRAM
zu beliebigen Zeitpunkten erfolgen kann.

von Stefan S. (phunky)


Lesenswert?

Gmh, die gleiche Anfrage in zwei verschiedenen Threads zu starten ist
keine gute Idee!

Hab Dir in deinem 2. Thread geantwortet:

http://www.mikrocontroller.net/forum/read-1-380742.html#380761

Stefan

von Thomas Wiemken (Gast)


Lesenswert?

@Jörg

Verstehe ich dich richtig?

Wenn ich später den externen Speicher, nur per Zeigeroperationen
anspreche um so Daten abzulegen, ich die (.init) Anweisung garnicht
gebrauchen muss??

Weil, dann kann diese Anweisung ja raus und braucht nicht von mir
erklärt werden. Oder kann da etwas passieren?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Gmh, die gleiche Anfrage in zwei verschiedenen Threads zu
> starten ist keine gute Idee!

Um nicht zu sagen: es ist hochgradig unhöflich.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Verstehe ich dich richtig?

Du verstehst.

von Thomas Wiemken (Gast)


Lesenswert?

@Forum

Sorry für den Doppelpost!!

@Jörg

Deine Antworten (seien sie auch sehr kurz) bringen mich gerade echt
weiter.

Dann habe ich aber noch eine Frage an dich. Leider kommt die aus dem
anderen Beitrag, möchte sie aber nun hier gerne haben. Es passt
thematisch auch besser hier rein.

Im Makefile habe ich folgende Anweisung, die so wie ich es verstanden
habe, für den Linker gedacht ist.

EXMEMOPTS =
-wl,--defsym=__heap_start=0x801100,--defsym=__heap_end=0x80ffff

damit befindet sich der Heap im externen Speicher.
Der Stack müsste sich immer noch im internen Speicher befinden.
So wie alles andere auch, ist jedenfalls so in der avrlib zu sehen.

Verwende ich die andere im Makefile gegebene Option
EXMEMOPTS = -wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff

dann schmiert mir der Prozessor komplett ab.

Die erste Option ist für die Möglichkeit des dynamischen Speichers
angedacht. Welcher dann per malloc() reserviert wird.
Wenn ich aber wiederrum nur per Zeigeroperationen im exteren Speicher
arbeite, benutze ich nunmal kein malloc().

Muss ich dann trotzdem diese Linkeranweisung machen?

Und warum schmiert mir der µC bei der zweiten Option ab??

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Deine Antworten (seien sie auch sehr kurz)...

:-)

's ist ja meine Arbeitszeit, da kann ich keine Romane schreiben.

> Verwende ich die andere im Makefile gegebene Option
> EXTMEMOPTS = -wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff
> dann schmiert mir der Prozessor komplett ab.

Der Haken ist, dass beim ATmega128 (und bei allen anderen AVRs, bei
denen die IO-Register länger als bis 0x60 gehen) sowohl die GCC-Specs
als auch die Kommandozeile eine -Tdata-Option haben, die dann an den
Linker weitergereicht wird.  Früher funktionierte das, da der Linker
die zweite auftauchende (die aus der Kommandozeile) benutzt hat.  Mit
binutils 2.16 hat sich dieses Verhalten geändert, damit wird dein
-Tdata ignoriert und nur das -Tdata=0x800100 aus den GCC-Specs
genommen.

Workaround: -Wl,--section-start=.data=0x801100

> Muss ich dann trotzdem diese Linkeranweisung machen?

Nein.  Sie definiert ausschließlich Symbole für die Steuerung des
Heaps.  Wenn du den nicht benutzt, interessieren diese Symbole keine
Sau.

von Thomas Wiemken (Gast)


Lesenswert?

Guten morgen!!

Ich merke immer wieder, das in meinem Studium als Nachrichtentechniker
viel zu wenig auf Mikrokontrollertechnik im allgemeinen eingegangen
worden ist. Es ist für mich immernoch recht komplex und schwierig zu
verstehen, besonders die Sachen mit Linker und seinen ganzen Optionen,
aber bislang konnte ich mir immer gut helfen.

@Jörg

Ok, wenn man also im Makefile deinen Workaround benutzt, ich denke mal
in folgender Form:

EXTMEMOPTS =
-Wl,--section-start=.data=0x801100,--defsym=__heap_end=0x80ffff

dann befinden sich also auch die Variablen im externen Speicher.
Falls dann globale Variablen genutzt werden, muss das XMEM Interface
mit der (.init) Anweisung frühzeitig initialisiert werden. Ansonsten
können die globalen Variablen beim Start-Up des Kontrollers nicht
deklariert und definiert werden.

Ich hoffe so habe ich es richtig beschrieben.

Gruß und Freude
Thomas

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

> Ich hoffe so habe ich es richtig beschrieben.

Hast du.

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.