Browse Source

rv32: combine heap and disk cache

Have the heap and disk cache share a memory pool, with the disk cache
resizable to fit the needs of the heap. This is a far more efficient
use of memory.
H. Peter Anvin 2 years ago
parent
commit
bf2d593e50

+ 2 - 2
fpga/max80.qpf

@@ -19,12 +19,12 @@
 #
 # Quartus Prime
 # Version 21.1.0 Build 842 10/21/2021 SJ Lite Edition
-# Date created = 09:52:16  May 18, 2022
+# Date created = 16:40:14  May 19, 2022
 #
 # -------------------------------------------------------------------------- #
 
 QUARTUS_VERSION = "21.1"
-DATE = "09:52:16  May 18, 2022"
+DATE = "16:40:14  May 19, 2022"
 
 # Revisions
 

BIN
fpga/output/bypass.jic


BIN
fpga/output/v1.fw


BIN
fpga/output/v1.jic


BIN
fpga/output/v1.rbf.gz


BIN
fpga/output/v1.rpd.gz


BIN
fpga/output/v1.sof


BIN
fpga/output/v1.svf.gz


BIN
fpga/output/v1.xsvf.gz


BIN
fpga/output/v2.fw


BIN
fpga/output/v2.jic


BIN
fpga/output/v2.rbf.gz


BIN
fpga/output/v2.rpd.gz


BIN
fpga/output/v2.sof


BIN
fpga/output/v2.svf.gz


BIN
fpga/output/v2.xsvf.gz


+ 7 - 2
rv32/Makefile

@@ -44,9 +44,9 @@ ROMS    := $(wildcard roms/*.rom)
 ROMOBJS  = $(ROMS:.rom=.o)
 
 LIBOBJ   = head.o dummy.o die.o system.o \
-	   ioregsa.o irqasm.o irqtable.o spurious_irq.o sbrk.o \
+	   ioregsa.o irqasm.o irqtable.o spurious_irq.o \
 	   console.o rtc.o romcopy.o spiflash.o esp.o \
-	   sdcard.o diskcache.o \
+	   sdcard.o \
 	   abcmem.o abcio.o abcdisk.o abcrtc.o abcpun80.o \
 	   memset.o memcpy.o \
 	   runtest.o start_test.o \
@@ -106,6 +106,11 @@ testimg.o: testimg.S testimg.bin
 %.hex: %.bin
 	$(OBJCOPY) -I binary -O ihex $< $@
 
+# Objects specific to certain executables
+max80.elf: diskcache.o
+
+jtagupd.elf: sbrk.o
+
 %.elf: %.ild %.o $(LIBS)
 	$(CC) $(LDFLAGS) -Wl,-T,$< -o $@ \
 		-Wl,--start-group $(filter-out $<,$^) -Wl,--end-group

+ 1 - 1
rv32/checksum.h

@@ -1,4 +1,4 @@
 #ifndef CHECKSUM_H
 #define CHECKSUM_H
-#define SDRAM_SUM 0x9b2d9673
+#define SDRAM_SUM 0x9acb8541
 #endif

+ 14 - 2
rv32/common.h

@@ -30,6 +30,18 @@ extern char __esplink_start[],   __esplink_end[];
 extern char __dram_init_start[], __dram_init_end[];
 extern char __dram_bss_start[],  __dram_bss_end[];
 
+static inline __constfunc size_t _align_down(size_t addr, size_t align)
+{
+    return addr & ~(align - 1);
+}
+#define align_down(a,l) ((__typeof__(a))_align_down((size_t)(a),(l)))
+
+static inline __constfunc size_t _align_up(size_t addr, size_t align)
+{
+    return _align_down(addr + align - 1, align);
+}
+#define align_up(a,l)   ((__typeof__(a))_align_up((size_t)(a),(l)))
+
 struct esplink_head;
 extern struct esplink_head esplink_head;
 extern uint32_t esplink[];
@@ -46,7 +58,7 @@ extern no_return _exit(int);
     }
 
 extern const uint8_t _end[];
-extern void *_sbrk(size_t);
+extern void *_sbrk(ptrdiff_t);
 
 extern uint32_t timer_irq_start;
 static inline uint32_t timer_count(void)
@@ -56,6 +68,7 @@ static inline uint32_t timer_count(void)
 }
 
 extern void init(void);
+extern void heap_init(void);
 
 extern void mount_abcdrives(void);
 
@@ -64,7 +77,6 @@ extern void write_rtc(void);
 extern volatile bool do_write_rtc;
 extern void rtc_abc_init(void);
 extern void rtc_abc_io_poll(void);
-extern void disk_cache_init(void);
 
 extern void pun80_init(void);
 

+ 1 - 0
rv32/compiler.h

@@ -8,6 +8,7 @@
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stdbool.h>
+#include <errno.h>
 
 /* Use builtin memcpy and memset optimizations */
 #define memset(s,c,n)	__builtin_memset(s,c,n)

+ 188 - 50
rv32/diskcache.c

@@ -1,28 +1,55 @@
 /*
- * Simple disk cache
+ * Simple disk cache, dynamically sharing space with the sbrk heap.
  */
 
 #include "common.h"
 #include "list.h"
 #include "sdcard.h"
+#include "sys.h"
 #include "systime.h"
 #include "console.h"
 
-#define CACHE_BLOCKS		1024
+/*
+ * Maximum possible arena size; metadata structures and sbrk
+ * thresholds are sized based on this value. A reasonable overestimate
+ * is fine.
+ */
+#define MAX_ARENA_SIZE		SDRAM_SIZE
+
 #define CACHE_BLOCK_BITS	5
+#define CACHE_BLOCK_SHIFT	(CACHE_BLOCK_BITS+SECTOR_SHIFT)
 #define CACHE_BLOCK_SECTORS	(1 << CACHE_BLOCK_BITS)
 #define CACHE_BLOCK_SIZE	(SECTOR_SIZE << CACHE_BLOCK_BITS)
 
-#define CACHE_HASH_SIZE		(CACHE_BLOCKS*2)
+#define MAX_CACHE_BLOCKS	(MAX_ARENA_SIZE/CACHE_BLOCK_SIZE)
+#define CACHE_HASH_SIZE		(MAX_CACHE_BLOCKS*2)
+
+/*
+ * Minimum cache size; fail sbrk allocation beyond this point.
+ */
+#define MIN_CACHE_SIZE		(MAX_ARENA_SIZE >> 4)
+#define MIN_CACHE_BLOCKS	(MIN_CACHE_SIZE/CACHE_BLOCK_SIZE)
+
+/*
+ * When reclaiming storage from the brk heap, free up this much space
+ * in addition to the requested allocation (if possible.)
+ */
+#define BRK_SLACK		(MAX_ARENA_SIZE >> 6)
+
+/* Minimum alignment for brk */
+#define HEAP_ALIGN		32
 
 typedef sector_t block_t;
 #define NO_BLOCK ((block_t)(-1))
 
 enum block_status {
-    FL_INVALID   = 0,
-    FL_VALID     = 1,
-    FL_DIRTY_BIT = 2,
-    FL_DIRTY     = FL_VALID|FL_DIRTY_BIT
+    FL_NOTCACHE	 = 0,		/* heap or not present */
+    FL_CACHE_BIT = 1,
+    FL_FREE      = FL_CACHE_BIT,
+    FL_VALID_BIT = 2,
+    FL_VALID     = FL_CACHE_BIT | FL_VALID_BIT,
+    FL_DIRTY_BIT = 4,
+    FL_DIRTY     = FL_VALID | FL_DIRTY_BIT
 };
 
 struct cache_block {
@@ -30,40 +57,32 @@ struct cache_block {
     struct dll lru;		/* Link in LRU chain */
     block_t block;		/* Physical block index */
     enum block_status flags;	/* Status flags */
-    char data[CACHE_BLOCK_SIZE] __attribute__((aligned(4)));
 };
 
-static struct dll __dram_noinit cache_hash[CACHE_HASH_SIZE];
-static struct cache_block __dram_noinit disk_cache[CACHE_BLOCKS];
+static struct dll __dram_bss cache_hash[CACHE_HASH_SIZE];
+static struct cache_block __dram_bss cache_blocks[MAX_CACHE_BLOCKS];
 
-static struct dll lru_list;
-static struct dll free_list;
+extern char __heap_start[], __heap_end[];
+#define CACHE_START	align_up(&__heap_start[0], CACHE_BLOCK_SIZE)
+#define CACHE_END	__heap_end
 
-void disk_cache_init(void)
+/* Convert between data and metadata pointers */
+static inline __constfunc char *bp_to_data(const struct cache_block *block)
 {
-    struct cache_block *bp, *bep;
-    struct dll *hp, *hep;
-
-    dll_init(&free_list);
-    dll_init(&lru_list);
+    return CACHE_START + ((block - cache_blocks) << CACHE_BLOCK_SHIFT);
+}
+static inline __constfunc struct cache_block *data_to_bp(void *data)
+{
+    return &cache_blocks[((char *)data - CACHE_START) >> CACHE_BLOCK_SHIFT];
+}
 
-    bp = disk_cache;
-    bep = bp + CACHE_BLOCKS;
-    while (bp < bep) {
-	dll_insert_head(&free_list, &bp->hash);
-	dll_insert_head(&lru_list, &bp->lru);
-	bp->block = NO_BLOCK;
-	bp->flags = FL_INVALID;
-	bp++;
-    }
+static struct dll lru_list;
+static struct dll free_list;
 
-    hp  = cache_hash;
-    hep = hp + CACHE_HASH_SIZE;
-    while (hp < hep)
-	dll_init(hp++);
-}
+static size_t cur_brk;
+static size_t cache_base;
 
-static inline __attribute__((const)) struct dll *hash_slot(block_t block)
+static inline __constfunc struct dll *hash_slot(block_t block)
 {
     uint64_t m;
     uint32_t hash;
@@ -71,7 +90,7 @@ static inline __attribute__((const)) struct dll *hash_slot(block_t block)
     m = UINT64_C(0x34f1f85d) * block;
     hash = (m >> 32) + m;
 
-    return &cache_hash[hash % CACHE_BLOCKS];
+    return &cache_hash[hash % CACHE_HASH_SIZE];
 }
 
 static struct cache_block *disk_cache_find(block_t block)
@@ -92,11 +111,13 @@ static struct cache_block *disk_cache_find(block_t block)
 
 static void invalidate_block(struct cache_block *bp)
 {
-    dll_remove(&bp->hash);
-    dll_insert_head(&free_list, &bp->hash);
-    bp->block = NO_BLOCK;
-    bp->flags = FL_INVALID;
-    dll_demote(&lru_list, &bp->lru);
+    if (bp->flags & FL_VALID_BIT) {
+	dll_remove(&bp->hash);
+	dll_insert_head(&free_list, &bp->hash);
+	bp->block = NO_BLOCK;
+	bp->flags = FL_FREE;
+	dll_demote(&lru_list, &bp->lru);
+    }
 }
 
 static DRESULT sync_block(struct cache_block *bp)
@@ -106,11 +127,10 @@ static DRESULT sync_block(struct cache_block *bp)
 	sector_t size = sdc.lbasize;
 	sector_t sectors = min(CACHE_BLOCK_SECTORS, size - sector);
 
-	if (sdcard_write_sectors(bp->data, sector, sectors) != (int)sectors) {
+	if (sdcard_write_sectors(bp_to_data(bp), sector, sectors) != (int)sectors) {
 	    invalidate_block(bp); /* Or...? */
 	    return RES_ERROR;
 	}
-
 	bp->flags = FL_VALID;
     }
 
@@ -145,6 +165,35 @@ static DRESULT sync_all(void)
     return rv;
 }
 
+static void invalidate_all(void)
+{
+    struct cache_block *bp  = data_to_bp((void *)cache_base);
+    const struct cache_block *ebp = data_to_bp(CACHE_END);
+
+    while (bp < ebp)
+	invalidate_block(bp++);
+}
+
+static DRESULT sync_kill_block(struct cache_block *bp)
+{
+    DRESULT rv = RES_OK;
+
+    if (!(bp->flags & FL_CACHE_BIT))
+	return;
+
+    if (bp->flags == FL_DIRTY)
+	rv = sync_block(bp);
+
+    if (!rv) {
+	dll_remove(&bp->hash);
+	dll_remove(&bp->lru);
+	bp->flags = FL_NOTCACHE;
+	bp->block = NO_BLOCK;
+    }
+
+    return rv;
+}
+
 static struct cache_block *disk_cache_get(block_t block, bool do_read)
 {
     const sector_t size = sdc.lbasize;
@@ -168,7 +217,7 @@ static struct cache_block *disk_cache_get(block_t block, bool do_read)
 	clear_block(bp);
 
 	if (do_read) {
-	    if (sdcard_read_sectors(bp->data, sector, sectors) != sectors)
+	    if (sdcard_read_sectors(bp_to_data(bp), sector, sectors) != sectors)
 		return NULL;
 
 	    bp->flags = FL_VALID;
@@ -182,6 +231,100 @@ static struct cache_block *disk_cache_get(block_t block, bool do_read)
     return bp;
 }
 
+/* --------------------------------------------------------------------------
+ *  Heap management
+ * ------------------------------------------------------------------------- */
+
+#define ARENA_START		((size_t)&__heap_start)
+#define ARENA_END		((size_t)&__heap_end)
+
+static size_t disk_cache_adjust(size_t newbrk)
+{
+    size_t new_base = align_up(newbrk + BRK_SLACK, CACHE_BLOCK_SIZE);
+
+    if (new_base > ARENA_END - MIN_CACHE_SIZE)
+	new_base = ARENA_END - MIN_CACHE_SIZE;
+
+    /*
+     * Add new entries to the tail of the free list in reverse order,
+     * so higher addresses get preferred.
+     */
+    struct cache_block * const obp = data_to_bp((void *)cache_base);
+    struct cache_block * const nbp = data_to_bp((void *)new_base);
+    struct cache_block *bp;
+
+    if (obp < nbp) {
+	/* Shrink cache */
+	for (bp = obp; bp < nbp; bp++) {
+	    if (sync_kill_block(bp))
+		break;
+	}
+    } else {
+	/* Grow cache */
+	for (bp = nbp; bp < obp; bp++) {
+	    bp->block = NO_BLOCK;
+	    bp->flags = FL_FREE;
+	    dll_insert_tail(&free_list, &bp->hash);
+	    dll_insert_tail(&lru_list, &bp->lru);
+	}
+	bp = nbp;
+    }
+
+    new_base = (size_t)bp_to_data(bp);
+
+    if (cache_base != new_base) {
+	con_printf("heap: start %p, brk %p, cache %p, %u blocks\n",
+		   __heap_start, (void *)newbrk, (void *)new_base,
+		   (ARENA_END - new_base) >> CACHE_BLOCK_SHIFT);
+    }
+
+    return cache_base = new_base;
+}
+
+void * __hot _sbrk(ptrdiff_t increment)
+{
+    const size_t heap_start = align_up(ARENA_START, HEAP_ALIGN);
+    size_t new_brk = align_up(cur_brk + increment, HEAP_ALIGN);
+
+    if (unlikely(new_brk > ARENA_END - MIN_CACHE_SIZE))
+	goto err;
+
+    if (unlikely(new_brk < heap_start))
+	new_brk = heap_start;
+
+    if (new_brk > cache_base || new_brk <= cache_base - BRK_SLACK) {
+	if (disk_cache_adjust(new_brk) < new_brk)
+	    goto err;
+    }
+
+    size_t old_brk = cur_brk;
+    cur_brk = new_brk;
+    return (void *)old_brk;
+
+err:
+    errno = ENOMEM;
+    return (void *)(-1);
+}
+
+void heap_init(void)
+{
+    const size_t heap_start = align_up(ARENA_START, HEAP_ALIGN);
+
+    cur_brk = heap_start;
+    cache_base = ARENA_END;
+
+    con_printf("heap_init: start %p, end %p\n", cur_brk, (void *)cache_base);
+
+    dll_init(&free_list);
+    dll_init(&lru_list);
+
+    struct dll * const hep = cache_hash + CACHE_HASH_SIZE;
+    for (struct dll *hp = cache_hash; hp < hep; hp++)
+	dll_init(hp);
+
+    disk_cache_adjust(cur_brk);
+}
+
 /* --------------------------------------------------------------------------
  *  Interface to fatfs
  * ------------------------------------------------------------------------- */
@@ -196,7 +339,7 @@ DSTATUS disk_initialize(BYTE drive)
     old_status = sdc.status;
     status = sdcard_init();
     if ((status ^ old_status) & STA_NOINIT)
-	disk_cache_init();
+	invalidate_all();
 
     return sdc.fsstatus = status;
 }
@@ -249,7 +392,7 @@ DRESULT disk_read(BYTE drive, BYTE *buffer,
 
 	size_t bytes = min(CACHE_BLOCK_SIZE - offset, len);
 
-	memcpy(buffer, bp->data + offset, bytes);
+	memcpy(buffer, bp_to_data(bp) + offset, bytes);
 	len -= bytes;
 	block++;
 	offset = 0;
@@ -286,7 +429,7 @@ DRESULT disk_write(BYTE drive, const BYTE *buffer, LBA_t sectornumber,
 	if (!bp)
 	    return RES_ERROR;
 
-	memcpy(bp->data + offset, buffer, bytes);
+	memcpy(bp_to_data(bp) + offset, buffer, bytes);
 	bp->flags = FL_DIRTY;
 
 	len -= bytes;
@@ -296,8 +439,3 @@ DRESULT disk_write(BYTE drive, const BYTE *buffer, LBA_t sectornumber,
 
     return RES_OK;
 }
-
-DWORD get_fattime(void)
-{
-    return SYSCLOCK_DATETIME;	/* Already in FAT format */
-}

+ 1 - 1
rv32/esp.c

@@ -46,7 +46,7 @@ void esp_init(void)
     ESP_SPI_IRQ                = 0;
 
     memset(&esplink_head, 0, sizeof esplink_head);
-    
+
     esplink_head.hlen          = sizeof esplink_head;
     esplink_head.board.cfg     = SYS_BOARDCFG;
     memcpy(esplink_head.signature, esp_signature, sizeof esp_signature);

+ 2 - 0
rv32/fatfs/source/ff.c

@@ -267,6 +267,7 @@
 
 
 /* Timestamp */
+#ifndef GET_FATTIME
 #if FF_FS_NORTC == 1
 #if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
 #error Invalid FF_FS_NORTC settings
@@ -275,6 +276,7 @@
 #else
 #define GET_FATTIME()	get_fattime()
 #endif
+#endif
 
 
 /* File lock controls */

+ 4 - 2
rv32/fatfs/source/ffconf.h

@@ -1,3 +1,5 @@
+#include "../../ioregs.h"
+
 /*---------------------------------------------------------------------------/
 /  FatFs Functional Configurations
 /---------------------------------------------------------------------------*/
@@ -238,7 +240,7 @@
 /  Note that enabling exFAT discards ANSI C (C89) compatibility. */
 
 
-#define FF_FS_NORTC		0
+#define FF_FS_NORTC	0
 #define FF_NORTC_MON	1
 #define FF_NORTC_MDAY	1
 #define FF_NORTC_YEAR	2020
@@ -250,7 +252,7 @@
 /  added to the project to read current time form real-time clock. FF_NORTC_MON,
 /  FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
 /  These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
-
+#define GET_FATTIME() (SYSCLOCK_DATETIME) /* Hardware-provided FAT format time */
 
 #define FF_FS_NOFSINFO	0
 /* If you need to know correct free space on the FAT32 volume, set bit 0 of this

+ 5 - 5
rv32/jtagupd.ld

@@ -288,10 +288,10 @@ SECTIONS
 		*(.dram.noinit*)
 	} >DRAM
 
-	/* No need to zero the heap */
-	.heap (NOLOAD) : ALIGN(16) {
-		*(.heap*)
-	} >DRAM
+	. = ALIGN(32);
+	PROVIDE(__heap_start = .);
+	. = SDRAM_ADDR + SDRAM_SIZE;
+	PROVIDE(__heap_end = .);
 
 	__dram_end = .;
 
@@ -310,5 +310,5 @@ SECTIONS
 	}
 	__junk_end = .;
 
-/*	HIDDEN($assert_no_junk = ASSERT(__junk_end == __junk_start, "unknown sections present")); */
+	HIDDEN($assert_no_junk = ASSERT(__junk_end == __junk_start, "unknown sections present"));
 }

+ 4 - 5
rv32/max80.ld

@@ -181,7 +181,6 @@ SECTIONS
 	.text.hot : ALIGN(4) {
 		*(.text.startup .text.startup.*)
 		*(.text.hot .text.hot.*)
-		KEEP(sbrk.o(.text))
 		*(.gnu.linkonce.t.*)
 	}
 	PROVIDE (__etext = .);
@@ -317,10 +316,10 @@ SECTIONS
 		*(.dram.noinit*)
 	} >DRAM
 
-	/* No need to zero the heap */
-	.heap (NOLOAD) : ALIGN(16) {
-		*(.heap*)
-	} >DRAM
+	. = ALIGN(32);
+	PROVIDE(__heap_start = .);
+	. = SDRAM_ADDR + SDRAM_SIZE;
+	PROVIDE(__heap_end = .);
 
 	__dram_end = .;
 

+ 2 - 2
rv32/romcopy.c

@@ -127,7 +127,7 @@ IRQHANDLER(romcopy,0)
 	len = __esplink_end - __esplink_start;
 	romcopy_bzero(__esplink_start, len);
 	break;
-      
+
     case 1:
 	/* Condition flash ROM */
 	romcopy_config_flash();
@@ -328,7 +328,7 @@ void rom_flash_from_memory(void *buf, size_t buflen)
 
     /* Now do it for real */
     max80_flash.ops = &max80_spiflash_ops;
-    
+
     if (spiflash_flash_file(&max80_flash, buf, buflen)) {
 	con_puts("update: flash update failed\n");
 	return;

+ 8 - 6
rv32/sbrk.c

@@ -1,5 +1,5 @@
 /*
- * sbrk allocator
+ * sbrk allocator (without disk cache integration)
  */
 
 #include <stddef.h>
@@ -7,17 +7,19 @@
 #include "sys.h"
 #include "common.h"
 
-#define HEAP_SIZE	(4 << 20) /* 4 MB */
+extern char __heap_start[], __heap_end[];
+static char *cur_brk = __heap_start;
 
-static char __attribute__((section(".heap"))) __heap[HEAP_SIZE];
-static char *cur_brk = __heap;
+void heap_init(void)
+{
+}
 
-void *_sbrk(size_t increment)
+void * __hot _sbrk(ptrdiff_t increment)
 {
     char *old_brk = cur_brk;
     char *new_brk = old_brk + increment;
 
-    if (unlikely(new_brk > &__heap[HEAP_SIZE])) {
+    if (unlikely(new_brk > __heap_end)) {
 	errno = ENOMEM;
 	return (void *)(-1);
     }

+ 12 - 12
rv32/spiflash.c

@@ -84,7 +84,7 @@ static int spiflash_read_data(spz_stream *spz, void *buf, unsigned int len)
 		int rv = inflate(&spz->zs, Z_SYNC_FLUSH);
 		if (rv == Z_OK || (rv == Z_BUF_ERROR && !spz->eoi))
 		    continue;
-		
+
 		spz->eoi = true;
 		if (rv != Z_STREAM_END && !spz->err)
 		    spz->err = rv;
@@ -278,7 +278,7 @@ static int spiflash_erase(const struct spiflash *flash,
 	MSG("update: nothing to erase\n");
 	return 0;
     }
-    
+
     while (sector_mask) {
 	if (!(addr & block_mask) &&
 	    ((sector_mask & block_sector_mask) == block_sector_mask)) {
@@ -295,7 +295,7 @@ static int spiflash_erase(const struct spiflash *flash,
 
 	if (sector_mask & 1) {
 	    MSG("\rupdate: erasing %2uK at 0x%06x... ", erasesize >> 10, addr);
-	
+
 	    rv = spiflash_write_enable(flash);
 	    if (rv)
 		return rv;
@@ -367,7 +367,7 @@ static void spiflash_check_block(spz_stream *spz, uint32_t addr,
 
     *erase_mask = 0;
     memset(prog_mask, 0, SPIFLASH_BLOCK_SIZE/SPIFLASH_PAGE_SIZE/8);
-    
+
     p = spz->vbuf;
     q = spz->dbuf;
 
@@ -447,7 +447,7 @@ static int spiflash_flash_chunk(spz_stream *spz)
 	    rv = spiflash_read(spz->flash, addr, spz->vbuf, SPIFLASH_BLOCK_SIZE);
 	    if (rv)
 		goto err;
-	    
+
 	    spiflash_check_block(spz, addr, &erase_mask, prog_mask);
 	    if (erase_mask) {
 		MSG("[erase mask = %04x] ", erase_mask);
@@ -465,9 +465,9 @@ static int spiflash_flash_chunk(spz_stream *spz)
 
 	    if (!(prog_mask[page >> 5] & (UINT32_C(1) << (page & 31))))
 		continue;	/* No need to program */
-	    
+
 	    programmed = true;
-	    
+
 	    udelay(100);
 	    MSG("\rupdate: writing at 0x%06x... ", addr + page_offs);
 
@@ -497,7 +497,7 @@ static int spiflash_flash_chunk(spz_stream *spz)
 	    MSG("ok\n");
 	else
 	    MSG("update: nothing to write\n");
-	
+
 	addr += pre_padding + bytes;
 	data_left -= bytes;
     }
@@ -523,7 +523,7 @@ static int spiflash_get_sfdp(const struct spiflash *flash, void *sfdp)
 static void *spiflash_read_chunk_str(spz_stream *spz)
 {
     int rv;
-    
+
     if (spz->header.len >= SPIFLASH_BLOCK_SIZE) {
 	spz->err = Z_DATA_ERROR;
 	return NULL;
@@ -644,7 +644,7 @@ int spiflash_flash_file(const struct spiflash *flash, void *buf, size_t buflen)
 	    }
 	    MSG("\n");
 	}
-    
+
 	if (sfdp[0] != 0x50444653) {
 	    MSG("update: invalid SFDP information read\n");
 	    return SPIFLASH_ERR_DETECT;
@@ -663,14 +663,14 @@ int spiflash_flash_file(const struct spiflash *flash, void *buf, size_t buflen)
 		return err;
 	    if (sr1 & 0x01) {
 		MSG("update: flash busy, trying reset... ");
-		
+
 		err = spiflash_simple_command(flash, ROM_ENABLE_RESET);
 		if (err)
 		    return err;
 		err = spiflash_simple_command(flash, ROM_RESET);
 		if (err)
 		    return err;
-		
+
 		udelay(60);
 		err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
 		if (err || (sr1 & 0x01)) {

+ 14 - 12
rv32/system.c

@@ -175,9 +175,23 @@ void __hot init(void)
 	if (v)
 	    con_puts(hotstr("SDRAM .bss is not zero!\n"));
     }
+
+    if ( MINITESTS ) {
+	uint32_t hello_dump[4];
+	con_puts(hotstr("SDRAM jump test:"));
+	memcpy(hello_dump, (void *)hello_sdram, sizeof hello_dump);
+	for (int i = 0; i < 4; i++) {
+	    con_putc(' ');
+	    con_print_hex(hello_dump[i]);
+	}
+	con_puts(hotstr("\nJumping to SDRAM... "));
+	hello_sdram();
+	con_puts(hotstr("back in SRAM.\n"));
+    }
     set_leds(6);
 
     con_flush();
+    heap_init();
 
     set_leds(5);
 #if DELAY
@@ -187,17 +201,6 @@ void __hot init(void)
     con_flush();
 #endif
 
-    if ( MINITESTS ) {
-	const volatile uint32_t *p = (const volatile uint32_t *)hello_sdram;
-	con_puts(hotstr("SDRAM jump test:"));
-	for (int i = 0; i < 4; i++) {
-	    con_putc(' ');
-	    con_print_hex(p[i]);
-	}
-	con_puts(hotstr("\nJumping to SDRAM... "));
-	hello_sdram();
-	con_puts(hotstr("back in SRAM.\n"));
-    }
 
     late_init();
 }
@@ -259,7 +262,6 @@ static void __noinline late_init(void)
     set_leds(3);
 
     sdcard_reset();
-    disk_cache_init();
     abcdisk_init();
 
     set_leds(2);