Forum: Mikrocontroller und Digitale Elektronik PIC18 Memory Banking


von Martin D. (eric_arthur_blair)


Lesenswert?

Hallo!

Ich versuche gerade die EFSL (Embedded File System Library) auf meinem
PIC18F6680 zum laufen zu bekommen. Ich verwende dabei den
MCC18-Compiler. Ich bekomme beim Übersetzen eines der Sourcefiles
(ui.c) eine Fehlermeldung, dass der "stack frame" zu groß währe. Ich
vermute dass das mit einem zu großen lokalen Datenobjekt zu tun hat,
denn der Compiler weißt direkt auf die Stelle im Code hin wo das Objekt
angelegt wird (Array der Größe 512 vom Typ unsigned char). Allerdings
ist mir das nicht so ganz schlüssig, weil IMO lokale Datenobjekt auf
dem Heap und nicht auf dem Stack abgelegt werden und ich deswegen
keinen Grund sehe, warum der stack frame zu groß werden sollte.
Nichts desto trotz lese ich auch im Compiler-Handbuch, dass man für
Objekte, die die Größe einer Memory Bank überschreiten spezielle
Vorkehrungen treffen muss. Es muss eine eigene Speicher-Section
deklariert werden im Linkerscript ein geeigent großer Bereich definiert
werden und die Section dorthin platziert werden. Allerdings gilt das
IMHO auch nur für globale Datenstrukturen.
Um meine Frage auf den Punkt zu bringen: Wie kann ich innerhalb einer
Funktion lokale Datenobjekte verwenden, die die Größe einer Memory-Bank
überschreiten? Oder ist das überhaupt kein Problem und der Fehler liegt
woanders?

Gruß,

Martin

von Schoasch (Gast)


Lesenswert?

Damit du eine Antwort auf diese Frage bekommst, solltest du sie auf
www.fernando-heitor.de post. Dort kann dir sicher jemand was dazu
sagen.

mehr kann ich auch nicht machen. mfg Schoasch

von Bernhard (Gast)


Lesenswert?

@ Schoasch
Es gibt auch hier PIC-User - und das soll, so finde ich, auch so
bleiben.

@ Martin
weil IMO lokale Datenobjekt auf
>> ... dem Heap und nicht auf dem Stack abgelegt werden
Soweit ich weiß (also ein ganz großes IMHHHO) werden beim PIC18F nur
der Hardware-Stack, der Software-Stack und die speziellen
Rückgaberegister verwendet.
Wenn ich richtig liege, dann musst du nur in den Build-Einstellungn auf
das Multi-Stack-Bank-Modell umschalten und im Linkerskript die
Stackgröße erhöhen. Ich habe das so gemacht:

DATABANK   NAME=gpr6       START=0x600          END=0x7FF
//DATABANK   NAME=gpr7       START=0x700          END=0x7FF
...
STACK SIZE=0x200 RAM=gpr6

Dabei fehlt laut C18-Handbuch ein PROTECTED, es ging aber  trotzdem.

Viele Grüße,
Bernhard

von Schoasch (Gast)


Lesenswert?

Hi

Ja ich weis,ich bin selbst auch PIC-User. Ich bin halt nur ein
ziemlicher Fan vom oben erwähnten Forum. Es hat zwar nicht eine so hohe
Frequenz wie dieses, aber man lernt sehr sehr viel(find ich halt).
Ausserdem ist der Umgangston dort etwas angenehmern... hier wird man ja
als PIC benutzer eher niedergemacht als einem Hilfreiche tipps gegeben
werden.

von Martin D. (eric_arthur_blair)


Lesenswert?

Ich muss dazu sagen, dass ich nicht ganz freiwillig die PICs verwende.
Ich finde das Adressierungsmodell in der Tat etwas anachronistisch.

ontopic: Ich habe versucht das File mit der -ls Option (large stack) zu
kompilieren, aber es tritt immer noch der gleiche Fehler auf. Ich habe
allerdings das Linker-File noch nicht modifiziert. Muss ich das denn
überhaupt beim Erzeugen des Object-Files schon machen? Ich meine ich
linke ja noch gar nicht.

von A.K. (Gast)


Lesenswert?

PIC oder nicht, ein C-Compiler legt lokale Objekte entweder auf dem
Stack ab (Standard) oder, wenn die Microcontroller-Architektur sich
damit schwer tut, statisch. Letzteres ist nicht Standard-konform und
ist nicht reentant, deshalb finden sich dann entsprechende Optionen im
Compiler. Lokale Objekte automatisch auf dem Heap abzulegen wär mir mal
was neues und ergibt nicht allzu viel Sinn.

Aber egal wo der Compiler die Objekte auch immer ablegt, die PIC18
Architektur hat so ihre Probleme mit indirekter Adressierung von
Objekten grösser als 256 Bytes. Ohne den Compiler zu kennen nehme ich
daher an, dass mit der -ls Option zwar der Stack insgesamt grösser als
256 Bytes werden darf, aber dennoch die Grösse einzelner Objekte
beschränkt bleibt um diese noch mit halbwegs vernünftigem Aufwand
adressieren zu können.

Da es ausserdem recht untypisch für Microcontroller-Programmierung ist,
grosse Objekte auf dem Stack zu plazieren, wirst du dir wohl ein anderes
Verfahren einfallen lassen müssen.

von Dieter Werner (Gast)


Lesenswert?

>> die PIC18 Architektur hat so ihre Probleme mit indirekter
>> Adressierung von Objekten grösser als 256 Bytes.

Für direkte Adressierung trifft das sicher zu: movwf 8-Bit-Adresse
(plus 4 Bit aus Bankselect oder im "access" Bereich).
Indirekt wird mit fsr(0..2) adressiert und die haben je 12 Bit.

Für die Übergabe von Variablen wird der Compiler mit hoher
Wahrscheinlichkeit einen Softwarestack anlegen, da interrupt, call und
return einen 32 Ebenen tiefen Hardware-Stack benutzen.

von A.K. (Gast)


Lesenswert?

Yep, natürlich benutzt PIC einen Software-Stack für lokale Variablen,
geht ja nicht anders.

Auch AVRs scheinen übrigens mit Hinblick auf getrennte Stacks für
Variablen und Return-Adressen konzipiert zu sein, warum auch immer
(sonst gäbe es die Möglichkeit, den Stack-Pointer atomar zu
manipulieren).

von Martin D. (eric_arthur_blair)


Lesenswert?

Ok. Das mit dem Heap war zugegebener Weise Quatsch. Um die Sache nochmal
etwas zu illustrieren ein kleiner Codeausschnitt der betreffenden
Problemstelle:

short listFiles(FileSystem *fs, char *dirname)
{
  unsigned long startCluster;
  unsigned char fileEntryCount;
  unsigned short counter=0;
  unsigned long offset=0;
  FileRecord fileEntry;
  FileLocation loc;
  unsigned char buf[512];
  File dir;
  unsigned short i;

... [SNIP] ...

Der lokale Parameter fs ist ein Pointer auf eine 423 Byte große
Struktur, wird ja aber nur per Referenz übergeben und dürfte deswegen
doch auch auf dem Callstack nicht mehr Platz als üblich einnehmen. Das
struct FileLocation ist 48 Bytes groß und FileRecord 256 Byte. Damit
wären die Bank-Grenzen schon locker überschritten. Ich habe den Code
jetzt mal mit der -sco (storage class overlay) compiliert. Dabei werden
die lokalen Variablen als static deklariert, aber bei jedem
Funktionseintritt neu initialisiert. Außerdem versucht der Linker
später Variablen gleichen Typs aus unterschiedlichen Funktionen im
gleichen Datenbereich zu speichern. Haltet ihr das für eine mögliche
Lösung der Problematik?

Gruß,

Martin

von A.K. (Gast)


Lesenswert?

Du solltest Ausschau halten, wie oft grosse Datenstrukturen wie dieser
512-Byte-Puffer und FileRecord vorkommen. Ist das häufig genug, dann
leg die nicht in jeder Funktion lokal sondern einmal global an. Das mag
nicht bester Programmierstil sein, auf dem PC würde ich davon dringend
abraten, ist aber genau die Sorte Kompromiss die man bei
Schmalspurmaschinen wie 8bit-µCs nunmal eingehen muss.

von A.K. (Gast)


Lesenswert?

PS: Stack-Frames von überschaubarer Grösse erleichtern oft auch die
Erkenntnis, wann das RAM nicht mehr ausreicht. Ebenso die -sco
Variante. Die Gesamtgrösse der statischer Daten kennt man nach dem
Linken, beim Stack ist das meist schwieriger zu erkennen. Auch dies ist
so ein strategischer Unterschied zwischen PC und Controller.

von Peter D. (peda)


Lesenswert?

@A.K.

"Ist das häufig genug, dann leg die nicht in jeder Funktion lokal
sondern einmal global an."

Das muß dann ne PIC-C Eigenart sein.

Beim Keil C51 wird man jedenfalls bestaft, wenn man dem Overlayer ins
Handwerk pfuscht und unnötiger Weise globale statt lokale Variablen
verwendet.

Nur wenn man Funktionspointer verwendet, die erst zur Laufzeit
berechnet werden, kann der Overlayer nicht mehr richtig den
Calling-Tree parsen.
Aber auch dann ist es besser, die zu überlagernden Funktionen im
Linker-Script anzugeben, statt die Variablen global zu machen.


Peter

von A.K. (Gast)


Lesenswert?

@PeDa: Wenn das der Compiler selber sauber hinkriegt ist das prima. Man
macht sich halt von spezifischen Eigenschaften des Compilers abhängig.
Kann hier aber schon sinnvoll sein, denn es geht ja um die Übernahme
fremden Codes.

von Martin D. (eric_arthur_blair)


Lesenswert?

Also. Um das ganze nochmal klar zu bekommen. Der Datenspeicher des PICs
wird über einen 8-Bit Pointer adressiert, womit sich bis zu 256 Byte
ansprechen lassen. Dazu gibt es für die direkte Adressierung noch ein
Bank Select Register (BSR), mit dem 15 verschiedene Memory Banks
ausgewählt werden können. Es lassen sich also insgesamt 4096 Bytes
adressieren. Für die indirekte Adressierung gibt es entsprechend über
die File Select Register (FSR = FSRH + FSRL) einen 12-Bit-Pointer, mit
dem sich genau dieser Datenbereich anwählen lässt. Wenn ich das richtig
versetehe, dann wird beim normalen Zugriff auf den Datenspeicher der
8-Bit-Pointer verwendet, wobei dann ja irgendwie hard-kodiert der
Wechsel zu einer anderen Memory Bank festgelegt werden muss, sobald die
256 Byte einer Bank ausgeschöpft sind.
Nun zu der Frage mit dem Stack. Der Return Stack ist hier ja nicht
gemeint, weil der weder im Daten-, noch im Programmspeicher liegt und
eine fixe größe von 32x21 Bit hat. Der Stack zum Speichern der lokalen
Variablen einer Funktion befindet sich also im Datenspeicher. Ist denn
jetzt das Problem, dass der Stack Pointer nicht über die Grenze einer
Bank inkrementiert werden kann, oder warum gibt es Ärger, wenn die
Größe der lokalen Variablen 256 Bytes überschreiten?

Wenn ich nun über die Compiler-Option -sco die default storage class
auf overlay festlege, ist dann der Compiler oder der Linker schlau
genug, die richtigen Partner für das "Memory Sharing" auszuwählen?

Gruß,

Martin

von Martin D. (eric_arthur_blair)


Lesenswert?

Ich habe noch eine Frage zu der Thematik mit den Memory Banks:

Wenn ich im Linkerscript eine Datensection anlege, die mehrere Banks
umfasst, wie ist der PIC dann überhaupt in der Lage die Section
komplett zu adressieren, wenn er nur eine 8-Bit-Adresse verwendet?

von Gerhard Gunzelmann (Gast)


Lesenswert?

Hallo

für solch RAM-hungrige Programme sind die neuen PICS (dsPIC30- oder
PIC24-Derivate) besser geeignet. Da gibts RAM bis 16k.

Gerhard

von Martin Domke (Gast)


Lesenswert?

Was solls… Der Schaltplan ist jetzt schon gedruckt und der PIC schon
ausgewählt…

von Dieter Werner (Gast)


Lesenswert?

Da in C häufig mit Pointern gearbeitet wird nehme ich mal stark an dass
die Adressierung mit einem der 3 fsr (12 Bit) erfolgt.
Damit erreicht man alle 16 Bänke (zu je 256 Byte).

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.