feat: add lab-rv32i-freertos-heap-models card

This commit is contained in:
user
2026-07-21 19:13:55 +02:00
commit 70fea255f9
64 changed files with 33932 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
# K05 — C++ heap bridge, heap models and HeapStats
## Position
- Series: FreeRTOS C++
- Lesson: L04, card K05
- Duration: 30 minutes
- Runtime: real FreeRTOS V11.3.0 `heap_4.c` on Hazard3/RV32I
- C++ mode: freestanding C++17, no exceptions, RTTI or hosted `libstdc++`
- New surface: `HeapStats`, `HeapWatermark`, complete `new`/`delete` bridge
K03 proved one-owner RAII, while K05 now exposes the allocator policy and its
metrics. It does not introduce runtime resource selection; K06 will build that
on top of the explicit failure and alignment contract established here.
## Outcome
After 30 minutes the student can choose among `heap_1``heap_5` for a stated
memory topology/lifetime policy, enumerate all C++ allocation bridge forms,
distinguish total free bytes from largest free block, and prove that
minimum-ever free bytes retain history after current free returns to baseline.
## Lesson plan — 30 minutes
| Time | Mode | Evidence |
| --- | --- | --- |
| 05 | choose policy | justify heap scheme for three deployments |
| 59 | bridge | trace `new[] -> pvPortMalloc` and `delete[] -> vPortFree` |
| 914 | predict | draw A/B/C blocks and predicted stats after freeing A/C |
| 1421 | Hazard3 | prove `request < total`, `request > largest`, failure |
| 2126 | coalescence | free B; retry same request; inspect one free block |
| 2630 | history/exit | baseline recovery versus minimum-ever history |
## Policy table
| Scheme | Free? | Coalesce? | Key deployment constraint |
| --- | --- | --- | --- |
| heap_1 | no | n/a | monotonic startup allocation only |
| heap_2 | yes | no | holes remain separate; fragmentation grows |
| heap_3 | libc | libc | linker/compiler must provide a suitable heap |
| heap_4 | yes | yes | one FreeRTOS-owned region |
| heap_5 | yes | yes | regions defined in increasing address order before allocation |
Student choices:
1. All objects allocated once at startup, never deleted: `heap_1` is a valid
simplest policy.
2. One freestanding RAM region, dynamic tasks/queues with deletion: `heap_4`.
3. Two disjoint RAM banks that must both feed the kernel allocator: `heap_5`.
`heap_3` is not treated as a portable freestanding default because it delegates
storage and behavior to the compiler C library and linker heap.
## Canonical experiment
```text
baseline: [ free ........................................ ]
allocate: [ A 2K ][ B 4K ][ C 2K ][ free ............... ]
free A/C: [ free ][ B live ][ free + trailing free ...... ]
total=12256, largest=10192, blocks=2
request 10193 -> FAIL (sum is enough, no individual block is)
free B: [ one coalesced free block ..................... ]
retry 10193 -> SUCCESS
free retry -> current free returns to baseline
minimum-ever remains at the historical low
```
Exact values may change with alignment/header configuration. Assessment uses
relations, except for the fixed requested payload sizes in the source.
## Stable evidence contract
| Evidence | Required relation |
| --- | --- |
| C++ addresses | three distinct, aligned addresses inside `ucHeap` |
| bridge counters | allocations=3, deallocations=3, live=0 |
| deallocation order | A, C, B |
| fragmented stats | free blocks >=2 and total > largest |
| failed request | largest < request < total; successful allocations unchanged |
| coalesced stats | free blocks=1; largest=current=baseline |
| retry | same request succeeds inside `ucHeap` |
| final stats | current=baseline, minimum-ever remains below current |
## Complete bridge contract
```cpp
void* operator new(size_t);
void* operator new[](size_t);
void operator delete(void*) noexcept;
void operator delete[](void*) noexcept;
void operator delete(void*, size_t) noexcept;
void operator delete[](void*, size_t) noexcept;
```
Ordinary `new` is fail-fast in this course profile because exceptions are off.
The recoverable experiment uses raw `pvPortMalloc` and checks `nullptr`.
## Misconceptions
1. Total free bytes do not imply one payload of that size can be allocated.
2. Returning to the current-free baseline does not erase the minimum-ever
watermark.
3. `heap_4` limits external fragmentation by coalescing; it cannot promise
absence of all fragmentation while live blocks separate holes.
4. A C++ bridge selects one heap domain; it does not merge five schemes.
5. `heap_3` inherits the C library/linker heap contract and is therefore not a
self-contained freestanding answer.
## Acceptance
- host test verifies field mapping and current/peak watermark calculations;
- ABI test finds all six allocation functions and no hosted C++ runtime;
- Hazard3 reaches all seven checkpoints and PASS;
- failure request lies strictly between largest block and total free bytes;
- retry succeeds only after B is freed and blocks coalesce;
- student chooses heap_1, heap_4 and heap_5 for the three stated scenarios and
justifies each constraint.