Skip to content

Commit 1d35e5b

Browse files
committed
Merge branch 'next'
fix(p4): execute_from_psram to fix PSRAM boot crash-loop (#31) Claude-Session: https://claude.ai/code/session_01CDQ9ccpnsu3YtiVkvPqZtr
2 parents d66fa4f + c1a69c8 commit 1d35e5b

8 files changed

Lines changed: 38 additions & 12 deletions

File tree

boards/esp32p4-evboard.yaml

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@
77
# ESP32-C6/-C5 companion radio over SDIO, configured via `esp32_hosted:` below.
88
# NOTE — P4 PSRAM is HEX mode only; the only valid speeds are 20 / 100 / 200 MHz.
99
# Default is 200MHz, which runs fine on the Function-EV-Board and typical P4
10-
# modules. If YOUR specific board crash-loops at boot with
11-
# `esp_task_stack_is_sane_cache_disabled`, its PSRAM chip doesn't tolerate these
12-
# settings — drop to `speed: 100MHz` or `20MHz`, or remove the psram: block to
13-
# run without PSRAM. That's a per-board PSRAM/board-definition mismatch, not the
14-
# speed being universally wrong (see discussion #31).
10+
# modules.
11+
#
12+
# IMPORTANT — the P4 needs `execute_from_psram: true` (below, under
13+
# framework.advanced) to boot reliably with PSRAM. Without it, some P4 boards
14+
# crash-loop at boot with `esp_task_stack_is_sane_cache_disabled`: a
15+
# PSRAM-resident task stack becomes unreachable during the flash cache-disable
16+
# window. `execute_from_psram` maps to ESP-IDF's XIP-from-PSRAM, which keeps
17+
# PSRAM accessible through those windows and fixes the crash (discussion #31).
18+
# If a board STILL crash-loops with the flag set, drop to `speed: 100MHz` or
19+
# `20MHz`, or remove the psram: block to run without PSRAM.
1520

1621
esphome:
1722
name: tigo-server
@@ -32,6 +37,11 @@ esp32:
3237
# P4's experimental-features gate. Harmless, and matches the known-good
3338
# TigoMonitorP4 config; some IDF/board combos want it for PSRAM.
3439
enable_idf_experimental_features: yes
40+
# Execute code in-place from PSRAM (XIP). Required on the P4 so
41+
# PSRAM-resident task stacks stay reachable during flash cache-disable
42+
# windows — without it some boards crash-loop at boot with
43+
# `esp_task_stack_is_sane_cache_disabled` (discussion #31).
44+
execute_from_psram: true
3545
components:
3646
# esp_tsdb backs the History view; littlefs is its filesystem. The P4
3747
# target still needs the RAR/esp_tsdb fork (upstream 2.1.0's manifest
@@ -64,7 +74,8 @@ esp32:
6474
CONFIG_LWIP_MAX_LISTENING_TCP: "16"
6575

6676
# Enable PSRAM. P4 is hex-mode only; valid speeds are 20 / 100 / 200 MHz.
67-
# If your board crash-loops at boot, drop to 100MHz or 20MHz, or remove this block.
77+
# Needs execute_from_psram: true (above) to boot reliably. If your board still
78+
# crash-loops at boot, drop to 100MHz or 20MHz, or remove this block.
6879
psram:
6980
mode: hex
7081
speed: 200MHz

boards/test-p4-tigomonitor.yaml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ esp32:
2020
advanced:
2121
# P4 experimental-features gate (harmless; matches the known-good config).
2222
enable_idf_experimental_features: yes
23+
# XIP-from-PSRAM — keeps PSRAM-resident task stacks reachable during flash
24+
# cache-disable windows; without it some P4 boards crash-loop at boot with
25+
# esp_task_stack_is_sane_cache_disabled (discussion #31).
26+
execute_from_psram: true
2327
components:
2428
# Upstream 2.1.0 has everything we need code-wise, but its manifest still
2529
# lacks the esp32p4 target, so P4 stays on the RAR/esp_tsdb fork's
@@ -87,8 +91,9 @@ wifi:
8791
psram:
8892
mode: hex
8993
# P4 is hex-mode only; valid speeds 20 / 100 / 200 MHz. 200 works on the
90-
# EV-board + typical P4 modules. If a board crash-loops at boot, drop to
91-
# 100MHz/20MHz or remove this block (per-board PSRAM quirk — see #31).
94+
# EV-board + typical P4 modules. Needs execute_from_psram: true (above) to
95+
# boot reliably; if a board still crash-loops, drop to 100MHz/20MHz or remove
96+
# this block (see #31).
9297
speed: 200MHz
9398

9499
logger:

site/boards.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,10 @@ font:
232232
flash_size: '16MB',
233233
partitions: { default: 'partitions/tigo-16mb.csv' },
234234
psram: { mode: 'hex', speed: '200MHz' },
235-
frameworkAdvanced: { enable_idf_experimental_features: true },
235+
// execute_from_psram (XIP) is required on the P4 so PSRAM-resident task
236+
// stacks stay reachable during flash cache-disable windows — without it some
237+
// boards crash-loop at boot with esp_task_stack_is_sane_cache_disabled (#31).
238+
frameworkAdvanced: { enable_idf_experimental_features: true, execute_from_psram: true },
236239
frameworkComponents: ['joltwallet/littlefs^1.16'],
237240
hostedComponent: { source: 'https://github.com/RAR/esp_tsdb.git', ref: 'tigomonitor' },
238241
sdkconfig: {

site/lib/yaml-extract.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ export function extractBoardFields(text) {
2424
// `^\s*` anchors to line start so a commented-out `# enable_idf_...` line
2525
// (an opt-in left disabled) is correctly read as NOT enabled.
2626
const experimental = /^\s*enable_idf_experimental_features:\s*(yes|true)\b/m.test(text);
27+
// Same `^\s*` anchoring so a commented-out opt-in reads as NOT enabled.
28+
const executeFromPsram = /^\s*execute_from_psram:\s*(yes|true)\b/m.test(text);
2729
const hasHosted = /^esp32_hosted:\s*$/m.test(text);
2830
const components = [...text.matchAll(/^\s*-\s*(?:name:\s*)?([A-Za-z0-9_./^-]+)\s*$/gm)]
2931
.map((m) => m[1])
3032
.filter((c) => c.includes('/'));
31-
return { flash_size, partitions, psramMode, psramSpeed, experimental, hasHosted, components };
33+
return { flash_size, partitions, psramMode, psramSpeed, experimental, executeFromPsram, hasHosted, components };
3234
}

site/test/boards.shape.test.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ test('P4 defines a hosted radio + 200MHz PSRAM with the experimental flag', () =
2222
assert.ok(p4.hosted, 'P4 must define an esp32_hosted companion');
2323
assert.equal(p4.psram.speed, '200MHz', 'P4 valid speeds are 20/100/200 — 200 default');
2424
assert.equal(p4.frameworkAdvanced.enable_idf_experimental_features, true);
25+
assert.equal(p4.frameworkAdvanced.execute_from_psram, true, 'P4 needs XIP-from-PSRAM to boot (#31)');
2526
});
2627

2728
test('BLE is only offered where a BLE partition exists', () => {

site/test/drift.test.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ for (const [id, rel] of Object.entries(FILE)) {
2323
assert.equal(b.psram?.mode ?? null, f.psramMode, 'psram.mode drift');
2424
assert.equal(b.psram?.speed ?? null, f.psramSpeed, 'psram.speed drift');
2525
assert.equal(b.frameworkAdvanced.enable_idf_experimental_features, f.experimental, 'experimental flag drift');
26+
assert.equal(Boolean(b.frameworkAdvanced.execute_from_psram), f.executeFromPsram, 'execute_from_psram flag drift');
2627
assert.equal(Boolean(b.hosted), f.hasHosted, 'esp32_hosted presence drift');
2728
});
2829
}

site/test/yaml.test.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ test('P4 emits esp32_hosted + 200MHz PSRAM + experimental flag; no psram-less bo
2727
assert.ok(p4.includes('esp32_hosted:'));
2828
assert.ok(p4.includes('speed: 200MHz'), 'P4 default is 200MHz (valid P4 speeds: 20/100/200)');
2929
assert.ok(p4.includes('enable_idf_experimental_features: true'), 'P4 emits the experimental flag');
30+
assert.ok(p4.includes('execute_from_psram: true'), 'P4 emits execute_from_psram (XIP) — fixes #31 boot crash');
3031
assert.ok(!p4.includes('80MHz'), 'P4 must not emit 80MHz — invalid for P4 (cv.one_of 20/100/200)');
3132
const s3lite = toYaml(assembleConfig(getBoard('esp32s3-atoms3'), { ...form, uart: { tx_pin: 'GPIO6', rx_pin: 'GPIO5' } }));
3233
assert.ok(!s3lite.includes('\npsram:'), 'no-PSRAM board must not emit psram');

site/yaml.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ export function toYaml(cfg) {
4444
L.push(`${I(1)}framework:`);
4545
L.push(`${I(2)}type: esp-idf`);
4646
L.push(`${I(2)}version: recommended`);
47-
if (cfg.esp32.frameworkAdvanced.enable_idf_experimental_features) {
47+
const adv = cfg.esp32.frameworkAdvanced;
48+
if (adv.enable_idf_experimental_features || adv.execute_from_psram) {
4849
L.push(`${I(2)}advanced:`);
49-
L.push(`${I(3)}enable_idf_experimental_features: true`);
50+
if (adv.enable_idf_experimental_features) L.push(`${I(3)}enable_idf_experimental_features: true`);
51+
if (adv.execute_from_psram) L.push(`${I(3)}execute_from_psram: true`);
5052
}
5153
const comps = [...cfg.esp32.frameworkComponents];
5254
if (cfg.esp32.hostedComponent || comps.length) {

0 commit comments

Comments
 (0)