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?
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.
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.
${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.
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?
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.
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.
@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!
@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
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
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 ;-).
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
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.
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
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.
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!
Ü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
@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
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.
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
Naja, du musst doch nur dem Compiler sagen, ab wo er mit den Variablen beginnen soll. Damit sind wir ja fast wieder am Ausgangspunkt. ;-)
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
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.
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
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.
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
@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.
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
Ü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
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.
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
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 ;)
@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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.