Forum: Compiler & IDEs Frage zu "sections" im ext. memory


von OldBug (Gast)


Lesenswert?

Guten morgen zusammen!

Meine ersten Geh-Versuche mit externem SRAM scheinen relativ erfolglos
zu sein.

Angenommen, das externe SRAM liegt ab Adresse 0x2000 (0x802000 für den
Linker). Ich möchte gleich zu Anfang des externen SRAMs eine section
"bekannt machen". Dazu habe ich folgende Zeile im Makefile ergänzt:

LDFLAGS += -Wl,--section-start=.extmem=0x802000

So weit, so gut.

Jetzt möchte ich ein Array in dieser section reservieren:

[..]
#define EXT_MEM_ __attribute_ ((section (".extmem")))
[..]
uint8_t extSRAM0[128] EXT_MEM;
[..]
    extSRAM0[0] = 0xAA;
[..]

Kompilieren lässt sich das ohne weiteres, aber dann fleigt ein Stock in
die Speichen:
Das erzeugte .hex lässt sich im AVR-Studio nicht öffnen. Meldung:

"The contents of the objectfile exceeds the maximum program
 memory of the device
 Error loading objectfile xyz.hex"

Ich vermute, daß es daran liegt, daß meine section keine "no-init"
section ist, und ext. SRAM eben nicht während des Programmiervorganges
erreichbar ist.
Gibt es da eine einfache Lösung?

von Jörg Wunsch (Gast)


Lesenswert?

Meiner Erinnerung nach kannst du leider nicht so einfach eine section
für den RAM ,,erfinden''.  Erfundene sections werden, sofern man
nicht
den Linkerscript ändert, dem ROM zugeschlagen.

von OldBug (Gast)


Lesenswert?

Aha, hm, OK!

Dann stellt sich mir jetzt erst mal nur noch die Frage, welches das
Default-Linkerscript für einen Mega128 mit ext. RAM ist.

von Jörg Wunsch (Gast)


Lesenswert?

${prefix}/avr/lib/ldscripts/avr5.x

Solltest du mit avr-gcc -v beim Linken auch sehen.

Externer RAM oder nicht ist egal, wird nicht unterschieden.

von OldBug (Gast)


Lesenswert?

Mittlerweile glaube ich, daß das eine blöde Idee ist.
Einfach alles bis auf den Stack nach extern verlagern und gut ist.

Oder gibts da Einwände?

von Jörg Wunsch (Gast)


Lesenswert?

Ich würde es auch so machen, sofern es halt auf den einen zusätzlichen
Zyklus beim RAM-Zugriff nicht ankommt.

Alternative: Stack und Variablen im internen RAM halten und den Heap
in den externen RAM legen.  Diese Variante ist auch recht einfach
einzurichten.  Alles, was nach extern gehen soll, wird dann via
malloc() gemacht.

von Fritz Ganter (Gast)


Lesenswert?

So hab ichs auch gemacht, externes RAM ist mind. 1 Takt langsamer, und
ich brauchte es nur für einen Haufen Daten, also die alle über malloc()
und Zeiger ansprechen.
Ausserdem kannst du hier keine Heap/Stack Kollision bekommen.

von OldBug (Gast)


Lesenswert?

@Fritz:

Ja, genau.
Ich wollte dem Stack dann aber gleich "richtig" Platz machen :)

Naja, mal sehen, wie schnell das dann ist.
Ich werde sicher beide Varianten Testen, danke für eure Hilfe!

von Hagen (Gast)


Lesenswert?

@OldBug:

du baust ja am AVR-Ethernet projekt, richtig ? Ich vermute das deine
Frage in direktem Zusammenhang zu diesem Projekt steht.

Über den CPLD wird ja bis zu 512Kb externer SRAM dynamisch in zwei
Bänke in den AVR eingeblendet. Der interne 64Kb Addressbereich des XMEM
Interfaces wird in die Bänke 0x0000-0x7FFF und 0x8000-0xFFFF gemappt.
Nun zum Problem: verlagerst du den Stack in den externen SRAM, sprich
zb. in Addressbereich unterhalb 0xFFFF so hast du Probleme mit dem
Memory Bank Switching. Ein Switching der oberen RAM Bank wird so gut
wie unmöglich da du ja dann direkt die Daten des Stack's über das
Memory Bank Switching wegschaltest.

Der Stack sollte meiner Meinung nach immer im internen SRAM liegen, da
dieser in jedem Falle ganz unabhängig vom externen und Memory Banked
SRAM immer zur Verfügung steht.

@Jörg Wunsch: Du bist der Experte für WinAVR-gcc und deshalb wäre es
sehr hilfreich deine Meinung dazu zu hören.

Ich habe das VHDL für den CPLD programmiert und laut Guido's/OldBug's
Tests funktioniert das schon sehr gut. Allerdings könnte ich zu jeder
Zeit noch Änderungen am Memory Banking Konzept durchführen, falls sie
eben die Arbeit mit WinAVR und AVRStudio sinnvoller machen sollten.

Momentan sieht es so aus, für einen ATMega128:
Die internen 64Kb MemorySpace sind primär in zwei Bänke aufgeteilt.
0x0000-0x7FFF Bank 0
0x8000-0xFFFF Bank 1

Es ist klar das in Bank 0 noch die internen Bereiche überblendet
werden, sprich interner SRAM + Memory Mapped Geräte:

0x0000-0x10FF interner SRAM
0x1100-0x11FF Memory Bank Register
0x1200-0x12FF externer 8 Bit Port
0x1300-0x13FF USB-FTDI
0x1400-0x14FF Realtek
0x1500-0xFFFF externer SRAM, banked in 512Kb

Extern kann nun bis zu 512Kb an SRAM angesprochen werden. Dazu müssen
die obersten 4 Addressleitungen A15-A18 des SRAM's über den AVR im
Memory Bank Register vorgegeben werden. Je nach Zugriff auf den
externen Speicher wird zwischen Addressen in Bank 0 und Bank 1
unterschieden. Wird in Bank 0 gelesen/geschrieben so legt der CPLD das
unterste Nibble im Memory_Bank_Register auf A15-A18. Wird auf Bank 1
zugegriffen so wird das obere Nibble an A15-A18 gelegt.
Der CPLD macht also automatisch die Bank Auswahl und der Programmierer
hat durch den Zugriff auf Addresse 0x1100 die Möglichkeit in einem Byte
die zwei auszuwählenden 32Kb SRAM Bereiche für die zwei internen Bänke
vorzugeben. Somit kann man 2^4 = 16 * 32Kb = 512Kb des externen SRAM's
ganz individuell in jeweils 2*32Kb des XMEM's einblenden.

Nungut, es wird jetzt klarer warum man den Stack eben nicht in den
externen SRAM auslagern sollte. Also nicht nur weil er 1 Takt
langsammer ist sondern weil man das Memory Banking damit zumindesten
für die oberste Bank unmöglich macht. Würde man Bank 1 ständig in
andere Addressbereich im ext. SRAM einblenden so würde man ja dem
Program den Stack unterm Arsch wegreißen.

Was wäre nun deiner Meinung nach die beste Alternative ein solches
System für WinAVR und AVRStudio fit zu machen ?
Ich bin nämlich auch davon ausgegangen das man im MakeFile zusätzliche
Sections definiert. Einmal Sections für den einfachen Zugriff auf die
Memory Mapped Geräte, für jeden obigen Addressbereich der Geräte eine
eigene Section.
Und dann jeweils zwei Sections für jede der zwei Bänke. Der Inhalt,
sprich der extern real gemappte Addressbereich wäre dann dynamisch,
Immerhin ging ich davon aus das die 512Kb externer SRAM hauptsächlich
für große zusammenhängende Datenmengen benutzt wird, sprich
HTML/XML/Script Parser usw. Der interne SRAM wäre dem Stack
vorbehalten. Bank 0 die sowieso nicht vollständig verfügbar wäre durch
die überblendeten Addressbereiche würden dann für Variablen zur
Verfügung stehen.

Es ist alles ziemlich schwer zu erklären und eventl. auch kompliziert,
aber genau deshalb würde mich deine Meinung dazu brennend
interessieren.

Immerhin sollen auch weniger erfahrene Programmierer mit WinAVR und dem
Ethernet projekt zurechtkommen,und da wäre ein fertig konfektioniertes
MakeFile und LinkerScript wohl die beste Lösung.

Gruß Hagen

von Hagen (Gast)


Angehängte Dateien:

Lesenswert?

Hier mal eine kleine Grafik, vielleicht wirds dadurch besser
verständlich.

Links die 64Kb des AVR's, rechts der 512Kb SRAM.
Rot die Bank 0 im AVR und grün die Bank 1.
Rechts neben dem 8Bit großen Memory Bank Register der hypothetische
Wert von 0x72. Die 2 selektiert also die Bank 2 des externen SRAM und
blendet sie in die Bank 0 des AVR's ein. Die 7 selektiert die Bank 7
des ext. SRAMS und blendet sie in Bank 1 des AVR's ein.

Der blaue Breich ist immer der fixe Speicherbereich mit fester
Zuordnung. Dieser überblendet immer den untersten Speicherbereich der
Bank 0. Es entsteht also in Bank 0 ein Shaddow RAM von 0x1500 Bytes.

Mit der Konfiguration von 0x72 im Memory Bank Register wird also im
Speicherbereich 0x0000-0x7FFF der externe SRAM von 0x10000-0x17FFF und
im Speicherbereich 0x8000-0xFFFF der externe SRAM von 0x38000-0x3FFFF
eingeblendet. Der CPLD erkennt nun an Hand der Addressleitung A15 ob
man in Bank 0 oder Bank 1 schreibt oder liest und aktiviert die
obersten Addressleitungen entweder mit dem untersten oder obersten
Nibble des Memory Bank Registers.

Gruß Hagen

von Jörg Wunsch (Gast)


Lesenswert?

Naja, Stack in externen RAM würde ich schon aus Performancegründen
wirklich nicht machen.

Eine andere Idee bezüglich der Bankumschalterei, falls es noch nicht
zu spät ist.  Ich hatte seinerzeit auf meinem CP/M-Rechner einen
schnellen RAM (Schottky-TTL 74S89, die haben richtig was weggeheizt),
der hat die oberen 4 Adressleitungen des Z80 in 8 Adressleitungen
uminterpretiert, so dass ich 16 Speichersegmente zu je 4 KB hatte im
logischen Adressraum und bis zu 1 MB physischen Speicher adressieren
konnte.  (Die Reset-Mimik hat den RAM nach einem Hardware-Reset
ausgeschaltet, damit er mit physischen Adressen arbeitet und den
Urlader findet.)

Der zusätzliche RAM wurde dann für RAM-Disks genutzt.  Das stelle ich
mir allemal auch für euer Projekt sinnvoller vor, als das alles über
dem Compiler bekannte Speichersegmente machen zu wollen.  Durch die
feine Segmentierung konnte man recht gut verschiedene Speicherblöcke
(RAM, ROM, Framebuffer) parallel einblenden, außerdem war für das
Lesen oder Schreiben der RAM-Disk immer nur ein Kopiervorgang nötig
statt mehrerer (und den habe ich noch per DMA abhandeln können ;-).

von Hagen (Gast)


Lesenswert?

Hi Jörg,

aus den gleichen Gründen wie du sie ansprichst habe ich den CPLD
designt. Ich würde die 4Kb internen SRAM eben auch als Stack + globalen
Variablen benutzen. Für die nötigen Buffer, sei es SD/MMC FAT Software,
Realtek Netzwerk Software oder die einzelnen Netzwerkprotokolle würde
ich eben die 32Kb Blöcke im externen SRAM einblenden lassen. Auch in
diesem Projekt managed der CPLD die obersten Addressleitungen, aber
eben automatisch samt Addressdekodierung der Memory Mapped Geräte. So
gesehen besteht HW-mäßig kein Unterschied.

Allerdings zielte meine spezielle Frage an dich eben eher in Richtung
Software und die beste Anpassung an die HW die mit WinAVR gcc möglich
ist. Ich hätte dazu eben die einzelnen Sections im Makefile definiert,
aber allerdings kann eben AVRStudio damit nicht umgehen.
Was bleibt denn dann noch übrig als über Addresspointer auf den banked
SRAM zuzugreifen ? Elegant wäre das ja wohl nicht ?
Auch ob der integrierte dynamische Speichermanager in WinAVR eine gute
Wahl ist würde ich bestreiten. Kann der den auch mit einem solchen
Banking umgehen ? Ideal wäre es wenn man mit Hilfe des WinAVR Compilers
vollständig transparent auf die 512Kb externen SRAM zugreifen könnte.
D.h. wäre es denn möglich über Makro o.ä. die Zeigerarithmetik und
Speicherzugriff so zu kapseln das der Compiler abhängig von der
Addresse selbständig die Bänke umschaltet ?

Gruß Hagen

von Fritz Ganter (Gast)


Lesenswert?

Hagen schrieb:
>vollständig transparent auf die 512Kb externen SRAM zugreifen könnte.
>D.h. wäre es denn möglich über Makro o.ä. die Zeigerarithmetik und
>Speicherzugriff so zu kapseln das der Compiler abhängig von der
>Addresse selbständig die Bänke umschaltet ?

Vielleicht verstehe ich da was falsch, aber wie soll das gehen? Dazu
bräuchte man 32bit Zeiger.
Man könnte das nur in eine Funktion kapseln, wie

int memr (uint32_t addr);
memw(uint32_t addr,int16_t &val);

a=mem(blablub);
Müsste man aber für 8,16 und 32 bit machen.

Aber vielleicht hab ich ja nix kapiert.

von Hagen (Gast)


Lesenswert?

Richtig ich dachte da auch an 32Bit oder eventl. 24Bit Zeiger.
Allerdings wäre das auch nur eine Lösung um für ganz ungeübte
Programmierer die Sache zu erleichtern, da auf alle Fälle der erzeugte
Code ziemlich ineffizient gegenüber den 16Bit Zeigern wäre.

Ich kenne mich eben mit den Möglichkeiten wie tief und intensiv man in
WinAVR und dessen Codeerzeugung eingreifen kann nicht aus.
Die Idee wäre einen eigenen neuen Zeigertyp der 32Bit breit ist zu
definieren. Die Regeln die der Compiler nun nutzen muß um eine
Speicheraddresse zu lesen müssten manuell angepasst werden. Die
obersten 4 Bit der 19Bit Addresse müsste ausgewertet werden und in das
Bankingregister geschrieben werden. Naja, war auch nur so eine dumme
Idee von mir.

Gruß Hagen

von Jörg Wunsch (Gast)


Lesenswert?

Ich denke, wenn du sowas zimmern willst, musst du wohl oder übel einen
angepassten Compiler bauen.  Nicht, dass ich es für unmöglich hielte,
aber dieser Aufgabe musst du dich dann stellen.  Damit kannst du die
Bankumschaltung natürlich auch optimieren, indem der Compiler das
Umschaltregister cachen lässt und es nur neu ausgibt, wenn eine andere
Bank benötigt wird.

von OldBug (Gast)


Lesenswert?

Halt halt!

Ich wollte den Stack gar nicht nach extern verlagern, sondern "nur"
alles andere, sprich Variablen, dynamische Speicherreservierung.
Stack bleibt auf jeden Fall intern.
Und die Variablen werden wohl aus den gleichen Gründen, die Hagen gegen
den Stack vorgebracht hat wohl auch intern bleiben müssen.

Also bleibt nur die Dynamische Speicherreservierung übrig. Und dazu
könnte man evtl. sogar noch ein malloc anpassen oder selber schreiben,
sodaß das Bankswitching auch wieder "automatisiert" wird.

Aber ich arbeite auch grad an einer anderen Ecke, bevor ich wieder an
das SRAM komme.

Ich werd mich da auf jedenfall noch mal zu melden...

Danke schon mal!

von Hagen (Gast)


Lesenswert?

Über die dynamische Speicherallozierung hatte ich mir auch schon
Gedanken gemacht. Leider kann man die in WinAVR integrierte Version
auch nicht so einfach benutzen. Entweder man benutzt für jede 32Kb eine
eigene Speicherverwaltung und kann dann mit 16Bit Pointern arbeiten.
Dann muß man aber vor der Benutzung=Zugriff mit solchen Pointern immer
erst die korrekte Page zum Pointer aktivieren.
Der Ausweg wäre die Benutzung von 32Bit Segementierten Zeigern. Die
untersten 16Bit gestatten als Zeigeroffset den direkten Zugriff mit den
herkömmlichen Funktionen, alsowie gewohnt. Die oberen 16Bit der
Zeiger=Segmente definieren die Speicherseite und werden nur beim
Allozieren/Deallozieren/Aktivieren der Speicherpage benötigt. Man
verändert also zb. Malloc so das es einen 32Bit Zeiger liefert. Will
man nun über diesen Zeiger Speicher lesen/schreiben so muß man diesen
mit Hilfe eines Makros in einen 16Bit Zeiger umwandeln. Dabei würde das
Makro einfach als Funktion nur den 16Bit Zeigeroffset des 32Bit Zeigers
zurückgeben. Allerdings eben vorher überprüfen ob im zugehörigen
Bankingregister der richtige Wert drinnen steht. Da wir effektiv nur
24Bit benötigen haben wir sogar noch weitere 8Bits im Segmentteil des
32Bit Zeigers übrig. Diese 8 Bit können dann für spezielle Funktionen
im Malloc()/Speichermanager verwendet werden.

Leider habe ich bisher eben nur auf 32Bit PC einen Speichermanager
programmiert, und der arbeitete eben nicht mit solchen Segementierten
Speichern.

Auf alle Fälle wäre das aus meiner Sicht der einzigst vernünftige Weg
wenn man die Verwaltung der 512Kb SRAM auch für Anfängerprogrammierer
nützlich machen will. Klar, ein erfahrener Programmierer wird keine
Probleme haben auf so einer Hardware mit einfachen 32Kb Datenblöcken zu
arbeiten.

Gruß Hagen

von Hagen (Gast)


Lesenswert?

@Jörg:

"Damit kannst du die
Bankumschaltung natürlich auch optimieren, indem der Compiler das
Umschaltregister cachen lässt und es nur neu ausgibt, wenn eine andere
Bank benötigt wird."

Das Caching wäre nichtmal nötig. Das Bank Register im CPLD kann
geschrieben wie auch gelesen werden. Der CPLD hat im Normalfalle ein
Troughput von 16ns ist also viel schneller als das XMEM Interface des
AVR's mit ca. 62ns. Deshalb kann man auch ohne Waitstates auf den
externen SRAM und Memory Mapped Geräte zugreifen.
Ergo: ein gecachtes Bankregister im AVR wäre gerade mal 1 Takzyklus
schneller als wenn man den CPLD direkt auslesen würde.

Bisher tendiere ich zu einem Mix aus 32/16 Bit Zeigern. Die 32Bit
Zeiger dienen zur Allokation/Deallokation im Speichermanager und müssen
vor ihrer Benutzung in reale 16 Bit Zeiger per Makro konvertiert werden.
Innerhalb dieses Makros wird die Bankumschaltung ermöglicht. Da man 2
Bänke zur Verfügung hat kann man diese bequem auf Interruptbasierte und
nicht Interruptbasierte Programstücke aufteilen.

Gruß Hagen

von Jörg Wunsch (Gast)


Lesenswert?

Naja, ich würde dir nochmal nahelegen, mehr als 2 Bänke zu
realisieren.  Auf diese Weise kannst du gut einen Bereich aufsetzen,
der deine IO-Geräte hat, einen, in dem man externe Variablen oder
malloc() benutzen kann, und den Rest für ein Dateisystem.  Die
erstgenannten Mappings bleiben dabei über die Laufzeit praktisch
konstant, nur das Dateisystem muss umgeschaltet werden.  Auch dort
sind aber kleinere Segmente sicher nicht unpraktisch, bspw. könnte man
das Directory fest in einem Segment platzieren und muss dann nur noch
die Datensegmente umschalten.

Durch die Dateiarbeit hälst du all den Overhead für die
Bankumschaltung aus den ,,normalen'' Zeigern der Applikation heraus.

Zwar geht das sicher auch mit nur 2 Bänken (den unteren Teil lässt du
permanen gemappt, den oberen schaltest du für die Dateiarbeit um),
aber dann brauchst du mehr Umschaltungen und mehr Hin- und
Her-Kopieren über Speicher in der unteren Bank, weil du zwischen
verschiedenen Zielbereichen der oberen Bank nicht direkt kopieren
kannst.

Irgendeine Form von ,Betriebssystem' braucht das Teil dann sowieso,
das kann auch das Dateisystem implementieren.

Patrick, nein, ein Modifizieren des malloc() allein würde nicht
genügen, der erhaltene Zeiger muss ja auch die Bankinformation mit
sich tragen und es muss bei jedem Zugriff über einen derartigen
Zeiger eine Bankumschaltung eingeleitet werden.

Hagen, du hast das mit dem Cachen falsch verstanden.  Was ich meinte
ist, wenn du den Compiler modifizierst, dann kann er sich *im
Compiler* merken, wie der derzeitige Bankstatus ist, sodass bei
aufeinanderfolgenden Zugriffen auf Zeiger in einem bankgeschalteten
Bereich die Umschaltung nicht neu erfolgen muss, wenn der Compiler
sich sicher sein kann, dass die Einstellung noch stimmt.
Interruptroutinen müssen natürlich dann unbedingt die Bankregister
vorher retten und danach wiederherstellen, das ist weniger das
Problem, aber ich denke trotzdem, dass der Aufwand für eine derartige
Implementierung in keinem Verhältnis zum Nutzen steht.  Ich habe noch
auf einer PDP-11 unter RSX-11 angefangen, dort konnte man auch den
Speicher via MMU segmentieren und der Linker (dort Taskbuilder
genannt) hat das Anlegen von overlay-Segmenten unterstützt, aber das
war trotz vorhandener Tools ein grässlicher Aufwand.

Lass einen Teil konstant im Mapping und nimm den Rest für ein
Dateisystem, das dürfte aus meiner Sicht das beste Verhältnis von
Aufwand und Nutzen ergeben.  Du kannst den ,normalen' Speicher dann
mit Bordmitteln ansprechen, ohne an den Tools was ändern zu müssen,
und hast den restlichen RAM trotzdem noch als umfangreiches Datengrab
relativ einfach verfügbar.

von Hagen (Gast)


Lesenswert?

Hi Jörg:

"Naja, ich würde dir nochmal nahelegen, mehr als 2 Bänke zu
realisieren."

Das wäre sogar relativ einfach möglich. Man teilt Bank 0 in noch mehr
Bänke auf. Dabei wäre zb. direkt nach den Memory Mapped Geräte zb. eine
feste Bank drinnen. Zb. die ersten 16 Kb der Bank 0 würden immer in die
ersten 16Kb des externen SRAMs gemappt. Allerdings sehe ich jetzt nicht
den großen Vorteil darin.

Ich hätte es sowieso so gemacht das Bank 0 fast immer fest auf Page 0
im SRAM mappt. Die Änderung des Mappings von Bank 0 würde nur für
Spezialaufgaben -> Kopieren innerhalb der Pages oder Zugriff auf die
0x1500 Kb des Shaddow RAMs, benutzt. Somit wäre es ohne große Risiko
möglich die globalen Variablen im Program in Bank 0 zu legenund nur den
Stack im internen 4Kb SRAM. Allerdings WIE sage ich es möglichst clever
dem Compiler ? ohne das AVRStudio mit den nun nötigen Sections nicht
zurechtkommt?

Das Sichern des Bank Registers in den IRQ's ist übrigens eine gute
Idee, auf diese simple Lösung bin ich noch garnicht gekommen :)

Gruß Hagen

von Jörg Wunsch (Gast)


Lesenswert?

Naja, du musst doch nur dem Compiler sagen, ab wo er mit den Variablen
beginnen soll.  Damit sind wir ja fast wieder am Ausgangspunkt. ;-)

von Hagen (Gast)


Lesenswert?

Resumee: es gibt die Möglichkeit solche globalen Variablen in eigene
Sections zu verfrachten, ergo in eigene Speicherbereiche. Aber
AVRStudio kann damit nichts anfangen. Ansonsten gibts in WinAVR keine
andere Möglichkeiten. Somit müssten die globalen Variablen samt Stack
in den internen SRAM gelegt werden indem man das Linkerscript benutzt
das nur 4Kb SRAM für den ATMega128 vorsieht.

Oder wäre es denn möglich das Linkerscript so abzuändern das es mit
32Kb SRAM arbeitet, die standardmäßige Initialisierung des Stacks wird
aber innerhalb des Programmes überschrieben und auf den 4Kb internen
SRAM beschränkt ? Vorraussetzung wäre das der Linker den Stack
unterhalb der globalen Variablen einrichtet. Dann würde der Linker ja
keine separate Sections benötigen und AVRStudio kämme eventl. klar
damit.

Gruß Hagen

von Jörg Wunsch (Gast)


Lesenswert?

Das hat alles gar nichts mit dem Linkerscript zu tun, das kannst du
alles von der Kommandozeile aus einstellen.  Der Stack ist nur ein
wenig tricky, da er zweimal initialisiert wird, einmal im Startup-Code
und einmal am Anfang von main().  Das ist ein Bug (war mal ein Feature
für eine Übergangszeit, als es der Startupcode noch nicht getan hat).

Wenn du allerdings Variablen nahtlos vom internen in den externen RAM
übergehen lassen willst, dann solltest du deine Geräte-MMIO nicht
direkt bei 0x1100 anfangen lassen sondern dort RAM haben.

von Hagen (Gast)


Lesenswert?

Hi Jörg,

ok ein par direkte Fragen, die sparen mir die Sucherei in den WinAVR
libs:

1.) der Stack liegt unterhalb der globalen Variablen und dekremtiert in
Richtung Register File, richtig ?

2.) die globalen Variablen haben feste Länge und somit kann der
Compiler den Gesamtbedarf an Speicher dafür berechnen, deshalb liegen
sie normalerweise über dem Stack und unterhalb RAMTop, richtig ?

3.) was muß ich wie konfigurieren das StackTop bei 0x10FF beginnt

4.) was muß ich konfigurieren das der Compiler die globalen Variablen
direkt oberhalb der Addresse 0x1500 ablegt, so daß der Rest des SRAM
oberhalb 0x1500 + sizeof(Vars) bis 0x7FFF noch zur freien Verfügung
steht

Die Memory Mapped Geräte wurden absichtlich oberhalb 0x1100 bis 0x1500
positioniert. Blendet man nämlich Page 0 und Page 1 des ext. SRAMs in
Bank 0 und Bank 1 ein so hat man einen linearen Speicherbereich von
58Kb Bytes. Der Shadow RAM würde an einem Stück im Bereich
0x0000-0x1500 liegen und nicht geteilt.
Aber am wichtigsten wäre der Unterschied zu den möglichen Alternativen.
Ich schätze mal das wenn die Memory Mapped Geräte woanders platziert
würden sie dann meistens unterhab 0xFFFF stehen würden, also am Ende
der Bank 1. In diesem Falle wäre keine der beiden Bänke je voll
zugreifbar, den Bank 0 hätte am Anfang einiges an Shadow RAM und Bank 1
eben am Ende für die Memory Mapped Geräte. Und um genau dies zu
vermeiden wurden sie "bündig" in Bank 0 platziert. Man hat so in Bank
1 immer vollen 32Kb Zugriff auf die kompletten 512 Kb externen SRAM,
einfach indem man die entsprechende Page in Bank 1 einblendet.

Soweit zu meiner Entscheidung es so zu machen wie es jetzt realisiert
ist. Ich würde also sehr ungern daran etwas ändern.
Zudem bin ich der Meinung das sich in solchen Sachen der Compiler mit
seinen Features den Gegebenheiten der Hardware unterzuordnen hat.

Und damit sind wir wieder beim Anfang: Wie muß man WinAVR am cleversten
konfigurieren.

1.) Stack + globale Variablen im internen 4Kb SRAM, alles andere wird
in große Happen aufgeteilt und als rießige Buffer zb. für FAT benutzt.
Diese Lösung funktioniert ohne Probleme auch im AVRStudio mit dem
jetzigen WinAVR ggc, richtig ?

2.) Stack im 4Kb internen SRAM, globale Variablen gleich im Anschluß an
die Memory Mapped Geräte oberhalb 0x1500. Page 0 des ext. SRAM's wäre
dann die Default-Page in Bank 0. Nur für ganz besondere Aufgaben wird
Bank 0 mit einer anderen Page belegt. Der Rest oberhalb der globalen
Variablen wäre frei verfügbar. Ich würde aber dort am liebsten eine
rießige globale Variable ablegen die den Rest bis 0x7FFF belegt. Der
darin enthaltene Speicher könnte durch einen Mini-Speichermanger
verwaltet werden, zb. für Dateisystem etc.
Ich vermute das dies mit WinAVR nicht so einfach zu realisieren ist, da
er dafür ausgehend von RAMBottom die globalen Variablen nach oben hin
verteilen müsste. WinAVR gcc verteilt sie aber von Oben nach Untern im
Datensegement, richtig ?

3.) Stack im int. 4Kb RAM, und keine globalen Variablen. Stattdessen im
SRAM->Page 0 oberhalb 0x1500-0x7FFF einen dynamischen Speichermanager
und alle größeren globalen Variablen müssen dynamsich alloziert werden.
Man hätte dann immernoch eine riesige Menge an Stack zur Verfügung !

4.) Stack + globale Variablen im int. 4Kb RAM. Oberhalb 0x1500-0x7FFF
einen Speichermanager. Im Normalfalle ist es selbst bei großen
Projekten eher selten der Fall das 4Kb RAM nicht für den Stack +
globale Variablen ausreichen werden, besonders wenn man dann noch 26Kb
an dynamischen Speichermanager zur Verfügung hat.

Was würdest du als relativ universellste Lösung vorziehen ?

Gruß Hagen

von OldBug (Gast)


Lesenswert?

Ich würde, nach meinen bisherigen recherchen, Variante 4. verwenden.
Das ist eigentlich "nur" ein klick in Mfile (für Einsteiger sehr
hilfreich).
In Form einer Makefile-Zeile sieht das dann so aus:

EXTMEMOPTS =
-Wl,--defsym=__heap_start=0x801500,--defsym=__heap_end=0x80ffff

Mit korrekter Initialisierung des Bank_Reg im CPLD (genauer: Bank_1)
würden von malloc somit Automatisch 60.159bytes alloziert werden
können.

Wenn man sich darauf beschränken möchte.
Möchte man das nicht, so muss man sich eh mit der ganzen Sache sehr
viel tiefer beschäftigen und auch eine abgewandelte malloc
Implementation verwenden können, oder gar selbst erstellen können.

von Hagen (Gast)


Lesenswert?

Ok, super OldBug.

Page 0 & 1 des ext. SRAM wären dann standardmäßig die aktiven Bänke 0 &
1. Alle anderen Funktionen wie zb. FAT usw. würden in Bank 1 ihre Buffer
in andere Pages > #1 des ext. SRAMs mappen. Einzigstes Problem dabei ist
das diese Funktionen ohne Malloc() auskommen müssten. Weiteres Problem
wäre das der Zugriff auf zb. Dateibuffer in Bank 1 ausserhalb der FAT
Routinen ebenfalls ohne Malloc() auskommen müssten.

Deswegen würde ich lieber den verfügbaren Speicher des Speichermanagers
mit

EXTMEMOPTS =
-Wl,--defsym=__heap_start=0x801500,--defsym=__heap_end=0x807fff

auf 0x1500-0x7FFF in Bank 0 beschränken. Das wären immer noch 28Kb.
Dafür wäre nun Bank 1 völlig frei und ohne die obigen Malloc()
Beschränkungen kann in den FAT Routinen und allen anderen Routinen auf
die Buffer in Bank 1 direkt zugegriffen werden.
Wie siehts denn mit der Effizienz des Speichermanagers in WinAVR aus ?
Gibts da eventl.WEB Seite mit tiefgehenderen Informationen ?

Es wird Zeit das ich endlich meine Platinen von Guido bekomme :)
Ich möchte nämlich die Idee mit den segmentierten 32Bit Zeiger und
vollständig dynamisch verwalteten Speicher noch nicht ganz fallen
lassen. Im Grunde würde das zwar pro 32Bit Zeiger mehr Resourcen
verbrauchen, allerings greift man nach Umwandlung dieser Zeiger in
16Bit Zugriffzeiger dann so auf die gemappten Pages zu als hätte man
nur max. 64Kb Speicher zur Verfügung. D.h. der Performanceverlust durch
diese 32Bit Zeiger beschränkt sich auf sehr wenige Taktzyklen.
Der Speichermanager selber würde mit 32Kb Seiten arbeiten und könnte so
ziemlich elegant das Paging zur Defragmentation ausnutzen.

Gruß Hagen

von geloescht (Gast)


Lesenswert?

Dieser Beitrag wurde auf Wunsch des Autors geloescht.

von OldBug (Gast)


Angehängte Dateien:

Lesenswert?

@Hagen:

Ich hab jetzt mal meine 128kB RAM aufgelötet :)
Sieht gut aus bis jetzt, im Anhang mal meine UART-Debugs...

Eingerichtet mit:

EXTMEMOPTS =
-Wl,--defsym=__heap_start=0x801500,--defsym=__heap_end=0x807fff

[..]
volatile uint8_t *pSRAM = (uint8_t *) 0x8000;
volatile uint8_t *pBank_Reg = (uint8_t *) 0x1100;
[..]
        *pBank_Reg = (1 << B1P1); /* setup CPLD bank-switching */
        uint8_t *pExtMallocBlock = NULL;
[..]
                for(pSRAM = (uint8_t *) 0x8000;
                    pSRAM < (uint8_t *) 0xFFFF;
                    pSRAM++)
                {
                        *pSRAM = data++;
                }

                pExtMallocBlock = malloc(256);
                memset(pExtMallocBlock, 0xAA, 256);
[..]
                mem_dump(pExtMallocBlock, (size_t) 0xFF);
                free(pExtMallocBlock);

                mem_dump((void *)0x8000, (size_t) 0xFF);
[..]

...nur 0xFF bytes, weil sonst die Ausgabe auf der UART ein Wenig zuuuu
lang würde :)

Ich spiele aber grad auch noch ein wenig rum.

von Hagen (Gast)


Lesenswert?

Wo kommen die Daten ab 0x8000 her ? Das sieht so aus als würde er die
unteren Addressen als Daten spiegeln, das wäre schlecht !

Desweiteren gefällt mir eben das nicht:

volatile uint8_t *pSRAM = (uint8_t *) 0x8000;
volatile uint8_t *pBank_Reg = (uint8_t *) 0x1100;

Schade das AVR Studio nicht die .sections die man in WinAVR erzeugen
kann versteht. Mit solchen Sections wäre die "blöde"
Zeigerschreibweise vom C zu umgehen. Sähe dann inetwa so aus

volatile uint8_t SRAM[0x8000] SRAM_BANK1_SECTION;
volatile uint8_t Bank_Reg CPLD_BANK_REG_SECTION;

Und Zugriffe dann statt:

*pBank_Reg = (1 << B1P1);

eben

Bank_Reg = (1 << B1P1);
SRAM[1234] = 14;

Naja, vieleicht gibts noch andere Wege.

Du hast dich aber auch auf folgende Logik "festgelegt":

- Bank_0 fast immer in ext. SRAM Page 0 -> 0x00000-0x07FFF
- Bank_1 variabel in ext. SRAM Page x
- ab 0x1500 in Bank_0 bis 0x7FFF den dynamischen Speichermanger
- Stack und globale Variablen im int. SRAM

Ich meine auch das dies die beste Variante ist.

Gruß Hagen

von Hagen (Gast)


Lesenswert?

Übrigens bei 128 Kb SRAM kannst du die obersten beiden Bits der
Bank_Reg_Nibble als normalen 2Bit Ausgabeport benutzen. Je nach
Speicherzugriffsaddresse < 0x8000 oder >= 0x8000 werden dann die freien
Addressleitungen A18 udn A17 aus diesen Bits gespeisst.

Das selbe gilt auch für Zugriffe in die Memory Mapped Devices. Zb. beim
Zugriff auf den Realtek könnte man vorher die untersten 2 Bits im
Bank_Reg belegen und als zwei zusätzliche Steuerleitungen für den
Realtek benutzen. Diese Steuerleitungen müssten vom RealTek an A18/A17
gehen.

Gruß Hagen

von Jörg Wunsch (Gast)


Lesenswert?

Klar gibt's andere Wege.  VMLAB und GDB sollten damit problemlos
klarkommen.  Bei AVR Studio müsst ihr wahrscheinlich Atmel einfach
nur lange genug nerven.

von Hagen (Gast)


Lesenswert?

Jo, oder wie ich einfach auf das Debugging im AVR Studio verzichten,
mach ich eh nicht so :)
Nur, auch andere Programmierer sollten mit dem Source umgehen können.
Wenn's nach mir ginge also kein Problem.

Gruß Hagen

von OldBug (Gast)


Lesenswert?

Das mit den Sections funktioniert eben leider nicht, siehe erstes
Posting.
Man kann sie zwar anlegen, aber bekommt immer die Meldung, daß das
Program-Memory überfüllt sei, sobald man dort eine "Variable" hin
legt.

Es sei denn, man modifiziert das Linkerscript, wie Jörg schon
andeutete.

...ich bastel aber immer noch ;)

von Hagen (Gast)


Lesenswert?

@OldBug: du meinst im WinAVR kommt dieser Fehler ?? Hm, bei meinen
letzten Tests ging das aber noch ohne Fehler. Werd's trotzdem nochmal
testen.

Gruß Hagen

von OldBug (Gast)


Lesenswert?

Die Sections lassen sich ohne weiteres anlegen, nur lässt sich dort noch
nichtmal ein Pointer anlegen.

Deswegen habe ich ja auch überhaupt erst diesen Thread eröffnet :)

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.