diff --git a/common_arm/ldscript.defs.at91 b/common_arm/ldscript.defs.at91 index d674f7afa..11ea46a68 100644 --- a/common_arm/ldscript.defs.at91 +++ b/common_arm/ldscript.defs.at91 @@ -3,6 +3,18 @@ mcu_flash_size = 512K; mcu_sram_size = 64K; +/* Stack for the OS image. Default in ldscript.common is 8488, but the AT91 only + * has 64K of SRAM and the stack is carved out of the same pool as BigBuf: + * BigBuf = _stack_start - __bss_end__, _stack_start = sram_end - stacksize - commonarea + * so every byte here is a byte BigBuf does not get. Worst-case stack measured + * across all 41 standalone modes (-fstack-usage + call graph, VLA bounds added + * in) is 5408 bytes, so 7168 keeps ~1.7 kB of headroom and hands 1320 bytes back + * to BigBuf. Re-measure before lowering this further - overflow grows silently + * down into the top of BigBuf, where BigBuf_malloc() hands out chunks. + * Not set for AT32/PM5, which has 512K of SRAM and no such pressure. + */ +stacksize = 7168; + mcu_flash_base_addr = 0x00100000; mcu_sram_base_addr = 0x00200000; diff --git a/common_fpga/fpga.h b/common_fpga/fpga.h index 1b009cd62..3ca8b07c7 100644 --- a/common_fpga/fpga.h +++ b/common_fpga/fpga.h @@ -32,7 +32,18 @@ #define FPGA_TYPE "2s30vq100" #define FPGA_CONFIG_SIZE 42336L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE #endif -#define FPGA_RING_BUFFER_BYTES (1024 * 30) +// LZ4 sliding-window / staging size for ONE compressed block, not the image: +// fpga_compress chops the interleaved bitstreams into blocks of at most this +// size, and the ARM decompresses them one at a time while DownloadFPGA() shifts +// the bytes out to the FPGA. It is allocated out of BigBuf, which on AT91 is +// only ~31-33 kB, so 30 kB left nothing for the caller. +// Smaller window = slightly worse compression +// Measured on flash: +// 30 kB -> 102555 bytes +// 16 kB -> 107121 bytes +// This does NOT bound the firmware's .data section +// fpga_compress gives the single-input case its own 1 MB block, because start.c only decompresses one. +#define FPGA_RING_BUFFER_BYTES (1024 * 16) #define FPGA_TRACE_SIZE 3072 // definitions for multiple FPGA config files support diff --git a/tools/fpga_compress/fpga_compress.c b/tools/fpga_compress/fpga_compress.c index b9fb8d332..0f3454d84 100644 --- a/tools/fpga_compress/fpga_compress.c +++ b/tools/fpga_compress/fpga_compress.c @@ -83,6 +83,19 @@ static int zlib_compress(FILE *infile[], uint8_t num_infiles, FILE *outfile) { } while (all_feof(infile, num_infiles) == false); + // Block size for the LZ4 stream. Two very different consumers: + // + // - many infiles: the interleaved FPGA bitstreams. armsrc decompresses them + // one block at a time into a FPGA_RING_BUFFER_BYTES buffer taken from BigBuf + // (see get_from_fpga_combined_stream()), so the block size must match. + // + // - one infile: the firmware's .data section. start.c's + // uncompress_data_section() reads ONE 4-byte length and does ONE + // LZ4_decompress_safe(), so this must come out as a SINGLE block - it has no + // loop over blocks, and a short result is not treated as an error, so a + // second block would silently leave the tail of .data uninitialized. + // That is why it gets its own, much larger, block size and must not be + // tied to FPGA_RING_BUFFER_BYTES. uint32_t buffer_size = FPGA_RING_BUFFER_BYTES; if (num_infiles == 1) { @@ -110,12 +123,24 @@ static int zlib_compress(FILE *infile[], uint8_t num_infiles, FILE *outfile) { LZ4_streamHC_t *lz4_streamhc = LZ4_createStreamHC(); LZ4_resetStreamHC_fast(lz4_streamhc, LZ4HC_CLEVEL_MAX); + if (num_infiles == 1 && total_size > buffer_size) { + fprintf(stderr, "error: %u bytes does not fit in a single %u byte block, and start.c only decompresses one\n" + , total_size + , buffer_size + ); + free(ring_buffer); + free(outbuf); + free(fpga_config); + LZ4_freeStreamHC(lz4_streamhc); + return (EXIT_FAILURE); + } + int current_in = 0; int current_out = 0; while (current_in < total_size) { - int bytes_to_copy = MIN(FPGA_RING_BUFFER_BYTES, (total_size - current_in)); + int bytes_to_copy = MIN(buffer_size, (total_size - current_in)); memcpy(ring_buffer, fpga_config + current_in, bytes_to_copy);