Dokumentation zur virtuellen Speicherverwaltung für SD-Karten
Eine allgemeine ÜbersichtHinweis: Diese virtuelle Speicherverwaltung wurde im offiziellen Bot-Code inzwischen durch das SD-Karten Dateisystem BotFS abgelöst, weil sie nicht für einen MMC-Zugriff von verschiedenen Threads ausgelegt ist. Trotzdem ist sie für eigene Erweiterungen (wenn kein Multithreading wie z.B. für die Kartographie benötigt wird) weiterhin einsetzbar und deshalb auch nach wie vor im Framework enthalten.
Der virtuelle Speicher im Detail
- Die Speicherallokierung
- Ein kleines Beispiel
- Die Schnittstelle zur Hardware
- Einige Erläuterungen zum Cache
Etwas Statistik zur Leistungsbewertung
Eine Übersicht aller Konfigurationsparameter
Eine allgemeine Übersicht
Das Speichersystem ist wie folgt organisiert:
Man fordert eine beliebige Menge an Speicher an und bekommt eine virtuelle Adresse in 32 Bit zurück.
Diese Adresse entspricht der Adresse auf der MMC / SD-Card, was den Benutzer aber nicht weiter interessieren muss. Zum Arbeiten mit den Daten
erhält man eine "echte" SRAM-Adresse, die auf einen 512 Byte großen Speicherbereich zeigt. Dieser Cacheeintrag wird
auf die MMC / SD-Card zurückgeschrieben, sobald der maximal für den Cache zugeteilte Speicher komplett belegt ist, das
Ein- und Auslagern der Seiten auf die MMC / SD-Card macht die Speicherverwaltung also automatisch.
Somit kann ein Verhalten beliebig viel (virtuellen) Speicher benutzen, siehe Abschnitt zum virtuellen Speicher.
Außerdem kann derselbe Adressraum und Cache auch für den Zugriff auf FAT16-Dateien benutzt werden, Näheres dazu gibt es hier.
Die Bezeichnung "page" (oder "Seite") stellt durchgängig etwas logisches (virtuelles) dar, mit "Block" hingegen ist ein physischer Block auf dem verwendeten
Datenträger gemeint.
Im Folgenden wird ausschließlich auf die Verwendung einer MMC / SD-Card als Speichermedium eingegangen, läuft der Code auf einem PC, so wird die
Speicherkarte mit Hilfe einer Datei emuliert, die Verwendung ist aber äquivalent (siehe auch pc/mmc-emu_pc.c).
Der virtuelle Speicher im Detail
Die Speicherallokierung
Nach der Anforderung einer beliebige Menge an Speicher per mmcalloc(), bekommt man eine virtuelle Adresse des 32 Bit großen
Adressraums zurück. mmc_get_data(uint32 virtuelle_Adresse) liefert dann eine "echte" SRAM-Adresse auf einen 512 Byte
großen Puffer zurück, dessen Inhalt auf die MMC / SC-Card geschrieben wird, sobald er nicht mehr im SRAM gehalten werden kann.
Die Funktion mmc_get_end_of_page(uint32 virtuelle_Adresse) hilft herauszufinden, bis wohin man diesen Pointer verwenden darf.
Benötigt man mehr Speicher, fordert man mit mmc_get_data(uint32 virtuelle_Adresse) einen neuen Pointer an.
Zu beachten ist, dass nur soviel virtueller Speicher zur Verfügung steht, wie die intuitive Rechnung "letzter Sektor der MMC / SD-Card" minus
"Startadresse für den virtuellen Speicher" ergibt. Ansonsten liefert mmcalloc() null zurück.
Ein kleines Beispiel
Das folgende Beispiel demonstriert, wie sich der virtuelle Speicher benutzen lässt:
uint8* p_addr = mmc_get_data(v_addr); // Pointer auf Puffer holen
... // irgendwas sinnvolles tun
if (i < 512) { // Ziel liegt noch im gleichen Block
p_addr[i] = my_data; // Daten speichern
i++;
} else { // Blockende erreicht => zunaechst neuen Pointer holen
v_addr += i;
i = 0;
p_addr = mmc_get_data(v_addr);
p_addr[i] = my_data; // Daten koennen nun gespeichert werden
}
Der zweite Parameter von mmcalloc() gibt an, ob man den angeforderten Speicher auf möglichst wenige Blöcke verteilt
haben möchte (also 512 Byte ausgerichtet), oder ob er einfach am nächsten freien Byte auf der Karte beginnen soll (1: aligned, 0: beliebig). Passt der
angeforderte Speicher noch komplett in einen bereits teilweise belegten Block, so wird er immer dort untergebracht.
Die Schnittstelle zur Hardware
Das folgende Diagramm zeigt die Beziehung der Funktionen des MMC-Codes und die entsprechenden Schnittstellen:
Die grün gekennzeichneten Funktionen sind in den jeweiligen Header-Dateien deklariert und bilden zusammen die Schnittstelle des MMC-Codes nach außen. Die rot Gekennzeichneten hingegen bleiben dem eigentlichen Code für MMC und virtual Memory vorbehalten.
Die Abhängigkeiten untereinander sind durch die Pfeile dargestellt (in Aufrufrichtung).
Den doppelt umrandeten Funktionen möge man besondere Aufmerksamkeit schenken - diese sind vorrangig in eigenen Verhalten zu verwenden. Die restlichen (internen) Funktionen sind eher zur Dokumentation des Speichersystems an sich aufgeführt, für die Benutzung in Bot-Verhalten sind sie nicht so sehr interessant.
Einige Erläuterungen zum Cache
Um den Zugriff auf die jeweiligen Daten möglichst performant zu halten, cached das Speichersystem eine vorgegebene Anzahl an 512 Byte großen
Blöcken im SRAM. Der Cache ist vollassoziativ, für das Zurückschreiben kommt das LRU-Verfahren zum Einsatz. Im Speicher wird er durch ein Array
vom Typ vm_cache_t (siehe auch mmc-vm.c) repräsentiert, die maximale Größe lässt sich mit einem
Konfigurationsparameter festlegen und wird bei Speichermangel automatisch reduziert. Es wird erst dann Cachespeicher belegt, wenn
auch Daten angefordert werden.
Ein Aufruf der Funktion mmc_flush_cache() schreibt den kompletten Cache auf die Karte zurück,
mmc_page_write_back(uint32 virtuelle_Adresse) hingegen nur die Seite, die zu der übergebenen Adresse gehört. Letztere Funktion tut dies
unabhängig davon, ob die Seite seit der Einlagerung verändert wurde oder nicht und ermöglicht so einen "tieferen" Eingriff in das
sonst automatisch arbeitende System.
Ein Verhalten sollte also vor seiner Beendigung dafür sorgen, dass der Cacheinhalt gesichert wird, wenn die gespeicherten Daten erhalten bleiben sollen!
Der Zugriff auf ein FAT16-Dateisystems
Die Unterstützung für FAT16-Dateien auf einer MMC / SD-Card ist wie folgt aufgebaut:
mmc_fopen(const char *filename) öffnet eine Datei im FAT16-Dateisystem der Karte und gibt die virtuelle Startadresse
zurück, so dass man mit mmc_get_data() Zugriff auf die Daten bekommt. Der Dateiname muss dabei ganz am Anfang in der Datei
stehen.
Achtung: Öffnet man eine Datei, die bereits mit mmc_fopen() geöffnet wurde, ist das Verhalten bzgl. dieser Datei derzeit
undefiniert!
mmc_clear_file(uint32 file_start) leert eine Datei im FAT16-Dateisystem, die zuvor mit
mmc_fopen() geöffnet wurde. Die Datei wird komplett mit Nullen überschrieben, nur der erste Sektor mit dem "Dateinamen" und der
Größe bleibt erhalten.
mmc_get_filesize(uint32 file_start) gibt die Größe einer geöffneten Datei in Byte zurück.
Etwas Statistik zur Leistungsbewertung
Wenn VM_STATS_AVAILABLE definiert ist, lässt sich mit der Funktion mmc_get_vm_stats() eine Statistik
über die Leistung des Speichersystems erstellen. mmc_print_statistic() gibt solch eine Statistik in der Konsole aus, wenn der
Code auf einem PC läuft:
*** VM-Statistik *** Groesse des Volumes: 32 MByte Groesse des VM: 16 MByte Belegter virt. Speicher: 0 KByte Groesse des Caches: 1024 Byte Auslastung des Caches: 100 % Seitenzugriffe: 1375054 Seiteneinlagerungen: 33770 Seitenauslagerungen: 33768 Seitenzugriffe / s: 5288 Seiteneinlagerungen / s: 129 Seitenauslagerungen / s: 129 Cache-Hit-Rate: 97.544096 % Messdauer: 260 s
Die hier ersichtlichen Daten stellen die Leistung des Speichersystems dar, unmittelbar nachdem ein Bot das Standardlabyrinth im c't-Sim komplett mit dem Wandfolger-Algorithmus
durchfahren und dabei eine Umgebungskarte erstellt hat.
Für die MCU gibt es derzeit noch keine entsprechende Ausgabemöglichkeit.
Eine Übersicht aller Konfigurationsparameter
- MMC_VM_AVAILABLE schaltet den Speichermanager ein oder aus.
- VM_STATS_AVAILABLE ermöglicht die Berechnung und Ausgabe einer Statistik über das Speichersystem, wenn definiert.
- MMC_START_ADDRESS gibt die physische Adresse (in Byte) an, wo der virtuelle Speicher beginnt. Sinnvoll ist z.B. die Hälfte der MMC- / SD-Card-Größe, der Speicherplatz davor kann dann für ein Dateisystem verwendet werden.
- MAX_SPACE_IN_SRAM definiert die Anzahl der 512 Byte großen Blöcke, die maximal gleichzeitig im SRAM gehalten werden.
- VM_FILENAME_MAX ist die maximale Länge eines Dateinamens in Zeichen.


