Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions document/notes/043-tamcpp-spike-loader-cpu-bugs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# 043 · 线 A A1 点灯 spike——C++ HAL 样例跑通 + 两处 loader/CPU bug

> 日期: 2026-07-08
> 分支: `feat/run-tamcpp-samples`(worktree,从 `feat/deepen-gui-and-core` 97c90d3 分)
> 基线: ctest 367 绿
> 状态: A1 spike 通(PC13 toggle=1>0),修 elf_loader + cortex_m3 两 bug,ctest 368 绿
> 后置: A2 串口(3_uart_logger TX→EventBus) / A3 样例卫生 / A4 跨仓库文档

## 背景

线 A 任务:让 micro-forge 加载并运行 `~/Tutorial_AwesomeModernCPP` 的 C++ HAL 样例,读者不买板子也能跑教程。A1 点灯 spike 是生死验证——确认 TAMCPP `1_led_control` 的 C++ elf 能在模拟器跑(GPIO PC13 toggle>0)。

最大未知:C++ 程序 main 前要 `__libc_init_array` 初始化全局对象,且 `HAL_Delay` 依赖 SysTick 中断递增 `uwTick`。micro-forge 的 elf_loader 之前只加载过 C 样例,这两条链路没验证过。TAMCPP `third_party/STM32F1` 是空目录,样例编译借 micro-forge 自带的 STM32CubeF1 HAL(不改 TAMCPP,跨仓库纪律)。

## 卡点 1:elf_loader .data 段漏 flash LMA 副本

**现象**:spike 跑出 toggle=0,PC 卡在 `HAL_Delay` 的 `while(HAL_GetTick()-start<wait)` 死循环,SysTick `ctrl=0/load=0/val=0`(从未被配置)。

**诊断**(实测,非猜):

| 时刻 | SystemCoreClock@SRAM | flash LMA@0x08000bc4 |
|---|---|---|
| elf 加载后、run 前 | 0x7A1200(8M,初值正确) | 0x0(副本缺失) |
| run 后 | 0x3D09000(64M,clock config 改的) | 0x0 |

elf_loader 按 `p_vaddr` 加载 .data(初值到 SRAM=8M ✅),但 flash LMA(`p_paddr`=0x08000bc4)没加载。裸机 startup 的 `CopyDataInit` 从 `_sidata`(=p_paddr)复制 .data 到 SRAM——读到 0,把 SRAM 初值覆盖成 0。SystemCoreClock=0 → `HAL_InitTick` 的 `udiv 0/1000=0` → `SYSTICK_Config(0)` 走 ERROR(`ticks-1=0xFFFFFFFF` 越界)不写 SysTick → HAL_Delay 死循环。

Reset_Handler 反汇编铁证:`CopyDataInit` 从 `r2=0x08000bc4`(_sidata)读 → 写 `r0=0x20000000`(_sdata),循环 3 次(12 字节 .data)。

**为什么 hal_blink E2E 没暴露**:它用 busy-loop `delay_ms`(`volatile count--`),不碰 SysTick/uwTick。C++ HAL 样例用 `HAL_Delay` 才首次踩中——正说明 A1 spike 的价值。

**修法(B 法,按 p_paddr 加载)**:PT_LOAD 段加载到 `p_paddr`(当 `p_paddr!=0` 且 `!=p_vaddr` 时),否则 `p_vaddr`。初值落 flash LMA,SRAM 留给 CopyDataInit 复制——与真实烧录器行为一致(JTAG/SWD 把 .data 烧到 flash LMA,上电 startup 复制)。.text 段 `p_vaddr==p_paddr` 无变化。

**为何不双加载(A 法)**:A 法把 .data 预填 SRAM(方便但不真实——真实 SRAM 上电是 0),且会掩盖"startup 必须调 CopyDataInit"的真实要求(用户写忘 CopyDataInit 的 startup,真硬件坏但模拟器能跑=模拟器撒谎)。B 法让模拟器行为=真硬件,对教育用途(读者学嵌入式)重要。前提已验证:hal_blink(startup.c)、TAMCPP(startup.s)都调 CopyDataInit。

## 卡点 2:SysTick 线程优先级被截断

elf_loader 修好后,SystemCoreClock 经 CopyDataInit 正确复制(8M→HAL_Init 配 SysTick→clock config 改 64M,SysTick `ctrl=0x10007 load=63999` 配上了),`uwTick` 仍=0。

**诊断**:SysTick 在 tick(COUNTFLAG=1,实际到 0 了 312 次),irq_cb 调了 312 次,`pending_sys_tick_` 设了 312 次,但 SysTick_Handler 没进(uwTick=0)。

`check_and_handle_interrupt` 的优先级比较:`active_preempt = preempt_priority(0xFF)`(线程模式),4 位截断成 0xF;SysTick 优先级 0xF0(HAL 设 TickPriority=15)→ `preempt_priority(0xF0)=0xF`。`sp(0xF) < active_preempt(0xF)` 为 **false** → 不抢占。

**违反 ARMv7-M**:线程模式优先级最低,应被**任何** exception 抢占(即使最低优先级)。`preempt_priority(0xFF)` 截成 0xF 后与最低 exception 相等,挡住 SysTick。与 prigroup 无关(0xFF 和 0xF0 高位相同)。

**修法**:线程模式 `active_preempt` 用 0xFF(不截断),任何 exception preempt(0..0xF)均 < 0xFF → 抢占。handler 模式 nesting 不变(`preempt_priority(current_priority_)`),NVIC E2E 不破坏(更高优先级中断仍 < 0xFF)。

**为何 Timer/USART/EXTI 没踩中**:它们优先级更高(<0xF0),preempt < 0xF,现状下 `sp < 0xF` 成立能抢占。SysTick 默认最低优先级 0xF0 才踩中——`examples/systick/runner` 不进 ctest(只 add_executable 无 add_test),所以这条链路从未被 ctest 验证。

## 验证

- `test_elf_loader`:加 `DataSegmentLoadedToFlashLmaWhenVaddrDiffers`(.data 初值落 flash LMA,SRAM 保持 0)。全量 ctest **368/368 绿**(含新测试 + NVIC E2E 不破坏)。
- A1 spike:重跑 `examples/tamcpp_blink/runner`,`GPIO PC13 toggled 1 times`(toggle=1>0)。uwTick=0x4E3=1259(>1000,第一个 HAL_Delay(500) 返回 + led.on() + 第二个 HAL_Delay(500) + led.off())。

## 陷阱小结

- **busy-loop 掩盖 bug**:hal_blink 用 `delay_ms` 不碰 SysTick,掩盖了 .data LMA + SysTick 优先级两个 bug。spike 必须用真实 HAL_Delay 链路才暴露。
- **COUNTFLAG 不代表次数**:Ctrl bit16 COUNTFLAG 置位不清(读 VAL/CTRL 才清),只说明"到过 0",不说明次数。诊断时一度误以为只到 0 一次,实际 312 次。
- **TAMCPP third_party 空**:`~/Tutorial_AwesomeModernCPP/third_party/STM32F1` 是空目录,样例编译借 micro-forge 的 STM32CubeF1(A0 用 `/tmp/tamcpp-build/build.sh` 手动编出 elf)。
- **worktree submodule**:git worktree 的 `git submodule update --init` 报 "Unable to find current revision"(worktree gitdir 链接问题),symlink 主仓 STM32CubeF1 临时跑 ctest(commit 不含 symlink,只 add 具体文件)。

## 后置

- **A2 串口**:加载 TAMCPP `3_uart_logger`,TX 经 EventBus(UartByte)→ GUI serial 面板可见;进 ctest E2E(参考 `test/CMakeLists.txt:175` hal_uart 模式)。tamcpp_blink 的 firmware build 正规化(借 STM32CubeF1 + TAMCPP 源码路径)。
- **A3 样例卫生**:`examples/hal_blink` `examples/hal_uart` 的 `CUBE_BASE` 参数化 + README + 重编验证仍通。
- **A4 跨仓库文档**:TAMCPP `documents/vol8-domains/embedded/` 补"用 micro-forge 无硬件跑教程"(跨仓库改动先报用户确认)。
42 changes: 42 additions & 0 deletions document/notes/044-tamcpp-samples-spike.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 044 — TAMCPP C++ HAL 样例 spike(线 A)

> 让 micro-forge 加载运行 TAMCPP(`~/Tutorial_AwesomeModernCPP/code/stm32f1-tutorials`)的 C++ HAL 样例(1_led/2_button/3_uart)。TAMCPP 是 C++(main.cpp + device/ + system/),micro-forge 之前只加载过 C 样例(hal_blink/hal_uart)。

## 已修 6 个模拟器 bug

1. **elf_loader .data LMA**(043):按 `p_vaddr` 加载 .data 到 SRAM,漏了 flash LMA 副本;裸机 startup `CopyDataInit` 从 `_sidata`(flash LMA)复制,读到 0 覆盖 SRAM → `SystemCoreClock=0` → SysTick 不配 → `HAL_Delay` 死循环。修:PT_LOAD 若 `p_paddr≠p_vaddr`,额外写副本到 `p_paddr`(flash LMA)。hal_blink 因用 busy-loop `delay_ms`(不碰 SysTick)从未暴露。
2. **cortex_m3 SysTick 线程优先级**(043):线程模式 `active_preempt` 用 0xFF,被 `preempt_priority`(0xF)截断,SysTick(0xF0)无法抢占线程 → `uwTick` 不涨。修:线程用 0xFF(不截断)。
3. **nvic IPR 字节写**(本批):`HAL_NVIC_SetPriority` 用 `strb` 写 `IP[IRQn]`,原 NVIC write 只支持 Word 返回 `Unaligned` → `DataAccessFault`。修:IPR 分支提前 + 按 width 更新字节,ISER/ICER 等仍 Word。TAMCPP 3_uart `enable_interrupt` 因此通。
4. **IT mask 解码**:`HAL_GPIO_ReadPin` 的 `ite ne` 被当成两个 THEN 槽,导致 `movne`/`moveq` 都执行、返回值恒为 0。修正 slot 对应的 mask bit 后,高低电平可正确返回 1/0。
5. **异常未保存 ITSTATE**:模拟器把 IT 条件保存在独立 `it_conditions_` 中,SysTick 若打断 `HAL_GPIO_ReadPin` 的 `ITE` 块,handler 会消费线程的条件,异常返回也不恢复。表现为 IDR 始终为 1 但 `ReadPin` 偶发返回 0,每次假按下都重置 release 去抖。修:异常 entry 按嵌套层级保存并清空 IT 状态,return 恢复;新增精确回归测试。
6. **t16_pop `pop {pc}` SP writeback 顺序**(本批):`POP {pc}` 弹出 EXC_RETURN 时,`write_pc` 先触发 `interrupt_return`(从 SP 弹 8 字异常帧并恢复 SP),但 t16_pop 随后用局部 `sp`(尚未 +4)writeback,**覆盖了 interrupt_return 刚恢复的 SP** → 每次 exception 净漏 0x20。3_uart_logger 的 USART1 RXNE 中断每收一字节 sp 偏 0x20 → `line_buf`(@sp+16)地址漂移 → `strb` 写错位置 → `handle_command` 的 `cmd=="LED ON"` 比较恒失败 → 回 `ERR`。修:`pop {pc}` 先 writeback `sp+4`、再 `write_pc`,让 interrupt_return 从正确位置弹帧。SysTick handler 不经 `pop {pc}`(走 `bx lr`),故此前一直平衡、未被本题暴露。

## spike 状态

- **1_led_control 通**:PC13 toggle=1(A1 生死验证,C++ elf 能在模拟器跑)。
- **2_button_control 通**:PA0 idle=1 → press=0 → release=1;Pressed 写 PC13 BSRR reset (`0x20000000`),Released 在 20ms 后写 set (`0x2000`),runner 验证 `on=1 off=1 ... [PASS]`。
- **3_uart_logger 通**:TX `UART Logger Ready!\r\n`(USART1 TX 链路与 hal_uart E2E 同);RX 注入 `LED ON\r\n` → RXNE 中断 → ring → main `handle_command` 回 `OK: LED ON\r\n`(全链路通)。

## firmware build 正规化

- `examples/tamcpp_blink/firmware/build_sample.sh`:参数化编 TAMCPP 1/2/3 C++ elf(借 micro-forge 的 STM32CubeF1 HAL —— TAMCPP `third_party/STM32F1` 是空目录,不改 TAMCPP)。gcc 编 .c、g++ 编 .cpp(`-std=c++23 -fno-exceptions -fno-rtti`)、g++ 链接(libstdc++ init)。
- `examples/tamcpp_blink/firmware/CMakeLists.txt`:参数化 `foreach(led/button/uart)`,`TAMCPP_ROOT` cache 变量(默认 `$ENV{TAMCPP_ROOT}` 或 `$HOME/Tutorial_AwesomeModernCPP/...`)+ STM32CubeF1 存在性门禁,绝不进 git。
- 链接脚本用 TAMCPP 的 `STM32F103C8TX_FLASH.ld`(带 `.init_array`,C++ 全局构造必需;不能用 micro-forge 的 `STM32F103_HAL.ld`——它无 `.init_array`)。

## 陷阱

- **TAMCPP third_party/STM32F1 空目录**:借 micro-forge 的 STM32CubeF1(不改 TAMCPP,跨仓库纪律)。
- **-std=c++23**:`uart_driver.hpp` 用 `std::expected`(C++23),build_sample.sh 必须加(1/2 不用 std::expected,c++17 够,但统一加无害)。
- **worktree STM32CubeF1 submodule 未 init**:hal_blink/hal_uart/E2E 被 skip(ctest 不破);tamcpp firmware 需 symlink 主仓的 STM32CubeF1。
- **C++ 启动**:startup `__libc_init_array` 调 `.init_array`(Meyers singleton `ClockConfig::instance()`);1_led 已验证通。

## 验证

- ctest 全绿,新增 `ExceptionPreservesInterruptedItBlock` 防止 ITSTATE 跨异常回归。
- led spike:PC13 toggle=1。
- button spike:`on=1 off=1 PA0=1 PC13=1 state=0 [PASS]`。
- uart spike:TX `UART Logger Ready!` + RX `OK: LED ON`(`TX banner=1 RX cmd=1`)。

## 状态(线 A 完成)

`E2E.Tamcpp{Led,Button,Uart}` 三 TEST 进 ctest(ctest 372 绿)。线 A 三样例全通,6 个模拟器 bug 修完:elf_loader `.data` LMA / SysTick 线程优先级 / nvic IPR 字节写 / IT mask 解码 / ITSTATE 异常保存 / `t16_pop` pop{pc} SP writeback。分支 `feat/run-tamcpp-samples`(本批 commit 本地未 push)。
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ add_subdirectory(systick)
add_subdirectory(hal_blink)
add_subdirectory(hal_uart)
add_subdirectory(hook_demo)
add_subdirectory(tamcpp_blink)
54 changes: 54 additions & 0 deletions examples/tamcpp_blink/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# TAMCPP C++ HAL 样例(1_led_control / 2_button_control / 3_uart_logger)。
# elf 由 firmware/ 参数化编译(借 micro-forge 的 STM32CubeF1 HAL,不改 TAMCPP 仓库)。
# runner 加载 elf 验证:led 看 PC13 toggle、button 注入 PA0 看 toggle、uart 看 TX 输出。

# TAMCPP_ROOT: stm32f1-tutorials 根,绝不硬编码进 git(cache 变量 + 环境变量 + 默认)。
if(NOT TAMCPP_ROOT)
if(DEFINED ENV{TAMCPP_ROOT})
set(TAMCPP_ROOT "$ENV{TAMCPP_ROOT}" CACHE PATH "TAMCPP stm32f1-tutorials root")
else()
set(TAMCPP_ROOT "$ENV{HOME}/Tutorial_AwesomeModernCPP/code/stm32f1-tutorials"
CACHE PATH "TAMCPP stm32f1-tutorials root")
endif()
endif()
if(NOT EXISTS "${TAMCPP_ROOT}/1_led_control/main.cpp")
message(STATUS "TAMCPP_ROOT not found (${TAMCPP_ROOT}), skipping tamcpp samples")
return()
endif()
message(STATUS "TAMCPP_ROOT = ${TAMCPP_ROOT}")

if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../third_party/STM32CubeF1/Drivers")
message(STATUS "STM32CubeF1 submodule not found, skipping tamcpp samples")
return()
endif()

add_subdirectory(firmware)

# ── runner: 每样例一个(elf 由 firmware target 编出,POST_BUILD copy 到 runner 目录)─
add_executable(tamcpp_blink_runner runner.cpp)
target_link_libraries(tamcpp_blink_runner PRIVATE micro_forge)
add_dependencies(tamcpp_blink_runner tamcpp_led_firmware)
add_custom_command(TARGET tamcpp_blink_runner POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_BINARY_DIR}/firmware/led.elf
${CMAKE_CURRENT_BINARY_DIR}/tamcpp_led.elf)

if(TARGET tamcpp_button_firmware)
add_executable(tamcpp_button_runner runner_button.cpp)
target_link_libraries(tamcpp_button_runner PRIVATE micro_forge)
add_dependencies(tamcpp_button_runner tamcpp_button_firmware)
add_custom_command(TARGET tamcpp_button_runner POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_BINARY_DIR}/firmware/button.elf
${CMAKE_CURRENT_BINARY_DIR}/tamcpp_button.elf)
endif()

if(TARGET tamcpp_uart_firmware)
add_executable(tamcpp_uart_runner runner_uart.cpp)
target_link_libraries(tamcpp_uart_runner PRIVATE micro_forge)
add_dependencies(tamcpp_uart_runner tamcpp_uart_firmware)
add_custom_command(TARGET tamcpp_uart_runner POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_BINARY_DIR}/firmware/uart.elf
${CMAKE_CURRENT_BINARY_DIR}/tamcpp_uart.elf)
endif()
41 changes: 41 additions & 0 deletions examples/tamcpp_blink/firmware/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 参数化编译 TAMCPP 1/2/3 样例的 C++ elf,借 micro-forge 的 STM32CubeF1 HAL。
# 不复制 TAMCPP 源码(绝对路径引用),用 build_sample.sh 编译。
# TAMCPP_ROOT 由父 CMakeLists 解析(cache 变量,不进 git)。

find_program(ARM_GCC arm-none-eabi-gcc)
find_program(ARM_GXX arm-none-eabi-g++)
find_program(ARM_OBJCOPY arm-none-eabi-objcopy)
if(NOT ARM_GCC OR NOT ARM_GXX OR NOT ARM_OBJCOPY)
message(STATUS "arm-none-eabi toolchain incomplete, skipping TAMCPP firmware")
return()
endif()

set(CUBE_BASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../third_party/STM32CubeF1")
set(OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}")

# 编一个样例:short(产物名/ target 后缀)、dir(TAMCPP 子目录)、extra(额外 .cpp)
function(tamcpp_build_sample short dir)
set(extra ${ARGN})
set(sample ${TAMCPP_ROOT}/${dir})
set(ld ${sample}/STM32F103C8TX_FLASH.ld)
set(deps ${CMAKE_CURRENT_SOURCE_DIR}/build_sample.sh ${ld}
${sample}/main.cpp ${sample}/system/clock.cpp
${sample}/system/hal_mock.c ${sample}/system/syscall.c)
foreach(e ${extra})
list(APPEND deps ${sample}/${e})
endforeach()
add_custom_command(
OUTPUT ${OUT_DIR}/${short}.elf ${OUT_DIR}/${short}.bin
COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/build_sample.sh
${short} ${dir} ${TAMCPP_ROOT} ${CUBE_BASE} ${OUT_DIR} ${extra}
DEPENDS ${deps}
COMMENT "[tamcpp/${short}] building ${short}.elf (STM32CubeF1 + TAMCPP C++)"
VERBATIM
)
add_custom_target(tamcpp_${short}_firmware ALL
DEPENDS ${OUT_DIR}/${short}.elf ${OUT_DIR}/${short}.bin)
endfunction()

tamcpp_build_sample(led 1_led_control)
tamcpp_build_sample(button 2_button_control)
tamcpp_build_sample(uart 3_uart_logger system/printf_redirect.cpp system/uart_irq.cpp)
44 changes: 44 additions & 0 deletions examples/tamcpp_blink/firmware/build_sample.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# 编译单个 TAMCPP C++ 样例 elf,借 micro-forge 的 STM32CubeF1 HAL(不改 TAMCPP 仓库)。
# 用法:build_sample.sh <short> <dir> <tamcpp_root> <cube_base> <out_dir> [extra_cpp...]
# extra_cpp: 相对样例根的额外 .cpp
# 3_uart_logger 需: system/printf_redirect.cpp system/uart_irq.cpp
set -euo pipefail
short=$1; dir=$2; tamcpp_root=$3; cube=$4; out=$5; shift 5
extra=("$@")

sample=$tamcpp_root/$dir
ld=$sample/STM32F103C8TX_FLASH.ld
cmsis=$cube/Drivers/CMSIS/Device/ST/STM32F1xx/Source/Templates
obj=$out/obj_$short
mkdir -p "$obj"

common=(-mcpu=cortex-m3 -mthumb -O2 -g -Wall -Wextra -Wno-missing-field-initializers
-ffunction-sections -fdata-sections -DUSE_HAL_DRIVER -DSTM32F103xB -DHSE_VALUE=8000000)
inc=(-I"$sample" -I"$sample/system" -I"$cube/Drivers/CMSIS/Include"
-I"$cube/Drivers/CMSIS/Device/ST/STM32F1xx/Include" -I"$cube/Drivers/STM32F1xx_HAL_Driver/Inc")

echo "--- [tamcpp/$short] C sources (HAL + system + syscall) ---"
for f in "$cube"/Drivers/STM32F1xx_HAL_Driver/Src/*.c "$cmsis/system_stm32f1xx.c" "$sample/system/hal_mock.c" "$sample/system/syscall.c"; do
[[ "$f" == *_template.c ]] && continue
arm-none-eabi-gcc "${common[@]}" "${inc[@]}" -c "$f" -o "$obj/$(basename "$f" .c).o"
done

echo "--- [tamcpp/$short] C++ sources (main + clock + extra) ---"
arm-none-eabi-g++ "${common[@]}" -std=c++23 -fno-exceptions -fno-rtti "${inc[@]}" -c "$sample/main.cpp" -o "$obj/main.o"
arm-none-eabi-g++ "${common[@]}" -std=c++23 -fno-exceptions -fno-rtti "${inc[@]}" -c "$sample/system/clock.cpp" -o "$obj/clock.o"
if [[ ${#extra[@]} -gt 0 ]]; then
for e in "${extra[@]}"; do
arm-none-eabi-g++ "${common[@]}" -std=c++23 -fno-exceptions -fno-rtti "${inc[@]}" -c "$sample/$e" -o "$obj/$(basename "$e" .cpp).o"
done
fi

echo "--- [tamcpp/$short] startup.s ---"
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -x assembler-with-cpp -c "$cmsis/gcc/startup_stm32f103xb.s" -o "$obj/startup.o"

echo "--- [tamcpp/$short] link (g++ for libstdc++ init) ---"
arm-none-eabi-g++ -mcpu=cortex-m3 -mthumb -T"$ld" -nostartfiles -specs=nano.specs -specs=nosys.specs \
-Wl,--gc-sections -Wl,-Map="$out/$short.map" -o "$out/$short.elf" "$obj"/*.o
arm-none-eabi-objcopy -O binary "$out/$short.elf" "$out/$short.bin"
arm-none-eabi-size --format=berkeley "$out/$short.elf"
echo "=== [tamcpp/$short] OK: $out/$short.elf ==="
Loading
Loading