Сборка bare-metal проекта в режиме Debug
This commit is contained in:
Dmitry Akimov 2026-03-06 16:14:37 +03:00
parent ce717ce9dd
commit 5ac85b90f5
11 changed files with 478 additions and 42 deletions

24
.clangd
View file

@ -23,9 +23,9 @@ Diagnostics:
--- ---
# Полностью отключаем clang-tidy для Core (генерированный STM32CubeMX код) # SDK NXP: сгенерированный/вендорный код — tidy полностью отключён
If: If:
PathMatch: "Core/.*\\.(c|h)" PathMatch: "sdk/.*\\.(c|h)"
Diagnostics: Diagnostics:
ClangTidy: ClangTidy:
Remove: Remove:
@ -33,7 +33,27 @@ Diagnostics:
UnusedIncludes: None UnusedIncludes: None
--- ---
# Сторонние библиотеки (SEGGER RTT и др.) — tidy полностью отключён
If:
PathMatch: "lib/.*\\.(c|h)"
Diagnostics:
ClangTidy:
Remove:
- "*"
UnusedIncludes: None
---
# BSP board: сгенерированный Config Tools код — tidy полностью отключён
If:
PathMatch: "bsp/board/(board|pin_mux|clock_config)\\.(c|h)"
Diagnostics:
ClangTidy:
Remove:
- "*"
UnusedIncludes: None
---
# Глобальные настройки
Index: Index:
Background: Build Background: Build
Diagnostics: Diagnostics:

View file

@ -1,11 +1,11 @@
{ {
"name": "STM32 Dev", "name": "MIMXRT1052 TFT Project",
"build": { "build": {
"dockerfile": "Dockerfile" "dockerfile": "Dockerfile"
}, },
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind", "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
"workspaceFolder": "/workspace", "workspaceFolder": "/workspace",
// "postCreateCommand": "cmake --preset host-debug && cmake --preset Debug && cp build/Debug/compile_commands.json /workspace/compile_commands.json", "postCreateCommand": "cmake --preset host-debug && cmake --preset Debug",
"customizations": { "customizations": {
"vscode": { "vscode": {
"extensions": [ "extensions": [
@ -42,7 +42,6 @@
"*.h": "c", "*.h": "c",
"*.c": "c" "*.c": "c"
}, },
"stm32.chip": "STM32F407VE",
// clangd // clangd
"clangd.path": "clangd", "clangd.path": "clangd",
"clangd.arguments": [ "clangd.arguments": [

1
.gitignore vendored
View file

@ -60,7 +60,6 @@ Testing/
# Файлы настройки среды разработки # Файлы настройки среды разработки
.project .project
.vscode/
compile_commands.json compile_commands.json
.cache/ .cache/
.DS_Store .DS_Store

38
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,38 @@
{
"version": "0.2.0",
"configurations": [
{
"preLaunchTask": "CopyExecutableDebug",
"type": "probe-rs-debug",
"request": "launch",
"name": "Local STM32F4 via probe-rs",
"cwd": "${workspaceFolder}",
"connectUnderReset": false,
"server": "127.0.0.1:50000",
"chip": "STM32F407VE",
"speed": 4000,
"wireProtocol": "Swd",
"flashingConfig": {
"flashingEnabled": true,
"haltAfterReset": true,
"formatOptions": {}
},
"coreConfigs": [
{
"coreIndex": 0,
"programBinary": "/tmp/firmware.elf",
"svdFile": "${workspaceFolder}/STM32F407.svd",
"rttEnabled": true,
"rttChannelFormats": [
{
"channelNumber": 0,
"dataFormat": "String",
"showTimestamps": true
}
]
}
],
"consoleLogLevel": "Console"
}
]
}

213
.vscode/tasks.json vendored Normal file
View file

@ -0,0 +1,213 @@
{
"version": "2.0.0",
"tasks": [
// Очистка директории с артефактами сборки
{
"label": "Clean",
"group": "build",
"type": "shell",
"command": "rm -rf build*",
"windows": {
"command": "rm build* -Recurse -Force"
},
"options": {
"statusbar": {
"color": "#ff8b8b",
"label": "$(clear-all) CleanBuildDir",
"detail": "Clean ALL"
}
}
},
// Сборка и запуск тестов на хосте в режиме Debug
{
"label": "Configure Host Debug tests",
"group": "build",
"hide": true,
"type": "shell",
"command": "cmake",
"args": [
"--preset",
"host-debug"
],
"options": {
"cwd": "${workspaceFolder}"
}
},
{
"label": "Build Host Debug tests",
"group": "build",
"hide": true,
"type": "shell",
"command": "cmake",
"args": [
"--build",
"--preset",
"host-debug-build"
],
"options": {
"cwd": "${workspaceFolder}"
},
"dependsOn": "Configure Host Debug tests"
},
{
"label": "🧪 Host Build & Test All",
"type": "shell",
"command": "ctest",
"args": [
"--preset",
"host-debug-test"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#c800ff",
"label": "$(test-view-icon) Host Test Debug",
"detail": "Perform all the host tests"
}
},
"group": "test",
"dependsOrder": "sequence",
"dependsOn": [
"Build Host Debug tests"
]
},
// Сборка и запуск тестов на хосте в режиме Release
{
"label": "Configure Host Release tests",
"group": "build",
"hide": true,
"type": "shell",
"command": "cmake",
"args": [
"--preset",
"host-release"
],
"options": {
"cwd": "${workspaceFolder}"
},
},
{
"label": "Build Host Release tests",
"group": "build",
"hide": true,
"type": "shell",
"command": "cmake",
"args": [
"--build",
"--preset",
"host-release-build"
],
"dependsOn": "Configure Host Release tests"
},
{
"label": "🧪 Host Release Tests",
"type": "shell",
"command": "ctest",
"args": [
"--preset",
"host-release-test"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#c800ff",
"label": "$(test-view-icon) Host Test Release",
"detail": "Perform all the host tests"
}
},
"group": "test",
"dependsOrder": "sequence",
"dependsOn": [
"Build Host Release tests"
],
},
// Подготовка сборки в контейнере с пресетом Debug
{
"label": "Docker: Configure (Debug)",
"type": "shell",
"hide": true,
"command": "cmake",
"args": [
"--preset",
"Debug"
],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
// Сборка проекта в контейнере Debug
{
"label": "Docker: Build (Debug)",
"type": "shell",
"hide": false,
"command": "cmake",
"args": [
"--build",
"--preset",
"Debug"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#ff8bd4",
"label": "$(debug-console-view-icon) Target: Build (Debug)",
"detail": "do Debug build"
}
},
"group": "build",
"dependsOn": "Docker: Configure (Debug)",
},
// Подготовка сборки в контейнере с пресетом Release
{
"label": "Docker: Configure (Release)",
"type": "shell",
"hide": true,
"command": "cmake",
"args": [
"--preset",
"Release"
],
"options": {
"cwd": "${workspaceFolder}"
},
"group": "build",
"problemMatcher": []
},
// Сборка проекта в контейнере Release
{
"label": "Docker: Build (Release)",
"type": "shell",
"hide": false,
"command": "cmake",
"args": [
"--build",
"--preset",
"Release"
],
"options": {
"cwd": "${workspaceFolder}",
"statusbar": {
"color": "#03b3ff",
"label": "$(pass-filled) Target: Build (Release)",
"detail": "do Release build"
}
},
"group": "build",
"dependsOn": "Docker: Configure (Release)",
},
],
"inputs": [
{
"id": "buildType",
"type": "pickString",
"description": "Тип сборки",
"options": [
"Debug",
"Release",
],
"default": "Debug"
}
],
}

175
bsp/BOOT_FLAGS.md Normal file
View file

@ -0,0 +1,175 @@
# Boot-дефайны IMXRT1052
## Процесс загрузки
При старте BootROM читает QSPI Flash через FlexSPI и ищет специальные структуры
по фиксированным смещениям от начала Flash (`0x60000000`):
```bash
0x60000000 FDCB — FlexSPI NOR Configuration Block (512 bytes)
0x60000400 IVT — Image Vector Table
0x60000420 Boot Data
0x60000430 DCD — Device Configuration Data (опционально)
```
После разбора структур BootROM передаёт управление на точку входа прошивки.
---
### `XIP_EXTERNAL_FLASH=1`
Код исполняется напрямую из QSPI Flash через XIP (Execute-In-Place) без
копирования в RAM. FlexSPI отображает Flash в адресное пространство начиная с
`0x60000000`. Стартап и SDK знают что копирование кода из Flash в RAM не
требуется.
### `XIP_BOOT_HEADER_ENABLE=1`
Включает генерацию FDCB и IVT в начале Flash-образа. Без этих структур BootROM
не сможет найти прошивку и передать ей управление — устройство не загрузится.
### `XIP_BOOT_HEADER_DCD_ENABLE=1`
Включает DCD внутри boot header. DCD — список команд для BootROM (запись
регистров, ожидание флагов), которые выполняются **до** передачи управления
прошивке. Используется для ранней инициализации SDRAM, чтобы линкер мог
размещать данные в SDRAM с самого старта.
> Если SDRAM инициализируется вручную в `main()` — DCD можно отключить,
> убрав этот дефайн.
### `SKIP_SYSCLK_INIT`
Запрещает `SystemInit()` (вызывается из стартапа до `main()`) настраивать
системные часы. Конфигурация тактирования полностью делегируется
`BOARD_BootClockRUN()` из `clock_config.c`. Без этого флага часы
инициализировались бы дважды, что может привести к нестабильной работе на
этапе старта.
---
## Сценарии размещения кода
### Справка: внутренняя RAM IMXRT1052
IMXRT1052 имеет три региона внутренней RAM:
| Регион | Адрес | Размер | Назначение |
|--------|--------------|--------|------------------------------------------------------|
| ITCM | `0x00000000` | 512 KB | Tightly Coupled Instruction Memory — код, ISR |
| DTCM | `0x20000000` | 512 KB | Tightly Coupled Data Memory — стек, критичные данные |
| OCRAM | `0x20200000` | 512 KB | On-Chip RAM — общего назначения, доступна DMA |
**ITCM** — лучший выбор для исполнения кода из RAM. Подключена напрямую к ядру
по выделенной шине, работает без wait states и без кэша. Даёт максимальную
детерминированность — критично для ISR и time-critical кода.
OCRAM подключена через системную шину и кэш — выше латентность, но доступна
периферии и DMA.
---
### Сценарий 1: хранение и исполнение из Flash (XIP)
Код хранится в QSPI Flash и исполняется напрямую оттуда через FlexSPI.
Копирования не происходит. Простейший вариант — минимум настроек.
**Применение:** большинство приложений где производительность не критична,
быстрый старт разработки.
```cmake
target_compile_definitions(bsp_board PUBLIC
XIP_EXTERNAL_FLASH=1 # исполнение прямо из Flash
XIP_BOOT_HEADER_ENABLE=1 # FDCB + IVT в начале образа
XIP_BOOT_HEADER_DCD_ENABLE=1 # DCD для инициализации SDRAM если нужна
SKIP_SYSCLK_INIT
)
```
Линкерный скрипт: `MIMXRT1052xxxxx_flexspi_nor.ld`
```bash
Flash (XIP): 0x60000000 — код и константы
DTCM: 0x20000000 — стек, .data, .bss
```
**Ограничения:** FlexSPI шина делится между исполнением кода и доступом к данным
Flash. При интенсивной работе с Flash (запись, чтение данных) производительность
кода падает.
---
### Сценарий 2: хранение во Flash, исполнение из внутренней RAM (ITCM)
Код хранится в QSPI Flash, при старте стартап копирует секцию `.text` в ITCM.
BootROM загружает стартап из Flash, стартап копирует остальной код и передаёт
управление.
**Применение:** ISR с жёсткими временными требованиями, алгоритмы управления,
участки кода где нужна детерминированная латентность.
```cmake
target_compile_definitions(bsp_board PUBLIC
# XIP_EXTERNAL_FLASH не определяем — код копируется в ITCM
XIP_BOOT_HEADER_ENABLE=1 # FDCB + IVT нужны — BootROM всё равно грузит из Flash
XIP_BOOT_HEADER_DCD_ENABLE=1 # DCD нужен если SDRAM используется под данные
SKIP_SYSCLK_INIT
)
```
Линкерный скрипт: `MIMXRT1052xxxxx_flexspi_nor_txt_itcm.ld` (или кастомный)
```bash
Flash: 0x60000000 — хранение образа
ITCM (LMA→VMA): 0x00000000 — исполнение кода
DTCM: 0x20000000 — стек, .data, .bss
```
**Ограничения:** ITCM всего 512 KB — весь исполняемый код должен в него
поместиться. Время старта увеличивается на время копирования из Flash.
---
### Сценарий 3: хранение во Flash, исполнение из SDRAM
Код хранится в QSPI Flash, при старте копируется в SDRAM (32 MB, `0x80000000`).
SDRAM **должна быть инициализирована до копирования** — через DCD BootROM.
**Применение:** большие приложения которые не помещаются во внутреннюю RAM и
слишком медленно работают из Flash — например, UI с графикой, обработка
изображений.
```cmake
target_compile_definitions(bsp_board PUBLIC
# XIP_EXTERNAL_FLASH не определяем — код копируется в SDRAM
XIP_BOOT_HEADER_ENABLE=1
XIP_BOOT_HEADER_DCD_ENABLE=1 # обязательно — DCD инициализирует SDRAM до старта
SKIP_SYSCLK_INIT
)
```
Линкерный скрипт: кастомный с регионом SDRAM
```bash
Flash: 0x60000000 — хранение образа
SDRAM (LMA→VMA): 0x80000000 — исполнение кода (32 MB)
DTCM: 0x20000000 — стек, критичные данные
ITCM: 0x00000000 — ISR и time-critical функции
```
Для ISR и критичного кода используй атрибут `__attribute__((section(".itcm")))`
чтобы разместить их в ITCM отдельно от основного кода в SDRAM.
**Ограничения:** SDRAM работает через SEMC с несколькими wait states — медленнее
ITCM. DCD усложняет конфигурацию — при смене частоты тактирования SEMC параметры
DCD нужно пересчитывать.
---
### Сводная таблица
| Сценарий | `XIP_EXTERNAL_FLASH` | `XIP_BOOT_HEADER_ENABLE` | `XIP_BOOT_HEADER_DCD_ENABLE` | `SKIP_SYSCLK_INIT` |
|---------------------|----------------------|--------------------------|------------------------------|--------------------|
| XIP из Flash | `1` | `1` | `1` (если SDRAM нужна) | да |
| Копирование в ITCM | не определять | `1` | `1` (если SDRAM нужна) | да |
| Копирование в SDRAM | не определять | `1` | `1` (обязательно) | да |

View file

@ -5,9 +5,9 @@ endif()
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# bsp_board — стартап, сгенерированные файлы Config Tools, инициализация платы # bsp_board — стартап, сгенерированные файлы Config Tools, инициализация платы
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
add_library(
bsp_board STATIC board/board.c board/pin_mux.c board/clock_config.c add_library(bsp_board STATIC board/board.c board/pin_mux.c board/clock_config.c
board/syscalls.c board/startup/startup_MIMXRT1052.S) board/startup/startup_MIMXRT1052.S)
target_include_directories(bsp_board PUBLIC board) target_include_directories(bsp_board PUBLIC board)

View file

@ -76,8 +76,17 @@ set(CMAKE_SIZE
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Флаги процессора MIMXRT1052 (Cortex-M7, FPv5-D16, hard-float ABI) # Флаги процессора MIMXRT1052 (Cortex-M7, FPv5-D16, hard-float ABI)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# -mcpu=cortex-m7 целевой процессор, включает оптимизации под эту
# архитектуру -mthumb набор инструкций Thumb-2 (16/32-bit),
# меньший код при той же производительности -mfpu=fpv5-d16 FPU: FPv5 с
# 16 парами 64-bit регистров, соответствует аппаратному FPU IMXRT1052
# -mfloat-abi=hard float/double передаются через FPU-регистры (быстрее чем
# softfp) -ffunction-sections каждая функция в отдельной секции, позволяет
# --gc-sections удалять неиспользуемые -fdata-sections аналогично, но для
# переменных -ffreestanding не предполагать наличия ОС, main() не точка
# входа, встроенные замены stdlib отключены
set(CPU_FLAGS set(CPU_FLAGS
"-mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard -ffunction-sections -fdata-sections" "-mcpu=cortex-m7 -mthumb -mfpu=fpv5-d16 -mfloat-abi=hard -ffunction-sections -fdata-sections -ffreestanding"
) )
set(CMAKE_C_FLAGS set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} ${CPU_FLAGS}" "${CMAKE_C_FLAGS} ${CPU_FLAGS}"
@ -115,16 +124,9 @@ set(CMAKE_ASM_FLAGS_RELEASE
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# C runtime библиотека newlib-nano (меньше размер, подходит для embedded) # C runtime библиотека newlib-nano (меньше размер, подходит для embedded)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# nano.specs выбирает компактную версию newlib-nano (меньший размер printf, # --specs=nano.specs использовать newlib-nano: облегчённая stdlib,
# malloc и т.д.). Но не предоставляет реализацию системных вызовов. # меньший размер printf/malloc -Wl,--no-warn-rwx-segments подавить
# предупреждение о RWX сегментах (ITCM/DTCM нормальны для MCU)
# nosys.specs предоставляет именно заглушки syscalls. Без него newlib ожидает
# что ты сам реализуешь _sbrk, _write, _exit и т.д. либо через semihosting,
# либо вручную.
# --gc-sections убирает неиспользуемые секции кода и данных. Работает в паре с
# -ffunction-sections -fdata-sections у компилятора (каждая функция в отдельной
# секции линкер выбрасывает ненужные).
set(CMAKE_EXE_LINKER_FLAGS_INIT "--specs=nano.specs -Wl,--no-warn-rwx-segments") set(CMAKE_EXE_LINKER_FLAGS_INIT "--specs=nano.specs -Wl,--no-warn-rwx-segments")
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------

View file

@ -1,7 +1,10 @@
# firmware/test/CMakeLists.txt Тестовая прошивка — входной контроль платы на # firmware/test/CMakeLists.txt Тестовая прошивка — входной контроль платы на
# производстве # производстве
add_executable(firmware_test main.c) add_executable(firmware_test)
target_sources(firmware_test PRIVATE main.c
${PROJECT_SOURCE_DIR}/bsp/board/syscalls.c)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Зависимости — только то что нужно для входного контроля bsp_board транзитивно # Зависимости — только то что нужно для входного контроля bsp_board транзитивно
@ -23,16 +26,15 @@ target_link_libraries(
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Linker script # Linker script
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# --gc-sections — убирает неиспользуемые секции кода и данных. Работает в паре с # --gc-sections — удалять неиспользуемые секции (работает с
# -ffunction-sections -fdata-sections у компилятора (каждая функция в отдельной # -ffunction/data-sections) --print-memory-usage — выводить таблицу
# секции — линкер выбрасывает ненужные). # использования Flash/RAM после линковки -Map — генерировать
# map-файл для анализа размещения символов -T — линкерный
# скрипт с описанием карты памяти IMXRT1052
target_link_options( target_link_options(
firmware_test firmware_test PRIVATE -Wl,--gc-sections -Wl,--print-memory-usage
PRIVATE
-T${PROJECT_SOURCE_DIR}/cmake/linker/MIMXRT1052xxxxx_flexspi_nor.ld
-Wl,--gc-sections
-Wl,-Map=${CMAKE_BINARY_DIR}/firmware_test.map -Wl,-Map=${CMAKE_BINARY_DIR}/firmware_test.map
-Wl,--print-memory-usage) -T${PROJECT_SOURCE_DIR}/cmake/linker/MIMXRT1052xxxxx_flexspi_nor.ld)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Post-build: генерация .bin для прошивки через blhost # Post-build: генерация .bin для прошивки через blhost

View file

@ -6,8 +6,8 @@
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*/ */
#include "SEGGER_RTT.h"
#include "board.h" #include "board.h"
/******************************************************************************* /*******************************************************************************
* Definitions * Definitions
******************************************************************************/ ******************************************************************************/

View file

@ -7,18 +7,6 @@ set(RTT_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../RTT/RTT)
set(RTT_SYSCALLS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../RTT/Syscalls) set(RTT_SYSCALLS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../RTT/Syscalls)
set(RTT_CONFIG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../RTT/Config) set(RTT_CONFIG_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../RTT/Config)
# Проверяем наличие субмодуля
if(NOT EXISTS "${RTT_SRC_DIR}/SEGGER_RTT.c")
message(
FATAL_ERROR
"
❌ SEGGER_RTT submodule не инициализирован!
Запустите в корне проекта:
git submodule update --init ThirdParty/SEGGER/RTT
")
endif()
# Создаём статическую библиотеку # Создаём статическую библиотеку
add_library(SeggerRTT STATIC) add_library(SeggerRTT STATIC)