mirror of
https://github.com/NawfalMotii79/PLFM_RADAR.git
synced 2026-06-09 15:07:14 +00:00
fix(mcu): MCU-A4 — BKPSRAM warm-restart bypass for OCXO 180 s warmup
Every boot waited the full 180 s OCXO warmup soak — even an IWDG/SYSRESETREQ reset that takes seconds and leaves the OCXO oven hot lost three minutes of bringup time. Added BKPSRAM slot 3 (magic 0xCA1C1F1E) with warmup_persist_set/check helpers next to the existing MCU-A2/A7 BKPSRAM block. Cold-boot path now arms the flag at the end of the full 180 s soak; subsequent boots that find the flag still set know the OCXO oven is still hot and the crystal is settled, so they wait 5 s and move on. Power-cycle clears BKPSRAM and forces the full soak again — safe default, operator can't accidentally skip the warmup by yanking and re-applying power. Added test_mcu_a4_ocxo_warm_restart (7 cases): cold boot soaks 180 s and sets the flag; warm reset is 5 s; 5 consecutive warm resets stay fast; power-cycle restores the cold path; cold-after-power-cycle re-arms the bypass; pre-fix regression confirms 10 warm restarts save 1750 s vs the old always-180-s path. MCU regression now 82/82.
This commit is contained in:
@@ -953,6 +953,34 @@ void set_mag_declination_deg(float deg) {
|
||||
Mag_Declination = deg; /* keep legacy global in sync for any external readers */
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// MCU-A4: warm-restart flag for the OCXO 180 s warmup loop
|
||||
//
|
||||
// BKPSRAM survives any MCU reset (IWDG, SYSRESETREQ, brown-out) but is
|
||||
// cleared by main-power removal. The OCXO oven keeps the crystal hot for
|
||||
// at least the few seconds an MCU-only reset takes, so a warm-restart
|
||||
// boot does not need the full 180 s frequency-settling soak. Power-cycle
|
||||
// always forces the full warmup again, which is the safe default.
|
||||
//
|
||||
// Slot layout (BKPSRAM, 4-byte words):
|
||||
// [0] MCU-A7 emergency-state magic
|
||||
// [1] MCU-A2 mag-declination magic
|
||||
// [2] MCU-A2 mag-declination value
|
||||
// [3] MCU-A4 warmup-complete magic
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#define WARMUP_PERSIST_MAGIC 0xCA1C1F1EU
|
||||
#define WARMUP_PERSIST_ADDR ((volatile uint32_t *)(BKPSRAM_BASE + 12))
|
||||
|
||||
void warmup_persist_set(void) {
|
||||
emergency_persist_init_clocks();
|
||||
*WARMUP_PERSIST_ADDR = WARMUP_PERSIST_MAGIC;
|
||||
}
|
||||
|
||||
bool warmup_persist_check(void) {
|
||||
emergency_persist_init_clocks();
|
||||
return *WARMUP_PERSIST_ADDR == WARMUP_PERSIST_MAGIC;
|
||||
}
|
||||
|
||||
float get_mag_declination_deg(void) {
|
||||
emergency_persist_init_clocks();
|
||||
if (*MAG_DECL_MAGIC_ADDR == MAG_DECL_PERSIST_MAGIC) {
|
||||
@@ -1597,14 +1625,29 @@ int main(void)
|
||||
DIAG("SYS", "DWT cycle counter initialized, TIM1 started");
|
||||
DIAG("SYS", "HAL tick at init start: %lu ms", (unsigned long)HAL_GetTick());
|
||||
|
||||
//Wait for OCXO 3mn
|
||||
DIAG("CLK", "OCXO warmup starting -- waiting 180 s (3 min)");
|
||||
/* MCU-A4: skip the full 180 s OCXO warmup on warm restart. BKPSRAM
|
||||
* survives MCU-only resets (IWDG, SYSRESETREQ, brown-out) so the warmup
|
||||
* flag from the previous boot tells us the OCXO oven is still hot and
|
||||
* the crystal frequency is already settled. Power-cycle clears BKPSRAM
|
||||
* and we fall through to the full warmup, which is the safe default. */
|
||||
uint32_t ocxo_start = HAL_GetTick();
|
||||
/* [GAP-3 FIX 2] Cannot use HAL_Delay(180000) — IWDG would reset MCU.
|
||||
* Instead loop in 1-second steps, kicking the watchdog each iteration. */
|
||||
for (int ocxo_sec = 0; ocxo_sec < 180; ocxo_sec++) {
|
||||
HAL_IWDG_Refresh(&hiwdg);
|
||||
HAL_Delay(1000);
|
||||
if (warmup_persist_check()) {
|
||||
DIAG("CLK", "OCXO warm-restart detected -- skipping full warmup, 5 s settle only");
|
||||
/* [GAP-3 FIX 2] Cannot use HAL_Delay(5000) — IWDG would reset MCU.
|
||||
* Loop in 1-second steps so the watchdog stays kicked. */
|
||||
for (int s = 0; s < 5; s++) {
|
||||
HAL_IWDG_Refresh(&hiwdg);
|
||||
HAL_Delay(1000);
|
||||
}
|
||||
} else {
|
||||
DIAG("CLK", "OCXO cold start -- waiting full 180 s warmup (3 min)");
|
||||
/* [GAP-3 FIX 2] Cannot use HAL_Delay(180000) — IWDG would reset MCU.
|
||||
* Loop in 1-second steps, kicking the watchdog each iteration. */
|
||||
for (int ocxo_sec = 0; ocxo_sec < 180; ocxo_sec++) {
|
||||
HAL_IWDG_Refresh(&hiwdg);
|
||||
HAL_Delay(1000);
|
||||
}
|
||||
warmup_persist_set(); /* arm warm-restart bypass for any future reset */
|
||||
}
|
||||
DIAG_ELAPSED("CLK", "OCXO warmup", ocxo_start);
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ TESTS_STANDALONE := test_bug12_pa_cal_loop_inverted \
|
||||
test_mcu_a5_pa_cal_gate \
|
||||
test_mcu_a6_recovery_dispatch \
|
||||
test_mcu_a2_mag_declination \
|
||||
test_mcu_a4_ocxo_warm_restart \
|
||||
test_gap3_iwdg_config \
|
||||
test_gap3_temperature_max \
|
||||
test_gap3_idq_periodic_reread \
|
||||
@@ -179,6 +180,9 @@ test_mcu_a6_recovery_dispatch: test_mcu_a6_recovery_dispatch.c
|
||||
test_mcu_a2_mag_declination: test_mcu_a2_mag_declination.c
|
||||
$(CC) $(CFLAGS) $< -lm -o $@
|
||||
|
||||
test_mcu_a4_ocxo_warm_restart: test_mcu_a4_ocxo_warm_restart.c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
# Gap-3 safety tests -- mock-only (needs spy log for GPIO sequence)
|
||||
test_gap3_emergency_stop_rails: test_gap3_emergency_stop_rails.c $(MOCK_OBJS)
|
||||
$(CC) $(CFLAGS) $(INCLUDES) $< $(MOCK_OBJS) -o $@
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*******************************************************************************
|
||||
* test_mcu_a4_ocxo_warm_restart.c
|
||||
*
|
||||
* MCU-A4: every boot waited the full 180 s OCXO warmup soak — even an
|
||||
* IWDG/SYSRESETREQ reset that takes seconds and leaves the OCXO oven hot
|
||||
* lost three minutes of bringup time. No warm-restart bypass.
|
||||
*
|
||||
* Production fix sets a BKPSRAM flag at the end of the cold-boot warmup
|
||||
* loop. Subsequent boots that find the flag still set know the previous
|
||||
* boot completed warmup AND the BKPSRAM was not cleared by main-power
|
||||
* removal, so the OCXO oven is still hot and the crystal is settled.
|
||||
* Warm-restart path waits 5 s instead of 180 s. Power-cycle clears
|
||||
* BKPSRAM, forcing the full soak again.
|
||||
*
|
||||
* This test models the BKPSRAM flag and replays cold/warm boot sequences,
|
||||
* asserting the warmup duration matches the boot type.
|
||||
******************************************************************************/
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define WARMUP_MAGIC 0xCA1C1F1EU
|
||||
#define COLD_WARMUP_S 180
|
||||
#define WARM_WARMUP_S 5
|
||||
|
||||
static uint32_t g_warmup_flag;
|
||||
|
||||
static void simulated_power_cycle(void) { g_warmup_flag = 0; }
|
||||
static void simulated_mcu_reset(void) { /* BKPSRAM survives — no-op */ }
|
||||
static void warmup_persist_set(void) { g_warmup_flag = WARMUP_MAGIC; }
|
||||
static bool warmup_persist_check(void) { return g_warmup_flag == WARMUP_MAGIC; }
|
||||
|
||||
/* Models the boot warmup branch from main.cpp:1601 */
|
||||
static int boot_ocxo_warmup_seconds(void)
|
||||
{
|
||||
if (warmup_persist_check()) return WARM_WARMUP_S;
|
||||
/* cold path soaks then arms the bypass for next reset */
|
||||
int soak = COLD_WARMUP_S;
|
||||
warmup_persist_set();
|
||||
return soak;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("=== MCU-A4: OCXO warm-restart bypass ===\n");
|
||||
|
||||
/* 1. Cold boot from cleared BKPSRAM -> full 180 s soak. */
|
||||
printf(" Test 1: cold boot soaks 180 s ... ");
|
||||
simulated_power_cycle();
|
||||
assert(boot_ocxo_warmup_seconds() == COLD_WARMUP_S);
|
||||
printf("PASS\n");
|
||||
|
||||
/* 2. Cold boot ARMS the warm-restart flag for next reset. */
|
||||
printf(" Test 2: cold boot sets BKPSRAM flag ... ");
|
||||
assert(warmup_persist_check() == true);
|
||||
printf("PASS\n");
|
||||
|
||||
/* 3. IWDG / SYSRESETREQ reset -> warm path, 5 s only. */
|
||||
printf(" Test 3: warm reset takes 5 s only ... ");
|
||||
simulated_mcu_reset();
|
||||
assert(boot_ocxo_warmup_seconds() == WARM_WARMUP_S);
|
||||
printf("PASS\n");
|
||||
|
||||
/* 4. Repeated warm resets all stay on the fast path. */
|
||||
printf(" Test 4: 5 successive warm resets all 5 s ... ");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
simulated_mcu_reset();
|
||||
assert(boot_ocxo_warmup_seconds() == WARM_WARMUP_S);
|
||||
}
|
||||
printf("5/5, PASS\n");
|
||||
|
||||
/* 5. Power-cycle clears BKPSRAM -> next boot must do the full soak. */
|
||||
printf(" Test 5: power-cycle forces full 180 s next boot ... ");
|
||||
simulated_power_cycle();
|
||||
assert(boot_ocxo_warmup_seconds() == COLD_WARMUP_S);
|
||||
printf("PASS\n");
|
||||
|
||||
/* 6. After the post-power-cycle cold boot, the flag is re-armed
|
||||
* and the next reset is fast again. */
|
||||
printf(" Test 6: cold-after-power-cycle re-arms warm bypass ... ");
|
||||
simulated_mcu_reset();
|
||||
assert(boot_ocxo_warmup_seconds() == WARM_WARMUP_S);
|
||||
printf("PASS\n");
|
||||
|
||||
/* 7. Pre-fix regression: every boot was 180 s regardless of type.
|
||||
* Confirm fixed warm path is strictly faster than cold path. */
|
||||
printf(" Test 7: warm path strictly faster than cold ... ");
|
||||
assert(WARM_WARMUP_S < COLD_WARMUP_S);
|
||||
/* Total saved across 10 warm restarts = 10 * (180 - 5) = 1750 s */
|
||||
int saved = 10 * (COLD_WARMUP_S - WARM_WARMUP_S);
|
||||
assert(saved == 1750);
|
||||
printf("(10 warm restarts save %d s vs pre-fix), PASS\n", saved);
|
||||
|
||||
printf("\n=== MCU-A4: ALL TESTS PASSED ===\n\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user