/* ============================================================================= * rtos_hooks.c - FreeRTOS 후크/정적 메모리 제공자 * * FreeRTOSConfig.h 에서 켜진 후크들을 구현한다: * - configCHECK_FOR_STACK_OVERFLOW=2 -> vApplicationStackOverflowHook * - configUSE_MALLOC_FAILED_HOOK=1 -> vApplicationMallocFailedHook * - configUSE_IDLE_HOOK=1 -> vApplicationIdleHook (IWDG refresh) * - configSUPPORT_STATIC_ALLOCATION=1-> vApplicationGetIdleTaskMemory / * vApplicationGetTimerTaskMemory * - configASSERT -> vAssertCalled * * 치명적 오류(스택 오버플로/malloc 실패/assert) 처리 전략: * 인터럽트를 끄고, 디버거가 붙어 있으면 정지(BKPT), 아니면 무한 루프에 머문다. * 이때 IWDG refresh 가 멈추므로 워치독이 MCU 를 안전하게 리셋한다. * 가능하면 bsp_fatal() 로 LED 점멸/로그를 남긴다(약결합 weak fallback 제공). * ===========================================================================*/ #include "FreeRTOS.h" #include "task.h" #include "timers.h" #include #include "bsp.h" /* bsp_fatal(const char*) — 안전한 오류 표시기 */ #include "watchdog.h" /* watchdog_refresh() — idle 후크에서 호출 */ /* ----------------------------------------------------------------------------- * bsp_fatal 약결합 fallback. * bsp.c 가 실제 구현(LED 점멸 + 디버그 정지)을 제공한다. 단위/호스트 테스트나 * bsp 미링크 환경에서도 본 파일이 단독으로 빌드되도록 weak 기본 구현을 둔다. * 실제 펌웨어 링크 시에는 bsp.c 의 강한 심볼이 이 정의를 대체한다. * ---------------------------------------------------------------------------*/ __attribute__((weak)) void bsp_fatal(const char *reason) { (void)reason; /* 모든 인터럽트 차단 (FromISR 경로 포함). 이후 워치독만이 복구 경로. */ taskDISABLE_INTERRUPTS(); #if defined(__GNUC__) && (defined(__ARM_ARCH) || defined(__arm__)) /* 디버거가 붙어 있으면 정지(C_DEBUGEN 비트). 아니면 그냥 통과 후 루프. */ volatile uint32_t *const dhcsr = (volatile uint32_t *)0xE000EDF0u; if ((*dhcsr & 0x1u) != 0u) { __asm volatile ("bkpt #0"); } #endif /* 무한 루프 — watchdog_refresh() 가 호출되지 않으므로 IWDG 가 리셋시킨다. */ for (;;) { /* 의도적으로 비움. */ } } /* ============================================================================= * 스택 오버플로 후크 (configCHECK_FOR_STACK_OVERFLOW=2) * 스케줄러가 컨텍스트 스위치 시 스택 한계를 넘었다고 판단하면 호출한다. * 태스크 이름을 reason 으로 넘겨 bsp_fatal 로 이관. * ===========================================================================*/ void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName) { (void)xTask; bsp_fatal(pcTaskName ? pcTaskName : "stack-overflow"); } /* ============================================================================= * malloc 실패 후크 (configUSE_MALLOC_FAILED_HOOK=1) * heap_4 의 pvPortMalloc 이 실패하면 호출. 힙 고갈/단편화 -> 안전 리셋. * ===========================================================================*/ void vApplicationMallocFailedHook(void) { bsp_fatal("malloc-failed"); } /* ============================================================================= * Idle 후크 (configUSE_IDLE_HOOK=1) * Idle 태스크가 돌 때마다 호출. 모든 애플리케이션 태스크가 정상적으로 양보하여 * Idle 까지 도달했다는 신호이므로 여기서 IWDG 를 갱신한다. * (헬스 루프 데드락/기아 상태면 Idle 에 도달하지 못해 워치독이 리셋.) * * 주의: Idle 후크 안에서는 블로킹 API(vTaskDelay 등)를 호출하면 안 된다. * watchdog_refresh() 는 IWDG_KR 레지스터 한 번 쓰기로 논블로킹. * ===========================================================================*/ void vApplicationIdleHook(void) { watchdog_refresh(); /* WFI 로 코어를 잠재워 소비전력/발열을 줄인다(다음 인터럽트까지 대기). */ __asm volatile ("wfi"); } /* ============================================================================= * configASSERT 처리기 * 파일/라인 정보를 보존하기 위해 지역 정적에 저장한 뒤 bsp_fatal 로 정지. * ===========================================================================*/ void vAssertCalled(const char *pcFile, unsigned long ulLine) { /* 디버거에서 검사할 수 있도록 컨텍스트 보존(최적화 제거 방지: volatile). */ static volatile const char *file; static volatile unsigned long line; file = pcFile; line = ulLine; (void)file; (void)line; bsp_fatal("assert"); } /* ============================================================================= * 정적 할당 콜백 (Idle/Timer 태스크 메모리) * * FreeRTOSConfig.h 는 configSUPPORT_STATIC_ALLOCATION=1 + * configKERNEL_PROVIDED_STATIC_MEMORY=1 (FreeRTOS-Kernel V11.1.0, 벤더 고정)로 * 설정되어 있다. 이 조합에서 커널의 tasks.c/timers.c 가 vApplicationGetIdleTaskMemory * / vApplicationGetTimerTaskMemory 를 **강한(non-weak) 심볼로 직접 제공**한다. * 따라서 여기서 같은 함수를 정의하면 다중 정의(multiple definition) 링크 오류가 난다. * -> 이 파일에서는 두 콜백을 정의하지 않는다(커널이 소유). * * 만약 더 낮은 커널(예: V10.x, configKERNEL_PROVIDED_STATIC_MEMORY 미지원)으로 * 교체하면 위 두 콜백이 미정의(undefined reference)가 되므로, 그때 이 자리에 * 명시적 제공자를 다시 추가한다. (커널 프로토타입의 stack-size 인자 타입은 * configSTACK_DEPTH_TYPE* 임에 유의.) * ===========================================================================*/