ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
psram_pool.h
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file psram_pool.h
6 * @brief Buffer placement policy (DRAM vs PSRAM) + SPI DMA ping-pong index manager
7 * (PC_ENABLE_PSRAM_POOL).
8 *
9 * An ESP32 with PSRAM has two heaps: fast internal DRAM (scarce, DMA-capable) and large external PSRAM
10 * (roomy, but not DMA-capable for most peripherals and slower). Serving big web assets / net buffers well
11 * means putting the large, cold buffers in PSRAM and keeping the small, hot, DMA buffers in DRAM, while
12 * always leaving an internal-DRAM reserve so the stack does not starve. That placement choice is a pure
13 * policy; the actual `heap_caps_calloc(..., MALLOC_CAP_SPIRAM / MALLOC_CAP_DMA)` is the app's.
14 *
15 * This module is that policy (`pc_psram_place`) plus the classic SPI DMA **ping-pong** double-buffer
16 * bookkeeping (`pc_pingpong_*`): while DMA drains one buffer, the CPU fills the other, and a swap
17 * exchanges their roles. Pure, no heap, no stdlib, host-testable.
18 */
19
20#ifndef PROTOCORE_PSRAM_POOL_H
21#define PROTOCORE_PSRAM_POOL_H
22
23#include "protocore_config.h"
24#include <stddef.h>
25#include <stdint.h>
26
27#if PC_ENABLE_PSRAM_POOL
28
29/** @brief Placement verdict (the sole return of pc_psram_place). */
30enum class pc_place : uint8_t
31{
32 PLACE_DRAM = 0, ///< allocate in internal DRAM.
33 PLACE_PSRAM = 1, ///< allocate in external PSRAM.
34 PLACE_FAIL = 2 ///< neither heap can satisfy the request.
35};
36
37/**
38 * @brief Decide where a buffer should live.
39 *
40 * Rules: a zero-size request fails; a DMA-required buffer must go in DRAM (or fail); a buffer at or above
41 * @p psram_threshold prefers PSRAM (falling back to DRAM); a smaller buffer prefers DRAM (falling back to
42 * PSRAM). A DRAM placement must still leave @p dram_reserve bytes free in DRAM.
43 *
44 * @param size requested bytes.
45 * @param dma_required true if the buffer must be DMA-capable (DRAM only).
46 * @param free_dram currently free internal DRAM.
47 * @param free_psram currently free PSRAM (0 if no PSRAM).
48 * @param psram_threshold size at/above which PSRAM is preferred.
49 * @param dram_reserve internal DRAM to keep free after a DRAM placement.
50 * @return PLACE_DRAM / PLACE_PSRAM / PLACE_FAIL.
51 */
52pc_place pc_psram_place(size_t size, bool dma_required, size_t free_dram, size_t free_psram, size_t psram_threshold,
53 size_t dram_reserve);
54
55/** @brief SPI DMA ping-pong double-buffer state. */
56struct PingPong
57{
58 uint8_t fill_idx; ///< buffer the CPU is filling (DMA drains the other).
59};
60
61/** @brief Initialize: CPU fills buffer 0, DMA drains buffer 1. */
62void pc_pingpong_init(PingPong *pp);
63
64/** @brief The buffer index the CPU should fill. */
65uint8_t pc_pingpong_fill_index(const PingPong *pp);
66
67/** @brief The buffer index DMA should drain (the other one). */
68uint8_t pc_pingpong_drain_index(const PingPong *pp);
69
70/** @brief Swap roles (a filled buffer is handed to DMA; the drained one is now filled). @return new fill index. */
71uint8_t pc_pingpong_swap(PingPong *pp);
72
73#endif // PC_ENABLE_PSRAM_POOL
74#endif // PROTOCORE_PSRAM_POOL_H
User-facing configuration for ProtoCore.