Expand description
Global heap ownership + external PSRAM integration.
The BSP owns the global heap so a binary never spells out
esp_alloc::heap_allocator! or the per-board region sizes itself:
init_heap declares the esp-alloc DRAM regions for a HeapProfile and,
optionally, registers external PSRAM. esp-alloc’s global heap holds at most
three regions, so each profile registers the reclaimed-ROM region, the
plain-DRAM region, and (when PSRAM is supplied) the external region — never a
fourth (a 4th add_region panics silently). The sizes are the HIL-proven
per-board values previously copied into every binary.
use m5stack_core::mem::{self, HeapProfile};
// After board init, before the first allocation:
mem::init_heap(HeapProfile::Default); // DRAM regions only — never touches PSRAM
mem::init_heap(HeapProfile::Lvgl);init_heap never touches PSRAM — it owns only the DRAM region sizes for a
HeapProfile. PSRAM is a separate, deliberate decision: see
psram_map / psram_split below. The PSRAM-specific surface (those two,
the checked psram_box / psram_vec, PsramSafe) needs the psram
feature; the heap regions and dma_buffer need only heap.
Both boards carry SPI PSRAM (Fire27: ~4 MB, CoreS3: ~8 MB). esp-alloc
exposes a single global heap that can be backed by several regions. Getting
PSRAM into one of those regions is opt-in and explicit — there is no
function that puts PSRAM behind the global allocator as a side effect of
anything else, because once a region carries external capability, every
plain alloc::vec! / Box / String in the whole crate graph — not just
your own code — becomes eligible to silently spill into it once internal
DRAM is exhausted (esp-alloc has no “external, but not for capability-less
requests” region flag).
psram_map— the default. Maps PSRAM and hands back the whole region as a private slice. Nothing is registered with the global heap; nothing here is ever reachable by a plain allocation. Hand the slice to a foreign allocator (e.g. LVGL’s TLSF) or use it directly.psram_split— deliberate global exposure. Carves a private region off the base (as above) and registers the remainder with the global heap, for the checkedpsram_box/psram_vechelpers (which reject atomic-bearing types at compile time — seePsramSafe). Reach for this only when you’ve decided part (or withreserve: 0, all) of PSRAM should be globally exposed, and accept what that costs.
use m5stack_core::mem;
// Never touches the global allocator:
let psram = mem::psram_map(peripherals.PSRAM);
// Deliberately expose part of PSRAM globally:
let split = mem::psram_split(peripherals.PSRAM, 2 * 1024 * 1024)?;
let mut big = mem::psram_vec::<u8>(512 * 1024); // in PSRAM, atomics rejected
let scratch = mem::psram_box([0u32; 1024]); // in PSRAM
let dma = mem::dma_buffer(4 * 1024); // in internal DRAM, DMA-safeThe raw marker allocators (ExternalMemory / InternalMemory) are also
re-exported as an escape hatch for allocator_api2 containers, but they do
not perform the atomic check — reach for them only when you know what
you are placing in PSRAM.
§Enforced vs. documented caveats
- Atomics must not live in PSRAM. Enforced on the checked path:
psram_box/psram_vecboundT: PsramSafe, so anything holding anAtomic*(directly or transitively) fails to compile. - DMA from PSRAM: the original ESP32 (Fire27) cannot DMA out of PSRAM.
Guarded by
assert_dma_capable(adebug_asserton Fire27, a no-op on CoreS3, which can DMA from PSRAM); usedma_bufferto get an internal-DRAM buffer in the first place. - opt-level > 0: Enforced at build time — enabling the
psramfeature withopt-level = 0fails the build (seebuild.rs). PSRAM timing calibration is unreliable unoptimized.
Structs§
- AnyMemory
- An allocator that uses all configured, available memory.
- External
Memory - An allocator that uses external (PSRAM) memory only.
- Internal
Memory - An allocator that uses internal memory only.
- Psram
Split - A private PSRAM region carved off the global heap, plus the external bytes
registered with the global heap. Returned by
psram_split.
Enums§
- Heap
Profile - Heap size profile — selects the HIL-proven per-board DRAM region sizes for a
workload. The BSP owns the sizes so every binary gets the validated values;
pass the matching profile to
init_heap. - Psram
Split Error - Why
psram_splitcould not satisfy the request.
Traits§
- Psram
Safe - Marker for types safe to store in PSRAM: nothing holding an inline atomic.
Functions§
- assert_
dma_ capable - No-op on every target except the ESP32 (Fire27); see the Fire27 variant.
- dma_
buffer - A zeroed byte buffer in internal DRAM, suitable as a DMA buffer.
- external_
free - Free bytes in the global heap’s external (PSRAM) region, right now —
0unlesspsram_splitregistered a non-empty remainder globally (a privatepsram_map/psram_splitregion is not counted here). - init_
heap - Register the global heap’s DRAM regions for
profile, using the HIL-proven per-board sizes. Call once, right aftercrate::board::init/Board::splitand before any allocation. - internal_
free - Free bytes in the global heap’s internal DRAM regions (reclaimed-ROM + plain-DRAM), right now. The tight resource on both boards; use it to measure headroom (e.g. before/after moving a subsystem’s heap to PSRAM).
- psram_
box - Allocate
valuein external PSRAM. Atomic-bearingTis rejected at compile time viaPsramSafe. - psram_
map - Map the board’s external PSRAM and return the whole region as a private
slice. Nothing is registered with the global heap — nothing returned here
is ever reachable by a plain
alloc::vec!/Box/String. - psram_
split - Map the board’s external PSRAM, carve a private region off the base, and register the remainder with the global heap.
- psram_
vec - A
Vec<T>with room forcapacityelements reserved in external PSRAM. Atomic-bearingTis rejected at compile time viaPsramSafe.