#!/bin/bash # check_stack.sh — Teensy 4.1 FlexRAM stack gauge. # # Computes the same ITCM/DTCM split the i.MX RT1062 boot ROM will perform, and # fails if the resulting free stack is below a threshold. The linker cannot # catch this because the split is computed at reset from _itcm_block_count, # not at link time. # # Usage: check_stack.sh [min_stack_bytes] # min_stack_bytes defaults to 16384 (16 KB). # # Exits 0 if the stack is above the threshold, 1 if below (with a diagnostic). # # The arithmetic (from imxrt1062_t41_flashmem.ld:215-217): # itcm_banks = ceil((.text.itcm + .ARM.exidx) / 32768) # dtcm_total = (16 - itcm_banks) * 32768 # free_stack = dtcm_total - .data - .bss # _estack = ORIGIN(DTCM) + dtcm_total (stack grows down from here) # # Note: .bss here is the DTCM .bss (RAM1), NOT .bss.dma (RAM2/OCRAM). The # .bss.dma section lives in a separate 512 KB region and does not affect the # stack. set -euo pipefail ELF="${1:-}" MIN_STACK="${2:-16384}" if [ -z "$ELF" ] || [ ! -f "$ELF" ]; then echo "check_stack: usage: $0 [min_stack_bytes]" >&2 exit 2 fi # Locate the arm-none-eabi-size tool from the Teensy toolchain. SIZE_BIN="" for candidate in \ /home/user/.arduino15/packages/teensy/tools/teensy-compile/*/arm/bin/arm-none-eabi-size \ "$(dirname "$0")/../.arduino15/packages/teensy/tools/teensy-compile/*/arm/bin/arm-none-eabi-size"; do if [ -x "$candidate" ]; then SIZE_BIN="$candidate"; break; fi done # Fall back to PATH if [ -z "$SIZE_BIN" ]; then SIZE_BIN="$(command -v arm-none-eabi-size || true)"; fi if [ -z "$SIZE_BIN" ]; then echo "check_stack: cannot find arm-none-eabi-size" >&2 exit 2 fi # We need per-section sizes, not the aggregate. Use objdump -h. OBJDUMP_BIN="${SIZE_BIN%size}objdump" # Extract section sizes (in bytes, decimal) by name. get_section_size() { local section="$1" # objdump -h prints: idx Name Size VMA LMA File-off Al "$OBJDUMP_BIN" -h "$ELF" 2>/dev/null \ | awk -v sec="$section" '$2 == sec { print strtonum("0x"$3); exit }' } TEXT_ITCM=$(get_section_size ".text.itcm") ARM_EXIDX=$(get_section_size ".ARM.exidx") DATA_SEC=$(get_section_size ".data") BSS_SEC=$(get_section_size ".bss") BSS_DMA=$(get_section_size ".bss.dma") # Guard against missing sections (e.g. exidx may be 0/absent). : "${TEXT_ITCM:=0}" : "${ARM_EXIDX:=0}" : "${DATA_SEC:=0}" : "${BSS_SEC:=0}" : "${BSS_DMA:=0}" BANK=32768 ITCM_BYTES=$(( TEXT_ITCM + ARM_EXIDX )) ITCM_BANKS=$(( (ITCM_BYTES + BANK - 1) / BANK )) DTCM_TOTAL=$(( (16 - ITCM_BANKS) * BANK )) DATA_BSS=$(( DATA_SEC + BSS_SEC )) FREE_STACK=$(( DTCM_TOTAL - DATA_BSS )) # Also report RAM2 (OCRAM) usage for completeness. RAM2_TOTAL=$(( 512 * 1024 )) RAM2_FREE=$(( RAM2_TOTAL - BSS_DMA )) echo "=== Teensy 4.1 FlexRAM stack gauge ===" echo " .text.itcm : $(printf '%7d' $TEXT_ITCM) bytes" echo " .ARM.exidx : $(printf '%7d' $ARM_EXIDX) bytes" echo " ITCM total : $(printf '%7d' $ITCM_BYTES) bytes -> $ITCM_BANKS banks ($(( ITCM_BANKS * BANK )) bytes)" echo " .data (DTCM) : $(printf '%7d' $DATA_SEC) bytes" echo " .bss (DTCM) : $(printf '%7d' $BSS_SEC) bytes" echo " DTCM total : $(printf '%7d' $DTCM_TOTAL) bytes ($(( 16 - ITCM_BANKS )) banks)" echo " data + bss : $(printf '%7d' $DATA_BSS) bytes" echo " FREE STACK : $(printf '%7d' $FREE_STACK) bytes (threshold: $MIN_STACK)" echo " ---" echo " .bss.dma RAM2: $(printf '%7d' $BSS_DMA) / $RAM2_TOTAL bytes (free: $RAM2_FREE)" if [ "$FREE_STACK" -lt "$MIN_STACK" ]; then echo "" echo " ❌ FAIL: free stack $FREE_STACK < threshold $MIN_STACK" echo " The boot ROM will allocate $ITCM_BANKS ITCM banks, leaving only" echo " $DTCM_TOTAL bytes of DTCM. .data+.bss needs $DATA_BSS bytes." echo " Reduce ITCM (route more code to FLASH) or reduce .data/.bss" echo " (move .rodata to FLASH)." exit 1 fi echo "" echo " ✅ OK: free stack $FREE_STACK >= threshold $MIN_STACK" exit 0