diff options
Diffstat (limited to 'src/soc.zig')
| -rw-r--r-- | src/soc.zig | 45 |
1 files changed, 45 insertions, 0 deletions
diff --git a/src/soc.zig b/src/soc.zig index 172966e..350948a 100644 --- a/src/soc.zig +++ b/src/soc.zig @@ -114,6 +114,51 @@ pub const rom = struct { } }; +/// Evict every flash-mapped cache line the bootloader left behind, by reading more flash than the +/// caches can hold. Call it before the first byte of `.rodata` is touched. +/// +/// This is a workaround for a real defect in the hand-over, not a tidiness measure. The second-stage +/// bootloader leaves lines cached against a mapping it then replaces, so an application reads its +/// own `.rodata` and gets its own `.text` back - **deterministically**, which is exactly what makes +/// it look like anything other than a cache. Measured on this die: a load at `0x40035A1C` returned +/// `93 85 85 0f` before eviction and `3c ee 08 40` after, and the second is what the image holds +/// there. A string literal read before this runs is machine code, so a firmware whose first act is +/// to print a marker prints garbage and looks like it never booted at all. +/// +/// Everything cheaper was tried first and every one of them said the hardware was fine, which is +/// why the list is here rather than being rediscovered: +/// +/// * The MMU table is correct. Entries 0..9 read `0x1001`..`0x100a` - the valid bit plus physical +/// page N+1 - which is exactly what the image builder's single flash-to-vaddr anchor requires, +/// and 10..11 are unmapped as they should be. Read off the die through +/// `SPI_MEM_C_MMU_ITEM_INDEX_REG`, not inferred. +/// * The flash is correct. `zig build flash` verifies an MD5 of what the ROM stored, and the +/// image matches the ELF byte for byte at the addresses that misread. +/// * The page size is not in question: hardwired to 64 KiB on this chip +/// (`hal/esp32p4/mmu_ll.h:126-130` returns `MMU_PAGE_64KB` and the setter asserts it). +/// * Not fragmentation of the mapping either: the bad bytes arrive in one contiguous run of +/// >= 192 B, not in 64-byte lines, and they are identical across three resets and two +/// reflashes - determinism is what kept this looking like anything but a cache. +/// +/// 512 KiB is four times the 128 KiB the L2 measured at (`examples/memprobe.zig` found real RAM +/// stopping at `0x4FFA0000`, the cache taking the rest), with the L1s smaller still. +/// +/// `rom.Cache_Invalidate_All` is the instrument that ought to do this and does not: called from an +/// image the ROM did not launch, it faults inside the ROM with its argument stranded in `a2`. +/// Capacity eviction needs no preconditions, which is the whole reason it is what ships. 512 KiB at +/// a 64-byte stride is 8,192 loads, once per boot. +pub fn flushFlashCache() void { + var sink: u32 = 0; + var p: u32 = 0x4000_0000; + while (p < 0x4008_0000) : (p += 64) { + sink +%= @as(*volatile u32, @ptrFromInt(p)).*; + } + // Consumed through a volatile store, or the optimiser drops the whole loop as dead. + @as(*volatile u32, &flush_sink).* = sink; +} + +var flush_sink: u32 = 0; + /// Busy-wait for a number of CPU cycles, using the cycle counter rather than the mask ROM. Useful /// when an image must not depend on ROM entry points at all, and for delays shorter than the ROM's /// microsecond granularity. |
