535 lines
20 KiB
C
535 lines
20 KiB
C
![]() |
/*
|
||
|
* c't-Bot
|
||
|
*
|
||
|
* This program is free software; you can redistribute it
|
||
|
* and/or modify it under the terms of the GNU General
|
||
|
* Public License as published by the Free Software
|
||
|
* Foundation; either version 2 of the License, or (at your
|
||
|
* option) any later version.
|
||
|
* This program is distributed in the hope that it will be
|
||
|
* useful, but WITHOUT ANY WARRANTY; without even the implied
|
||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||
|
* PURPOSE. See the GNU General Public License for more details.
|
||
|
* You should have received a copy of the GNU General Public
|
||
|
* License along with this program; if not, write to the Free
|
||
|
* Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||
|
* MA 02111-1307, USA.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*!
|
||
|
* @file mmc-vm.c
|
||
|
* @brief Virtual Memory Management mit MMC / SD-Card
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 30.11.2006
|
||
|
* @see Documentation/mmc-vm.html
|
||
|
*/
|
||
|
|
||
|
|
||
|
//TODO: * Statistikausgabe fuer MCU ergaenzen
|
||
|
// * Code optimieren, Groesse und Speed
|
||
|
|
||
|
#include "ct-Bot.h"
|
||
|
|
||
|
#ifdef MMC_VM_AVAILABLE
|
||
|
|
||
|
#include "mmc-vm.h"
|
||
|
#include "mmc.h"
|
||
|
#include "mmc-low.h"
|
||
|
#include "mmc-emu.h"
|
||
|
#include "display.h"
|
||
|
#include "timer.h"
|
||
|
#include "mini-fat.h"
|
||
|
#include "log.h"
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#ifdef MCU
|
||
|
#define MMC_START_ADDRESS 0x2000000 // [512;2^32-1] in Byte - Sinnvoll ist z.B. Haelfte der MMC / SD-Card Groesse, der Speicherplatz davor kann dann fuer ein Dateisystem verwendet werden
|
||
|
#define MAX_SPACE_IN_SRAM 6 // [1;127] - Pro Page werden 512 Byte im SRAM belegt, sobald diese verwendet wird
|
||
|
#define swap_out mmc_write_sector
|
||
|
#define swap_in mmc_read_sector
|
||
|
#define swap_space mmc_get_size()
|
||
|
#else
|
||
|
#define MMC_START_ADDRESS 0x1000000 // [512;2^32-1]
|
||
|
#define MAX_SPACE_IN_SRAM 2 // [1;127] - Pro Page werden 512 Byte im RAM belegt, sobald diese verwendet wird
|
||
|
#define swap_out mmc_emu_write_sector
|
||
|
#define swap_in mmc_emu_read_sector
|
||
|
#define swap_space mmc_emu_get_size()
|
||
|
#endif
|
||
|
|
||
|
#if MMC_ASYNC_WRITE == 1
|
||
|
#define MAX_PAGES_IN_SRAM MAX_SPACE_IN_SRAM-1
|
||
|
static uint8* swap_buffer; /*!< Puffer fuer asynchrones write-back */
|
||
|
#else
|
||
|
#define MAX_PAGES_IN_SRAM MAX_SPACE_IN_SRAM
|
||
|
#endif
|
||
|
|
||
|
typedef struct{ /*!< Struktur eines Cacheeintrags */
|
||
|
uint32 addr; /*!< Tag = MMC-Blockadresse der ins RAM geladenen Seite */
|
||
|
uint8* p_data; /*!< Daten = Zeiger auf 512 Byte grosse Seite im RAM */
|
||
|
#if MAX_PAGES_IN_SRAM > 2
|
||
|
uint8 succ; /*!< Zeiger auf Nachfolger (LRU) */
|
||
|
uint8 prec; /*!< Zeiger auf Vorgaenger (LRU) */
|
||
|
#endif
|
||
|
uint8 dirty; /*!< Dirty-Bit (0: Daten wurden bereits auf die MMC / SD-Card zurueckgeschrieben) */
|
||
|
} vm_cache_t;
|
||
|
|
||
|
#ifdef VM_STATS_AVAILABLE
|
||
|
typedef struct{
|
||
|
uint32 page_access; /*!< Anzahl der Seitenzugriffe seit Systemstart */
|
||
|
uint32 swap_ins; /*!< Anzahl der Seiteneinlagerungen seit Systemstart */
|
||
|
uint32 swap_outs; /*!< Anzahl der Seitenauslagerungen seit Systemstart */
|
||
|
uint32 vm_used_bytes; /*!< Anzahl der vom VM belegten Bytes auf der MMC / SD-Card */
|
||
|
uint32 time; /*!< Timestamp bei erster Speicheranforderung */
|
||
|
} vm_stats_t;
|
||
|
|
||
|
vm_stats_t stats_data = {0};
|
||
|
#endif
|
||
|
|
||
|
static uint32 mmc_start_address = MMC_START_ADDRESS; /*!< physische Adresse der MMC / SD-Card, wo unser VM beginnt */
|
||
|
static uint32 next_mmc_address = MMC_START_ADDRESS; /*!< naechste freie virtuelle Adresse */
|
||
|
static uint8 pages_in_sram = MAX_PAGES_IN_SRAM; /*!< Groesse des Caches im RAM */
|
||
|
static int8 allocated_pages = 0; /*!< Anzahl bereits belegter Cachebloecke */
|
||
|
static uint8 oldest_cacheblock = 0; /*!< Zeiger auf den am laengsten nicht genutzten Eintrag (LRU) */
|
||
|
static uint8 recent_cacheblock = 0; /*!< Zeiger auf den letzten genutzten Eintrag (LRU) */
|
||
|
|
||
|
vm_cache_t page_cache[MAX_PAGES_IN_SRAM]; /*!< der eigentliche Cache, vollassoziativ, LRU-Policy */
|
||
|
|
||
|
/*!
|
||
|
* Gibt die Blockadresse einer Seite zurueck
|
||
|
* @param addr Eine virtuelle Adresse
|
||
|
* @return Adresse
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 30.11.2006
|
||
|
*/
|
||
|
inline uint32 mmc_get_mmcblock_of_page(uint32 addr){
|
||
|
#ifdef MCU
|
||
|
/* Eine effizientere Variante von addr >> 9 */
|
||
|
asm volatile(
|
||
|
"mov %A0, %B0 \n\t"
|
||
|
"mov %B0, %C0 \n\t"
|
||
|
"mov %C0, %D0 \n\t"
|
||
|
"lsr %C0 \n\t"
|
||
|
"ror %B0 \n\t"
|
||
|
"ror %A0 \n\t"
|
||
|
"clr %D0 "
|
||
|
: "=r" (addr)
|
||
|
: "0" (addr)
|
||
|
);
|
||
|
return addr;
|
||
|
#else
|
||
|
return addr >> 9;
|
||
|
#endif // MCU
|
||
|
}
|
||
|
|
||
|
#ifdef VM_STATS_AVAILABLE
|
||
|
/*!
|
||
|
* Gibt die Anzahl der Pagefaults seit Systemstart bzw. Ueberlauf zurueck
|
||
|
* @return #Pagefaults
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 30.11.2006
|
||
|
*/
|
||
|
inline uint32 mmc_get_pagefaults(void){
|
||
|
return stats_data.swap_ins;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Erstellt eine kleine Statistik ueber den VM
|
||
|
* @return Zeiger auf Statistikdaten
|
||
|
* @date 01.01.2007
|
||
|
*/
|
||
|
vm_extern_stats_t* mmc_get_vm_stats(void){
|
||
|
static vm_extern_stats_t extern_stats = {0};
|
||
|
memcpy(&extern_stats, &stats_data, sizeof(vm_stats_t)); // .time wird spaeter ueberschrieben
|
||
|
uint16 delta_t = TICKS_TO_MS(TIMER_GET_TICKCOUNT_32 - stats_data.time)/1000;
|
||
|
if (delta_t == 0) delta_t = 1;
|
||
|
extern_stats.page_access_s = extern_stats.page_access / delta_t;
|
||
|
extern_stats.swap_ins_s = extern_stats.swap_ins / delta_t;
|
||
|
extern_stats.swap_outs_s = extern_stats.swap_outs / delta_t;
|
||
|
extern_stats.device_size = swap_space;
|
||
|
extern_stats.vm_size = swap_space - mmc_start_address;
|
||
|
extern_stats.cache_size = pages_in_sram;
|
||
|
extern_stats.cache_load = allocated_pages;
|
||
|
extern_stats.delta_t = delta_t;
|
||
|
return &extern_stats;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Gibt eine kleine Statistik ueber den VM aus (derzeit nur am PC)
|
||
|
* @date 01.01.2007
|
||
|
*/
|
||
|
void mmc_print_statistic(void){
|
||
|
#ifdef PC
|
||
|
vm_extern_stats_t* vm_stats = mmc_get_vm_stats();
|
||
|
printf("\n\r*** VM-Statistik *** \n\r");
|
||
|
printf("Groesse des Volumes: \t\t%lu MByte \n\r", vm_stats->device_size>>20);
|
||
|
printf("Groesse des VM: \t\t%lu MByte \n\r", vm_stats->vm_size>>20);
|
||
|
printf("Belegter virt. Speicher: \t%lu KByte \n\r", vm_stats->vm_used_bytes>>10);
|
||
|
printf("Groesse des Caches: \t\t%u Byte \n\r", (uint16)vm_stats->cache_size<<9);
|
||
|
printf("Auslastung des Caches: \t\t%u %% \n\r", ((uint16)vm_stats->cache_load<<9)/((uint16)vm_stats->cache_size<<9)*100);
|
||
|
printf("Seitenzugriffe: \t\t%lu \n\r", vm_stats->page_access);
|
||
|
printf("Seiteneinlagerungen: \t\t%lu \n\r", vm_stats->swap_ins);
|
||
|
printf("Seitenauslagerungen: \t\t%lu \n\r", vm_stats->swap_outs);
|
||
|
printf("Seitenzugriffe / s: \t\t%u \n\r", vm_stats->page_access_s);
|
||
|
printf("Seiteneinlagerungen / s: \t%u \n\r", vm_stats->swap_ins_s);
|
||
|
printf("Seitenauslagerungen / s: \t%u \n\r", vm_stats->swap_outs_s);
|
||
|
printf("Cache-Hit-Rate: \t\t%f %% \n\r", (100.0-((float)vm_stats->swap_ins/(float)vm_stats->page_access)*100.0));
|
||
|
printf("Messdauer: \t\t\t%u s \n\r", vm_stats->delta_t);
|
||
|
#else
|
||
|
/* Ausgabe fuer MCU derzeit nur ueber Logging */
|
||
|
#ifdef LOG_AVAILABLE
|
||
|
vm_extern_stats_t* vm_stats = mmc_get_vm_stats();
|
||
|
/* Texte wie oben */
|
||
|
LOG_INFO(("%lu", vm_stats->device_size>>20));
|
||
|
LOG_INFO(("%lu", vm_stats->vm_size>>20));
|
||
|
LOG_INFO(("%lu", vm_stats->vm_used_bytes>>10));
|
||
|
LOG_INFO(("%u", (uint16)vm_stats->cache_size<<9));
|
||
|
LOG_INFO(("%u", ((uint16)vm_stats->cache_load<<9)/((uint16)vm_stats->cache_size<<9)*100));
|
||
|
LOG_INFO(("%lu", vm_stats->page_access));
|
||
|
LOG_INFO(("%lu", vm_stats->swap_ins));
|
||
|
LOG_INFO(("%lu", vm_stats->swap_outs));
|
||
|
LOG_INFO(("%u", vm_stats->page_access_s));
|
||
|
LOG_INFO(("%u", vm_stats->swap_ins_s));
|
||
|
LOG_INFO(("%u", vm_stats->swap_outs_s));
|
||
|
LOG_INFO(("%u", (uint8)(100.0-((float)vm_stats->swap_ins/(float)vm_stats->page_access)*100.0)));
|
||
|
LOG_INFO(("%u", vm_stats->delta_t));
|
||
|
#endif
|
||
|
// TODO: Display-Ausgabe?
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*!
|
||
|
* Gibt die letzte Adresse einer Seite zurueck
|
||
|
* @param addr Eine virtuelle Adresse
|
||
|
* @return Adresse
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 30.11.2006
|
||
|
*/
|
||
|
inline uint32 mmc_get_end_of_page(uint32 addr){
|
||
|
return addr | 0x1ff; // die unteren 9 Bit sind gesetzt, da Blockgroesse = 512 Byte
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Gibt den Index einer Seite im Cache zurueck
|
||
|
* @param addr Eine virtuelle Adresse
|
||
|
* @return Index des Cacheblocks, -1 falls Cache-Miss
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 30.11.2006
|
||
|
*/
|
||
|
int8 mmc_get_cacheblock_of_page(uint32 addr){
|
||
|
uint32 page_addr = mmc_get_mmcblock_of_page(addr);
|
||
|
int8 i;
|
||
|
for (i=0; i<allocated_pages; i++){ // O(n)
|
||
|
if (page_cache[i].addr == page_addr) return i; // Seite gefunden :)
|
||
|
}
|
||
|
return -1; // Seite nicht im Cache
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Laedt eine Seite in den Cache, falls sie noch nicht geladen ist
|
||
|
* @param addr Eine virtuelle Adresse
|
||
|
* @return 0: ok, 1: ungueltige Adresse, 2: Fehler bei swap_out, 3: Fehler bei swap_in
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 30.11.2006
|
||
|
*/
|
||
|
uint8 mmc_load_page(uint32 addr){
|
||
|
if (addr >= next_mmc_address) return 1; // ungueltige virtuelle Adresse :(
|
||
|
#ifdef VM_STATS_AVAILABLE
|
||
|
stats_data.page_access++;
|
||
|
#endif
|
||
|
int8 cacheblock = mmc_get_cacheblock_of_page(addr);
|
||
|
if (cacheblock >= 0){ // Cache-Hit, Seite ist bereits geladen :)
|
||
|
/* LRU */
|
||
|
#if MAX_PAGES_IN_SRAM > 2
|
||
|
if (recent_cacheblock == cacheblock) page_cache[cacheblock].succ = cacheblock; // Nachfolger des neuesten Eintrags ist die Identitaet
|
||
|
if (oldest_cacheblock == cacheblock){
|
||
|
oldest_cacheblock = page_cache[cacheblock].succ; // Nachfolger ist neuer aeltester Eintrag
|
||
|
page_cache[page_cache[cacheblock].succ].prec = oldest_cacheblock; // Vorgaenger der Nachfolgers ist seine Identitaet
|
||
|
}
|
||
|
else{
|
||
|
page_cache[page_cache[cacheblock].prec].succ = page_cache[cacheblock].succ; // Nachfolger des Vorgaengers ist eigener Nachfolger
|
||
|
page_cache[page_cache[cacheblock].succ].prec = page_cache[cacheblock].prec; // Vorganeger des Nachfolgers ist eigener Vorgaenger
|
||
|
}
|
||
|
page_cache[cacheblock].prec = recent_cacheblock; // alter neuester Eintrag ist neuer Vorgaenger
|
||
|
#else
|
||
|
oldest_cacheblock = (pages_in_sram - 1) - cacheblock; // aeltester Eintrag ist nun der andere Cacheblock (wenn verfuegbar)
|
||
|
#endif
|
||
|
recent_cacheblock = cacheblock; // neuester Eintrag ist nun die Identitaet
|
||
|
return 0;
|
||
|
}
|
||
|
/* Cache-Miss => neue Seite einlagern, LRU Policy */
|
||
|
int8 next_cacheblock = oldest_cacheblock;
|
||
|
if (allocated_pages < pages_in_sram){
|
||
|
/* Es ist noch Platz im Cache */
|
||
|
next_cacheblock = allocated_pages;
|
||
|
page_cache[next_cacheblock].p_data = malloc(512); // Speicher anfordern
|
||
|
if (page_cache[next_cacheblock].p_data == NULL){ // Da will uns jemand keinen Speicher mehr geben :(
|
||
|
if (pages_in_sram == 1) return 1; // Hier ging was schief, das wir so nicht loesen koennen
|
||
|
/* Nicht mehr genug Speicher im RAM frei => neuer Versuch mit verkleinertem Cache */
|
||
|
pages_in_sram--;
|
||
|
return mmc_load_page(addr);
|
||
|
}
|
||
|
allocated_pages++; // Cache-Fuellstand aktualisieren
|
||
|
}
|
||
|
/* Pager muss nun aktiv werden */
|
||
|
#ifdef VM_STATS_AVAILABLE
|
||
|
stats_data.swap_ins++;
|
||
|
#endif
|
||
|
#if MMC_ASYNC_WRITE == 1 // im asnychronen Fall holen wir erst die neue Seite, dann kann sich das Zurueckschreiben ruhig Zeit lassen
|
||
|
uint8* p_tmp = page_cache[next_cacheblock].p_data;
|
||
|
if (swap_in(mmc_get_mmcblock_of_page(addr), swap_buffer) != 0) return 3;
|
||
|
#endif
|
||
|
if (page_cache[next_cacheblock].dirty == 1){ // Seite zurueckschreiben, falls Daten veraendert wurden
|
||
|
#ifdef VM_STATS_AVAILABLE
|
||
|
stats_data.swap_outs++;
|
||
|
#endif
|
||
|
if (swap_out(page_cache[next_cacheblock].addr, page_cache[next_cacheblock].p_data, MMC_ASYNC_WRITE) != 0) return 2;
|
||
|
}
|
||
|
#if MMC_ASYNC_WRITE == 1
|
||
|
page_cache[next_cacheblock].p_data = swap_buffer;
|
||
|
swap_buffer = p_tmp;
|
||
|
#else
|
||
|
if (swap_in(mmc_get_mmcblock_of_page(addr), page_cache[next_cacheblock].p_data) != 0) return 3;
|
||
|
#endif
|
||
|
#if MAX_PAGES_IN_SRAM > 2
|
||
|
oldest_cacheblock = page_cache[oldest_cacheblock].succ; // Nachfolger des aeltesten Eintrags ist neuer aeltester Eintrag
|
||
|
#else
|
||
|
oldest_cacheblock = (pages_in_sram - 1) - next_cacheblock; // neuer aeltester Eintrag ist nun der andere Cacheblock (wenn verfuegbar)
|
||
|
#endif
|
||
|
page_cache[next_cacheblock].addr = mmc_get_mmcblock_of_page(addr); // Cache-Tag aktualisieren
|
||
|
/* LRU */
|
||
|
#if MAX_PAGES_IN_SRAM > 2
|
||
|
page_cache[next_cacheblock].prec = recent_cacheblock; // Vorgaenger dieses Cacheblocks ist der bisher neueste Eintrag
|
||
|
page_cache[recent_cacheblock].succ = next_cacheblock; // Nachfolger des bisher neuesten Eintrags ist dieser Cacheblock
|
||
|
#endif
|
||
|
recent_cacheblock = next_cacheblock; // Dieser Cacheblock ist der neueste Eintrag
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Fordert virtuellen Speicher an
|
||
|
* @param size Groesse des gewuenschten Speicherblocks
|
||
|
* @param aligned 0: egal, 1: 512 Byte ausgerichtet
|
||
|
* @return Virtuelle Anfangsadresse des angeforderten Speicherblocks, 0 falls Fehler
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 30.11.2006
|
||
|
*/
|
||
|
uint32 mmcalloc(uint32 size, uint8 aligned){
|
||
|
if (next_mmc_address == mmc_start_address){
|
||
|
// TODO: Init-stuff here (z.B. FAT einlesen)
|
||
|
/* Inits */
|
||
|
if (mmc_start_address > swap_space){
|
||
|
mmc_start_address = swap_space-512;
|
||
|
next_mmc_address = mmc_start_address;
|
||
|
}
|
||
|
#if MMC_ASYNC_WRITE == 1
|
||
|
swap_buffer = malloc(512);
|
||
|
#endif
|
||
|
#ifdef VM_STATS_AVAILABLE
|
||
|
stats_data.time = TIMER_GET_TICKCOUNT_32;
|
||
|
#endif
|
||
|
}
|
||
|
uint32 start_addr;
|
||
|
if (aligned == 0 || mmc_get_end_of_page(next_mmc_address) == mmc_get_end_of_page(next_mmc_address+size-1)){
|
||
|
/* Daten einfach an der naechsten freien virtuellen Adresse speichern */
|
||
|
start_addr = next_mmc_address;
|
||
|
} else {
|
||
|
/* Rest der letzten Seite ueberspringen und Daten in neuem Block speichern */
|
||
|
start_addr = mmc_get_end_of_page(next_mmc_address) + 1;
|
||
|
}
|
||
|
if (start_addr+size > swap_space) return 0; // wir haben nicht mehr virtuellen Speicher als Platz auf dem Swap-Device
|
||
|
/* interne Daten aktualisieren */
|
||
|
next_mmc_address = start_addr + size;
|
||
|
#ifdef VM_STATS_AVAILABLE
|
||
|
stats_data.vm_used_bytes = next_mmc_address-mmc_start_address;
|
||
|
#endif
|
||
|
return start_addr;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Gibt einen Zeiger auf einen Speicherblock im RAM zurueck
|
||
|
* @param addr Eine virtuelle Adresse
|
||
|
* @return Zeiger auf uint8, NULL falls Fehler
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 30.11.2006
|
||
|
*/
|
||
|
uint8* mmc_get_data(uint32 addr){
|
||
|
/* Seite der gewuenschten Adresse laden */
|
||
|
if (mmc_load_page(addr) != 0) return NULL;
|
||
|
int8 cacheblock = mmc_get_cacheblock_of_page(addr);
|
||
|
page_cache[cacheblock].dirty = 1; // Daten sind veraendert
|
||
|
/* Zeiger auf Adresse in gecacheter Seite laden / berechnen und zurueckgeben */
|
||
|
return page_cache[cacheblock].p_data + (addr - (mmc_get_mmcblock_of_page(addr)<<9)); // TODO: 2. Summanden schlauer berechnen
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Erzwingt das Zurueckschreiben einer eingelagerten Seite auf die MMC / SD-Card
|
||
|
* @param addr Eine virtuelle Adresse
|
||
|
* @return 0: ok, 1: Seite zurzeit nicht eingelagert, 2: Fehler beim Zurueckschreiben
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 15.12.2006
|
||
|
*/
|
||
|
uint8 mmc_page_write_back(uint32 addr){
|
||
|
int8 cacheblock = mmc_get_cacheblock_of_page(addr);
|
||
|
if (cacheblock < 0) return 1; // Seite nicht eingelagert
|
||
|
if (swap_out(page_cache[cacheblock].addr, page_cache[cacheblock].p_data, MMC_ASYNC_WRITE) != 0) return 2; // Seite (evtl. asynchron) zurueckschreiben
|
||
|
page_cache[cacheblock].dirty = 0; // Dirty-Bit zuruecksetzen
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Schreibt alle eingelagerten Seiten auf die MMC / SD-Card zurueck
|
||
|
* @return 0: alles ok, sonst: Summe der Fehler beim Zurueckschreiben
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 21.12.2006
|
||
|
*/
|
||
|
uint8 mmc_flush_cache(void){
|
||
|
uint8 i;
|
||
|
uint8 result=0;
|
||
|
for (i=0; i<allocated_pages; i++){
|
||
|
if (page_cache[i].dirty == 1){
|
||
|
if (page_cache[i].addr < mmc_get_mmcblock_of_page(swap_space))
|
||
|
result += swap_out(page_cache[i].addr, page_cache[i].p_data, 0); // synchrones Zurueckschreiben
|
||
|
page_cache[i].dirty = 0;
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Oeffnet eine Datei im FAT16-Dateisystem auf der MMC / SD-Card und gibt eine virtuelle Adresse zurueck,
|
||
|
* mit der man per mmc_get_data() einen Pointer auf die gewuenschten Daten bekommt. Das Ein- / Auslagern
|
||
|
* macht das VM-System automatisch. Der Dateiname muss derzeit am Amfang in der Datei stehen.
|
||
|
* Achtung: Irgendwann muss man die Daten per mmc_flush_cache() oder mmc_page_write_back() zurueckschreiben!
|
||
|
* @param filename Dateiname als 0-terminierter String
|
||
|
* @return Virtuelle Anfangsadresse der angeforderten Datei, 0 falls Fehler
|
||
|
* @author Timo Sandmann (mail@timosandmann.de)
|
||
|
* @date 21.12.2006
|
||
|
*/
|
||
|
uint32 mmc_fopen(const char *filename){
|
||
|
uint32 block;
|
||
|
/* Pufferspeicher organisieren */
|
||
|
uint32 v_addr = mmcalloc(512, 0);
|
||
|
uint8* p_data = mmc_get_data(v_addr); // hier speichern wir im Folgenden den ersten Block der gesuchten Datei, der ist dann gleich im Cache ;)
|
||
|
/* Die Dateiadressen liegen ausserhalb des Bereichs fuer den VM, also interne Datenanpassungen hier rueckgaengig machen */
|
||
|
next_mmc_address -= 512;
|
||
|
#ifdef VM_STATS_AVAILABLE
|
||
|
stats_data.vm_used_bytes -= 512;
|
||
|
#endif
|
||
|
uint8 i;
|
||
|
#ifdef MCU // Debug-Info ausgeben
|
||
|
#ifdef DISPLAY_AVAILABLE
|
||
|
display_cursor(2,1);
|
||
|
display_printf("Find %s: 0x",filename);
|
||
|
uint16 k=0, j=0;
|
||
|
#endif
|
||
|
#else
|
||
|
printf("Find %s...",filename);
|
||
|
uint16 k=0, j=0;
|
||
|
#endif
|
||
|
/* MMC-Block suchen zwischen Kartenanfang und VM-Startadresse (<= Kartengroesse) */
|
||
|
for (block=0; block<mmc_get_mmcblock_of_page(mmc_start_address); block++){
|
||
|
#ifdef MCU // Debug-Info ausgeben
|
||
|
#ifdef DISPLAY_AVAILABLE
|
||
|
display_cursor(2,13);
|
||
|
display_printf("%02x%04x", j, k);
|
||
|
if (k==65535) j++;
|
||
|
k++;
|
||
|
#endif
|
||
|
#else
|
||
|
// printf(".");
|
||
|
// fflush(stdout);
|
||
|
#endif
|
||
|
if (swap_in(block, p_data) != 0) break; // Abbrechen, falls Fehler
|
||
|
/* Blockanfang mit Dateinamen vergleichen */
|
||
|
for (i=0; i<MMC_FILENAME_MAX; i++){
|
||
|
if (filename[i] == '\0'){
|
||
|
if (swap_in(++block, p_data) != 0) break; // Ersten Sektor der Datei ueberspringen, dort stehen interne Daten
|
||
|
page_cache[mmc_get_cacheblock_of_page(v_addr)].addr = block; // Cache-Tag auf gefundene Datei umbiegen
|
||
|
#ifdef MCU
|
||
|
#ifdef DISPLAY_AVAILABLE
|
||
|
k = block & 0xFFFF;
|
||
|
j = (block >> 16) & 0xFFFF;
|
||
|
display_cursor(2,1);
|
||
|
display_printf("Found %s: 0x%02x%04x",filename,j,k);
|
||
|
#endif
|
||
|
#else
|
||
|
k = block & 0xFFFF;
|
||
|
j = (block >> 16) & 0xFFFF;
|
||
|
printf("\n\rFound %s: 0x%02x%04x \n\r",filename,j,k);
|
||
|
#endif
|
||
|
return block<<9; // gesuchte Datei beginnt hier :)
|
||
|
}
|
||
|
if (filename[i] != p_data[i]) break; // gesuchte Datei beginnt nicht in diesem Block
|
||
|
}
|
||
|
}
|
||
|
/* Suche erfolglos, aber der Cache soll konsistent bleiben */
|
||
|
// TODO: ordentlich aufraeumen im Fehlerfall!
|
||
|
page_cache[mmc_get_cacheblock_of_page(v_addr)].addr = 0x800000; // Diesen Sektor gibt es auf keiner Karte <= 4 GB
|
||
|
page_cache[mmc_get_cacheblock_of_page(v_addr)].dirty = 0; // HackHack, aber so wird der ungueltige Inhalt beim Pagefault niemals versucht auf die Karte zu schreiben
|
||
|
#ifdef MCU
|
||
|
#ifdef DISPLAY_AVAILABLE
|
||
|
display_cursor(2,1);
|
||
|
display_printf("%s not found ",filename);
|
||
|
#endif
|
||
|
#else
|
||
|
printf("\n\r%s not found \n\r",filename);
|
||
|
#endif
|
||
|
return 0; // Datei nicht gefunden :(
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Liest die Groesse einer Datei im FAT16-Dateisystem auf der MMC / SD-Card aus, die zu zuvor mit
|
||
|
* mmc_fopen() geoeffnet wurde.
|
||
|
* @param file_start (virtuelle Anfangsadresse der Datei)
|
||
|
* @return Groesse der Datei in Byte
|
||
|
* @date 12.01.2007
|
||
|
*/
|
||
|
uint32 mmc_get_filesize(uint32 file_start){
|
||
|
file_len_t length;
|
||
|
uint8* p_addr = mmc_get_data(file_start-512);
|
||
|
/* Dateilaenge aus Block 0, Byte 256 bis 259 der Datei lesen */
|
||
|
uint8 i;
|
||
|
for (i=0; i<4; i++)
|
||
|
length.u8[i] = p_addr[259-i];
|
||
|
return length.u32;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Leert eine Datei im FAT16-Dateisystem auf der MMC / SD-Card, die zuvor mit mmc_fopen() geoeffnet wurde.
|
||
|
* @param file_start (virtuelle) Anfangsadresse der Datei
|
||
|
* @return 0: ok, 1: ungueltige Datei oder Laenge, 2: Fehler beim Schreiben
|
||
|
* @date 02.01.2007
|
||
|
*/
|
||
|
uint8 mmc_clear_file(uint32 file_start){
|
||
|
#ifdef PC
|
||
|
printf("Start of file: %lu \n\r", file_start);
|
||
|
#endif
|
||
|
uint32 length = mmc_get_filesize(file_start);
|
||
|
if (file_start == 0 || file_start + length >= mmc_start_address) return 1; // Datei existiert nicht oder Laenge ist ungueltig
|
||
|
/* Ersten Block der Datei laden (dessen Speicherbereich benutzen wir einfach als 0-Puffer) */
|
||
|
uint8* p_addr = mmc_get_data(file_start);
|
||
|
memset(p_addr, 0, 512); // leeren Puffer erzeugen
|
||
|
/* Alle Bloecke der Datei mit dem 0-Puffer ueberschreiben */
|
||
|
int8 cache_block;
|
||
|
uint32 addr;
|
||
|
for (addr=file_start; addr<file_start+length; addr+=512){
|
||
|
if (swap_out(mmc_get_mmcblock_of_page(addr), p_addr, 0) != 0) return 2;
|
||
|
/* Falls ein Block der Datei im Cache ist, auch diesen leeren */
|
||
|
cache_block = mmc_get_cacheblock_of_page(addr);
|
||
|
if (cache_block >= 0){
|
||
|
memset(page_cache[cache_block].p_data, 0, 512);
|
||
|
page_cache[cache_block].dirty = 0;
|
||
|
}
|
||
|
}
|
||
|
#ifdef PC
|
||
|
printf("End of file: %lu \n\r", addr);
|
||
|
#endif
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#endif // MMC_VM_AVAILABLE
|