diff --git a/.cproject b/.cproject index 34f11167..2589a7ba 100644 --- a/.cproject +++ b/.cproject @@ -403,4 +403,4 @@ - \ No newline at end of file + diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index dafda299..00000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM ubuntu:24.04 - -# Install git, git-lfs, colorama and build tools -RUN echo "deb http://security.ubuntu.com/ubuntu focal-security main universe" > /etc/apt/sources.list.d/ubuntu-focal-sources.list && \ - apt-get update && apt-get -y install git git-lfs python3 python3-pip curl python3-colorama cmake g++ build-essential libncurses5 libusb-1.0-0 gdb && \ - git lfs install - -# Install cubeclt -RUN mkdir /temp && cd /temp && git clone https://github.com/HyperloopUPV-H8/cubeclt.git && \ - cd cubeclt && git lfs pull && chmod +x cubeclt_1.16.0_installer.sh && \ - echo | LICENSE_ALREADY_ACCEPTED=1 ./cubeclt_1.16.0_installer.sh - -ENV PATH="$PATH:/opt/st/stm32cubeclt_1.16.0/GNU-tools-for-STM32/bin:/opt/st/stm32cubeclt_1.16.0/CMake/bin:/opt/st/stm32cubeclt_1.16.0/Ninja/bin" - -ENV PYTHONPATH="/workspaces/template-project/Tests/VirtualMCU/src/:$PYTHONPATH" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index a2acd037..00000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "Dev Container for Template Project", - "build": { - "dockerfile": "Dockerfile", - "options": [ - "--platform=linux/amd64" - ] - }, - "runArgs": [ - "--cap-add=SYS_PTRACE", - "--security-opt=seccomp=unconfined" - ], - "remoteUser": "root", - "customizations": { - "vscode": { - "extensions": [ - "ms-vscode.cpptools-extension-pack", - "ms-python.python", - "mcu-debug.debug-tracker-vscode", - "marus25.cortex-debug" - ] - } - } -} \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 196da5a5..1400b512 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,7 +50,7 @@ jobs: run: | git config --global --add safe.directory '*' git submodule update --init --depth 1 - ./deps/ST-LIB/tools/init-submodules.sh + ./deps/ST-LIB/tools/init-submodules.sh shell: bash - name: Setup Python environment diff --git a/.github/workflows/format-checks.yml b/.github/workflows/format-checks.yml index a225afb4..bc9db041 100644 --- a/.github/workflows/format-checks.yml +++ b/.github/workflows/format-checks.yml @@ -3,6 +3,7 @@ name: Format Checks on: workflow_dispatch: pull_request: + branches: [ main ] paths: - '**.cpp' - '**.hpp' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..ebf153e4 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,69 @@ +name: Run Simulator Tests + +on: + workflow_dispatch: + pull_request: + branches: [ main ] + +concurrency: + group: tests-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + tests: + runs-on: ubuntu-24.04 + timeout-minutes: 30 + permissions: + contents: read + packages: read + + container: + image: ghcr.io/hyperloop-upv/hyperloop-firmware-toolchain:latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: false + fetch-depth: 1 + + - name: Init submodules + run: | + git config --global --add safe.directory '*' + git submodule update --init --depth 1 + ./deps/ST-LIB/tools/init-submodules.sh + shell: bash + + - name: Cache simulator deps + uses: actions/cache@v4 + with: + path: out/build/simulator/_deps + key: template-simulator-deps-${{ runner.os }}-${{ hashFiles('CMakeLists.txt', 'deps/ST-LIB/CMakeLists.txt', 'deps/ST-LIB/Tests/CMakeLists.txt') }} + restore-keys: | + template-simulator-deps-${{ runner.os }}- + + - name: Configure (CMake) + run: | + cmake --preset simulator + shell: bash + + - name: Build + run: | + cmake --build --preset simulator + shell: bash + + - name: Run tests (ctest) + run: | + ctest --preset simulator-all + shell: bash + + - name: Upload test reports + if: always() + uses: actions/upload-artifact@v4 + with: + name: simulator-test-reports + path: | + out/build/simulator/Testing/Temporary/LastTest.log + out/build/simulator/deps/ST-LIB/Testing/Temporary/LastTest.log + retention-days: 7 + if-no-files-found: ignore diff --git a/.mxproject b/.mxproject index 4d55e80c..219f971a 100644 --- a/.mxproject +++ b/.mxproject @@ -31,4 +31,3 @@ SourcePath#0=..\LWIP\App SourcePath#1=..\LWIP\Target SourcePath#2=..\Core\Src SourceFiles=; - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index be8bb296..2b383cc9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,12 +3,18 @@ default_install_hook_types: - pre-commit - pre-push repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-merge-conflict + - id: end-of-file-fixer + - id: trailing-whitespace - repo: https://github.com/pre-commit/mirrors-clang-format rev: v17.0.6 hooks: - id: clang-format files: ^(Core/|tools/).* + exclude: ^(Core/Inc/Code_generation/Packet_generation/(DataTemplate\.hpp|OrderTemplate\.hpp)|tools/binary_metadata_template\.cpp)$ types_or: - c - c++ - exclude: ^Core/Inc/Code_generation/Packet_generation/.*Template\.hpp$|^tools/binary_metadata_template\.cpp$ diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 6a9c6db9..2455be23 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,4 +9,4 @@ } ], "version": 4 -} \ No newline at end of file +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 74f0dee8..b04553fd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,176 +1,228 @@ { - "version": "0.2.0", - "configurations": [ + "version": "0.2.0", + "configurations": [ + { + "name": "MCU | OpenOCD | Build + Debug (RTT)", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/out/build/latest.elf", + "servertype": "openocd", + "serverpath": "openocd", + "configFiles": [ + ".vscode/stlink.cfg", + ".vscode/stm32h7x.cfg" + ], + "serverArgs": [ + "-c", + "transport select swd", + "-c", + "gdb_port 3333", + "-c", + "tcl_port 6666", + "-c", + "telnet_port 4444" + ], + "gdbPath": "arm-none-eabi-gdb", + "device": "STM32H723ZGTx", + "interface": "swd", + "runToEntryPoint": "main", + "svdFile": "${workspaceFolder}/STM32H723.svd", + "showDevDebugOutput": "parsed", + "preLaunchCommands": [ + "monitor reset halt" + ], + "postLaunchCommands": [ + "monitor rtt setup 0x24000000 0x24080000 \"SEGGER RTT\"", + "monitor rtt start", + "monitor rtt server start 9090 0" + ], + "postRestartCommands": [ + "monitor halt", + "monitor rtt setup 0x24000000 0x24080000 \"SEGGER RTT\"", + "monitor rtt start", + "monitor rtt server start 9090 0" + ], + "rttConfig": { + "enabled": true, + "address": "auto", + "decoders": [ + { + "port": 0, + "type": "console" + } + ] + }, + "preLaunchTask": "CMake: build" + }, + { + "name": "MCU | OpenOCD | Debug (No Build, RTT)", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/out/build/latest.elf", + "servertype": "openocd", + "serverpath": "openocd", + "configFiles": [ + ".vscode/stlink.cfg", + ".vscode/stm32h7x.cfg" + ], + "serverArgs": [ + "-c", + "transport select swd", + "-c", + "gdb_port 3333", + "-c", + "tcl_port 6666", + "-c", + "telnet_port 4444" + ], + "gdbPath": "arm-none-eabi-gdb", + "device": "STM32H723ZGTx", + "interface": "swd", + "runToEntryPoint": "main", + "svdFile": "${workspaceFolder}/STM32H723.svd", + "showDevDebugOutput": "parsed", + "preLaunchCommands": [ + "monitor reset halt" + ], + "postLaunchCommands": [ + "monitor rtt setup 0x24000000 0x24080000 \"SEGGER RTT\"", + "monitor rtt start", + "monitor rtt server start 9090 0" + ], + "postRestartCommands": [ + "monitor halt", + "monitor rtt setup 0x24000000 0x24080000 \"SEGGER RTT\"", + "monitor rtt start", + "monitor rtt server start 9090 0" + ], + "rttConfig": { + "enabled": true, + "address": "auto", + "decoders": [ + { + "port": 0, + "type": "console" + } + ] + } + }, + { + "name": "MCU | OpenOCD | Attach (External Server, RTT)", + "type": "cortex-debug", + "request": "attach", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/out/build/latest.elf", + "servertype": "external", + "gdbTarget": "localhost:3333", + "gdbPath": "arm-none-eabi-gdb", + "device": "STM32H723ZGTx", + "interface": "swd", + "runToEntryPoint": "main", + "svdFile": "${workspaceFolder}/STM32H723.svd", + "showDevDebugOutput": "parsed", + "postAttachCommands": [ + "monitor rtt setup 0x24000000 0x24080000 \"SEGGER RTT\"", + "monitor rtt start", + "monitor rtt server start 9090 0" + ], + "rttConfig": { + "enabled": true, + "address": "auto", + "decoders": [ + { + "port": 0, + "type": "console" + } + ] + } + }, + { + "name": "MCU | ST-LINK | Build + Debug", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/out/build/latest.elf", + "servertype": "stlink", + "serverpath": "ST-LINK_gdbserver", + "gdbPath": "arm-none-eabi-gdb", + "device": "STM32H723ZGTx", + "interface": "swd", + "runToEntryPoint": "main", + "svdFile": "${workspaceFolder}/STM32H723.svd", + "showDevDebugOutput": "parsed", + "preLaunchCommands": [ + "monitor reset halt" + ], + "preLaunchTask": "CMake: build" + }, + { + "name": "MCU | ST-LINK | Debug (No Build)", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/out/build/latest.elf", + "servertype": "stlink", + "serverpath": "ST-LINK_gdbserver", + "gdbPath": "arm-none-eabi-gdb", + "device": "STM32H723ZGTx", + "interface": "swd", + "runToEntryPoint": "main", + "svdFile": "${workspaceFolder}/STM32H723.svd", + "showDevDebugOutput": "parsed", + "preLaunchCommands": [ + "monitor reset halt" + ] + }, + { + "name": "MCU | ST-LINK | Attach (External GDB Server)", + "type": "cortex-debug", + "request": "attach", + "cwd": "${workspaceFolder}", + "executable": "${workspaceFolder}/out/build/latest.elf", + "servertype": "external", + "gdbTarget": "localhost:61234", + "gdbPath": "arm-none-eabi-gdb", + "device": "STM32H723ZGTx", + "interface": "swd", + "runToEntryPoint": "main", + "svdFile": "${workspaceFolder}/STM32H723.svd", + "showDevDebugOutput": "parsed", + "postAttachCommands": [ + "monitor halt" + ] + }, + { + "name": "SIM | Rosetta | Debug App", + "type": "cppdbg", + "request": "launch", + "program": "out/build/latest.elf", + "miDebuggerServerAddress": "localhost:1234", + "miDebuggerPath": "/usr/bin/gdb", + "MIMode": "gdb", + "setupCommands": [ { - "name": "Build and Debug STM32H723 (OpenOCD + RTT)", - "type": "cortex-debug", - "request": "launch", - "cwd": "${workspaceFolder}", - "executable": "${workspaceFolder}/out/build/latest.elf", - "servertype": "openocd", - "configFiles": [ - "interface/stlink.cfg", - "target/stm32h7x.cfg" - ], - "serverpath": "openocd", - "device": "STM32H723ZGTx", - "interface": "swd", - "runToEntryPoint": "main", - "svdFile": "./STM32H723.svd", - "showDevDebugOutput": "parsed", - "postLaunchCommands": [ - "monitor rtt setup 0x24000000 0x24080000 \"SEGGER RTT\"", - "monitor rtt start", - "monitor rtt server start 9090 0" - ], - "postRestartCommands": [ - "monitor halt", - "monitor rtt setup 0x24000000 0x24080000 \"SEGGER RTT\"", - "monitor rtt start", - "monitor rtt server start 9090 0" - ], - "rttConfig": { - "enabled": true, - "address": "auto", - "decoders": [ - { - "port": 0, - "type": "console" - } - ] - }, - "preLaunchTask": "CMake: build" - }, - { - "name": "Debug STM32H723 (OpenOCD + RTT) - No Build", - "type": "cortex-debug", - "request": "launch", - "cwd": "${workspaceFolder}", - "executable": "${workspaceFolder}/out/build/latest.elf", - "servertype": "openocd", - "configFiles": [ - "interface/stlink.cfg", - "target/stm32h7x.cfg" - ], - "serverpath": "openocd", - "device": "STM32H723ZGTx", - "interface": "swd", - "runToEntryPoint": "main", - "svdFile": "./STM32H723.svd", - "showDevDebugOutput": "parsed", - "postLaunchCommands": [ - "monitor rtt setup 0x24000000 0x24080000 \"SEGGER RTT\"", - "monitor rtt start", - "monitor rtt server start 9090 0" - ], - "postRestartCommands": [ - "monitor halt", - "monitor rtt setup 0x24000000 0x24080000 \"SEGGER RTT\"", - "monitor rtt start", - "monitor rtt server start 9090 0" - ], - "rttConfig": { - "enabled": true, - "address": "auto", - "decoders": [ - { - "port": 0, - "type": "console" - } - ] - }, - // "preLaunchTask": "Pre-flash check" - }, - { - "name": "Debug simulator on Rosetta", - "type": "cppdbg", - "request": "launch", - "program": "out/build/latest.elf", - "miDebuggerServerAddress": "localhost:1234", - "miDebuggerPath": "/usr/bin/gdb", - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Set architecture to x86-64", - "text": "set architecture i386:x86-64", - "ignoreFailures": false - } - ], - "preLaunchTask": "Start Debug Server for Simulator on emulated arch", - "cwd": "${workspaceFolder}", - "externalConsole": false - }, - { - "name": "Debug ST-LIB tests (simulator)", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/out/build/simulator/deps/ST-LIB/Tests/st-lib-test", - "args": [], - "cwd": "${workspaceFolder}", - "externalConsole": false, - "stopAtEntry": false, - "MIMode": "lldb", - "preLaunchTask": "Tests: Build simulator" - }, - { - "type": "cortex-debug", - "request": "launch", - "name": "Build and Debug Project", - "servertype": "stlink", - "cwd": "${workspaceRoot}", - "runToEntryPoint": "main", - "showDevDebugOutput": "raw", - "executable": "out/build/latest.elf", - "device": "STM32H723ZG", - "configFiles": [ - ".vscode/stlink.cfg", - ".vscode/stm32h7x.cfg" - ], - "svdFile": ".vscode/STM32H723.svd", - "swoConfig": { - "enabled": true, - "cpuFrequency": 8000000, - "swoFrequency": 2000000, - "source": "probe", - "decoders": [ - { - "type": "console", - "label": "ITM", - "port": 0 - } - ] - }, - "preLaunchTask": "CMake: build" - }, - { - "type": "cortex-debug", - "request": "launch", - "name": "Debug Project", - "servertype": "stlink", - "cwd": "${workspaceRoot}", - "runToEntryPoint": "main", - "showDevDebugOutput": "raw", - "executable": "out/build/latest.elf", - "device": "STM32H723ZG", - "configFiles": [ - ".vscode/stlink.cfg", - ".vscode/stm32h7x.cfg" - ], - "svdFile": "./STM32H723.svd", - "swoConfig": { - "enabled": true, - "cpuFrequency": 8000000, - "swoFrequency": 2000000, - "source": "probe", - "decoders": [ - { - "type": "console", - "label": "ITM", - "port": 0 - } - ] - }, - "stm32cubeprogrammer": "/opt/st/stm32cubeclt_1.16.0/STM32CubeProgrammer/bin" + "description": "Set architecture to x86-64", + "text": "set architecture i386:x86-64", + "ignoreFailures": false } - ] + ], + "preLaunchTask": "SIM | Rosetta | Start Debug Server", + "cwd": "${workspaceFolder}", + "externalConsole": false + }, + { + "name": "SIM | ST-LIB Tests | Debug", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/out/build/simulator/deps/ST-LIB/Tests/st-lib-test", + "args": [], + "cwd": "${workspaceFolder}", + "externalConsole": false, + "stopAtEntry": false, + "MIMode": "lldb", + "preLaunchTask": "SIM | Tests | Build" + } + ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 05284a38..31213400 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,42 +2,112 @@ "version": "2.0.0", "tasks": [ { - "label": "Start Debug Server for Simulator on emulated arch", + "label": "SIM | Rosetta | Start Debug Server", "type": "shell", "command": "ROSETTA_DEBUGSERVER_PORT=1234 ./out/build/latest.elf", "problemMatcher": [], "isBackground": true }, { - "label": "Tests: Configure simulator", + "label": "MCU | OpenOCD | Start Server", + "type": "shell", + "command": "openocd", + "args": [ + "-f", + ".vscode/stlink.cfg", + "-f", + ".vscode/stm32h7x.cfg", + "-c", + "transport select swd", + "-c", + "gdb_port 3333", + "-c", + "tcl_port 6666", + "-c", + "telnet_port 4444" + ], + "isBackground": true, + "problemMatcher": { + "owner": "custom", + "pattern": [ + { + "regexp": ".*", + "message": 0 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": ".*", + "endsPattern": "Listening on port 3333 for gdb connections" + } + }, + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + } + }, + { + "label": "MCU | OpenOCD | RTT Console", + "type": "shell", + "command": "nc 127.0.0.1 9090", + "problemMatcher": [], + "presentation": { + "reveal": "always", + "panel": "new" + } + }, + { + "label": "MCU | ST-LINK | Start GDB Server", + "type": "shell", + "command": "ST-LINK_gdbserver", + "args": [ + "-p", + "61234", + "-d", + "-e", + "--halt", + "--frequency", + "4000" + ], + "problemMatcher": [], + "isBackground": true, + "presentation": { + "reveal": "always", + "panel": "dedicated", + "clear": true + } + }, + { + "label": "SIM | Tests | Configure", "type": "shell", "command": "cmake --preset simulator", "problemMatcher": [] }, { - "label": "Tests: Build simulator", + "label": "SIM | Tests | Build", "type": "shell", "command": "cmake --build --preset simulator -j8", "dependsOn": [ - "Tests: Configure simulator" + "SIM | Tests | Configure" ], "problemMatcher": [] }, { - "label": "Tests: Run all (simulator)", + "label": "SIM | Tests | Run All", "type": "shell", - "command": "ctest --test-dir out/build/simulator/deps/ST-LIB --output-on-failure", + "command": "ctest --preset simulator-all", "dependsOn": [ - "Tests: Build simulator" + "SIM | Tests | Build" ], "problemMatcher": [] }, { - "label": "Tests: Run ADC (simulator)", + "label": "SIM | Tests | Run ADC", "type": "shell", - "command": "ctest --test-dir out/build/simulator/deps/ST-LIB -R ADCTest --output-on-failure", + "command": "ctest --preset simulator-adc", "dependsOn": [ - "Tests: Build simulator" + "SIM | Tests | Build" ], "problemMatcher": [] } diff --git a/CMakeLists.txt b/CMakeLists.txt index 83570067..c1d122ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,14 @@ cmake_minimum_required(VERSION 3.14) project(template-project LANGUAGES ASM C CXX) +include(CTest) +enable_testing() set(EXECUTABLE ${PROJECT_NAME}.elf) -set(Board "TEST") # User must change the Board name +set(BOARD_NAME "TEST" CACHE STRING "Board key from Core/Inc/Code_generation/JSON_ADE/boards.json") +if(BOARD_NAME STREQUAL "") + message(FATAL_ERROR "BOARD_NAME cannot be empty") +endif() set(STLIB_DIR ${CMAKE_CURRENT_LIST_DIR}/deps/ST-LIB) set(LD_SCRIPT ${STLIB_DIR}/STM32H723ZGTX_FLASH.ld) @@ -16,6 +21,11 @@ if(CMAKE_HOST_WIN32) else() set(VENV_PYTHON ${CMAKE_SOURCE_DIR}/virtual/bin/python) endif() +if(EXISTS "${VENV_PYTHON}") + set(PYTHON_FOR_TOOLS "${VENV_PYTHON}") +else() + set(PYTHON_FOR_TOOLS "${Python3_EXECUTABLE}") +endif() option(USE_ETHERNET "Enable ethernet peripheral" OFF) option(TARGET_NUCLEO "Targets the STM32H723 Nucleo development board" OFF) @@ -39,28 +49,16 @@ if(USE_CCACHE) endif() endif() -if(PROJECT_IS_TOP_LEVEL AND (NOT CMAKE_CROSSCOMPILING)) - include(FetchContent) - option(BUILD_GMOCK OFF) - option(INSTALL_GTEST OFF) - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.15.2 - ) - FetchContent_MakeAvailable(googletest) - add_library(GTest::GTest INTERFACE IMPORTED) - target_link_libraries(GTest::GTest INTERFACE gtest_main) -endif() - message(STATUS "Template project: CMAKE_CROSSCOMPILING = ${CMAKE_CROSSCOMPILING}") message(STATUS "Template project: USE_ETHERNET = ${USE_ETHERNET}") message(STATUS "Template project: TARGET_NUCLEO = ${TARGET_NUCLEO}") +message(STATUS "Template project: BOARD_NAME = ${BOARD_NAME}") add_subdirectory(${STLIB_DIR}) message(STATUS "Using ST-LIB target: ${STLIB_LIBRARY}") file(GLOB_RECURSE GENERATOR_JSONS + CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/Core/Inc/Code_generation/JSON_ADE/*.json ) @@ -70,8 +68,7 @@ add_custom_command( OUTPUT ${GENERATED_DATA_PACKETS} ${GENERATED_ORDER_PACKETS} - ${GENERATED_STATE_MACHINE} - COMMAND ${VENV_PYTHON} ${GENERATOR_SCRIPT} ${Board} + COMMAND ${PYTHON_FOR_TOOLS} ${GENERATOR_SCRIPT} ${BOARD_NAME} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} DEPENDS ${GENERATOR_SCRIPT} @@ -79,16 +76,17 @@ add_custom_command( ${CMAKE_SOURCE_DIR}/Core/Inc/Code_generation/Packet_generation/DataTemplate.hpp ${CMAKE_SOURCE_DIR}/Core/Inc/Code_generation/Packet_generation/OrderTemplate.hpp ${GENERATOR_JSONS} - COMMENT "Generating packets for ${Board}" + COMMENT "Generating packets for ${BOARD_NAME}" ) -add_custom_target(run_generator ALL +add_custom_target(run_generator DEPENDS ${GENERATED_DATA_PACKETS} ${GENERATED_ORDER_PACKETS} ) file(GLOB_RECURSE METADATA_SOURCES + CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/Core/*.c ${CMAKE_SOURCE_DIR}/Core/*.cpp ${CMAKE_SOURCE_DIR}/Core/*.h @@ -103,7 +101,7 @@ list(REMOVE_ITEM METADATA_SOURCES ${METADATA_OUTPUT}) add_custom_command( OUTPUT ${METADATA_OUTPUT} - COMMAND ${VENV_PYTHON} ${METADATA_SCRIPT} + COMMAND ${PYTHON_FOR_TOOLS} ${METADATA_SCRIPT} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} DEPENDS ${METADATA_SCRIPT} @@ -117,13 +115,13 @@ add_custom_target(generate_binary_metadata ) if(CMAKE_CROSSCOMPILING) - file(GLOB_RECURSE SOURCE_C ${CMAKE_SOURCE_DIR}/Core/*.c) - file(GLOB_RECURSE SOURCE_CPP ${CMAKE_SOURCE_DIR}/Core/*.cpp) - file(GLOB_RECURSE SOURCE_H ${CMAKE_SOURCE_DIR}/Core/*.h) - file(GLOB_RECURSE SOURCE_HPP ${CMAKE_SOURCE_DIR}/Core/*.hpp) + file(GLOB_RECURSE SOURCE_C CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/Core/*.c) + file(GLOB_RECURSE SOURCE_CPP CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/Core/*.cpp) + file(GLOB_RECURSE SOURCE_H CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/Core/*.h) + file(GLOB_RECURSE SOURCE_HPP CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/Core/*.hpp) if(NOT BUILD_EXAMPLES) - file(GLOB_RECURSE EXAMPLE_CPP ${CMAKE_SOURCE_DIR}/Core/Src/Examples/*.cpp) + file(GLOB_RECURSE EXAMPLE_CPP CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/Core/Src/Examples/*.cpp) list(REMOVE_ITEM SOURCE_CPP ${EXAMPLE_CPP}) endif() diff --git a/CMakePresets.json b/CMakePresets.json index f59704a4..4e441148 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -203,6 +203,17 @@ "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } + }, + { + "name": "simulator-asan", + "displayName": "Simulator [ASAN+UBSAN]", + "inherits": [ + "sim" + ], + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "STLIB_ENABLE_SANITIZERS": "ON" + } } ], "buildPresets": [ @@ -269,6 +280,41 @@ { "name": "simulator", "configurePreset": "simulator" + }, + { + "name": "simulator-asan", + "configurePreset": "simulator-asan" + } + ], + "testPresets": [ + { + "name": "simulator-all", + "configurePreset": "simulator", + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "error" + } + }, + { + "name": "simulator-adc", + "inherits": "simulator-all", + "filter": { + "include": { + "name": "ADCTest" + } + } + }, + { + "name": "simulator-all-asan", + "configurePreset": "simulator-asan", + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "error" + } } ] } diff --git a/Core/Inc/Code_generation/Generator.py b/Core/Inc/Code_generation/Generator.py index b308426c..774d9aaf 100644 --- a/Core/Inc/Code_generation/Generator.py +++ b/Core/Inc/Code_generation/Generator.py @@ -1,32 +1,28 @@ -import sys -from Packet_generation.Packet_generation import * +import argparse +from Packet_generation.Packet_generation import ( + Generate_PacketDescription, + Generate_DataPackets_hpp, + Generate_OrderPackets_hpp, +) -if len(sys.argv)<2: - print("Please enter a board name,exiting...") - sys.exit() - -JSONpath = "Core/Inc/Code_generation/JSON_ADE" -aux = sys.argv[1] -filtered = "" -for char in aux: - if char.isalpha(): - filtered += char - else: - break -board = filtered +def parse_args(): + parser = argparse.ArgumentParser(description="Generate packet headers from JSON_ADE") + parser.add_argument("board", help="Board key from Core/Inc/Code_generation/JSON_ADE/boards.json") + return parser.parse_args() -boards = Generate_PacketDescription(JSONpath, board) -if __name__ == "__main__": +def main(): + args = parse_args() + json_path = "Core/Inc/Code_generation/JSON_ADE" + board = args.board.strip() + if not board: + raise SystemExit("Board name cannot be empty") + + Generate_PacketDescription(json_path, board) Generate_DataPackets_hpp(board) Generate_OrderPackets_hpp(board) - - - - - - - \ No newline at end of file +if __name__ == "__main__": + main() diff --git a/Core/Inc/Code_generation/Packet_generation/DataTemplate.hpp b/Core/Inc/Code_generation/Packet_generation/DataTemplate.hpp index 23cc3d9f..02030066 100644 --- a/Core/Inc/Code_generation/Packet_generation/DataTemplate.hpp +++ b/Core/Inc/Code_generation/Packet_generation/DataTemplate.hpp @@ -1,27 +1,27 @@ #pragma once #include "ST-LIB.hpp" -/*Data packets for {{board}} +/*Data packets for {{board}} -AUTOGENERATED CODE, DO NOT MODIFY-*/ class DataPackets{ public: {% for enum in enums -%} - enum class {{enum.name}} : uint8_t + enum class {{enum.name}} : uint8_t { {%- for value in enum["values"] %} {{value}} = {{loop.index0}}, {%- endfor %} }; {% endfor %} - + {% for packet in packets -%} static void {{packet.name}}_init({% for variable in packet.variables %}{{variable.type}} &{{variable.name}}{% if not loop.last %}, {% endif %}{% endfor %}) { {{packet.name}}_packet = new HeapPacket(static_cast({{packet.id}}){% if packet.variables %}, {% for variable in packet.variables %}&{{variable.name}}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}); } - + {% endfor -%} - + public: {%for packet in packets -%} inline static HeapPacket *{{packet.name}}_packet{nullptr}; @@ -29,9 +29,9 @@ class DataPackets{ {% for socket in DatagramSockets -%} inline static {{socket.type}} *{{socket.name}}{nullptr}; {% endfor %} - + static void start() - { + { {% for packet in packets -%} if ({{packet.name}}_packet == nullptr) { ErrorHandler("Packet {{packet.name}} not initialized"); @@ -41,7 +41,7 @@ class DataPackets{ {% for socket in DatagramSockets -%} {{socket.name}} = new DatagramSocket("{{socket.board_ip}}",{{socket.port}},"{{socket.remote_ip}}",{{socket.port}}); {% endfor %} - + {%- for group in sending_packets %} Scheduler::register_task({% if group.period_type == "ms" %}{{ (group.period*1000)|round|int }}{% else %}{{ group.period|round|int }}{% endif %}, +[](){ {% for packet in group.packets -%} @@ -52,5 +52,5 @@ class DataPackets{ } - + }; diff --git a/Core/Inc/Code_generation/Packet_generation/OrderTemplate.hpp b/Core/Inc/Code_generation/Packet_generation/OrderTemplate.hpp index 46bdd414..2ac2ced3 100644 --- a/Core/Inc/Code_generation/Packet_generation/OrderTemplate.hpp +++ b/Core/Inc/Code_generation/Packet_generation/OrderTemplate.hpp @@ -1,14 +1,14 @@ #pragma once #include "ST-LIB.hpp" -/*Order packets for {{board}} +/*Order packets for {{board}} -AUTOGENERATED CODE, DO NOT MODIFY- */ class OrderPackets{ public: {% for enum in enums -%} - enum class {{enum.name}} : uint8_t + enum class {{enum.name}} : uint8_t { {%- for value in enum["values"] %} {{value}} = {{loop.index0}}, @@ -25,7 +25,7 @@ class OrderPackets{ {% for packet in packets -%} inline static HeapOrder *{{packet.name}}_order{nullptr}; {% endfor %} - + {% for packet in packets -%} static void {{packet.name}}_init({% for variable in packet.variables %}{{variable.type}} &{{variable.name}}{% if not loop.last %}, {% endif %}{% endfor %}) { @@ -63,4 +63,4 @@ class OrderPackets{ {{packet.name}}_flag = true; } {% endfor %} -}; \ No newline at end of file +}; diff --git a/Core/Inc/Code_generation/Packet_generation/Packet_descriptions.py b/Core/Inc/Code_generation/Packet_generation/Packet_descriptions.py index 5e9b2cb0..6399b6a8 100644 --- a/Core/Inc/Code_generation/Packet_generation/Packet_descriptions.py +++ b/Core/Inc/Code_generation/Packet_generation/Packet_descriptions.py @@ -1,4 +1,4 @@ -import re +import re import json class BoardDescription: @@ -13,7 +13,7 @@ def __init__(self,name:str,board:dict,JSONpath:str): self.sockets=self.SocketsDescription(socks,self.ip) except Exception as e: raise Exception(f"Error in file {JSONpath}/boards/{name}/sockets.json: {e}") - #Packets: + #Packets: self.sending_packets = [] self.data_size =0 self.order_size =0 @@ -40,15 +40,15 @@ def __init__(self,name:str,board:dict,JSONpath:str): aux_sending= PacketDescription.check_for_sending(packet) if aux_sending is not None: self.sending_packets.append(aux_sending) - + if self.packets[packets_name][i].type != "order": self.data_size += 1 else: self.order_size += 1 i += 1 - + self.sending_packets = self.fix_sendind_packets(self.sending_packets) - + @staticmethod def fix_sendind_packets(sending_packets:list): fixed_packets = [] @@ -72,9 +72,9 @@ def fix_sendind_packets(sending_packets:list): fixed_packets.append(entry) return fixed_packets - - - + + + class SocketsDescription: def __init__(self,sockets:list,board_ip:str): self.allSockets=[] @@ -86,16 +86,16 @@ def __init__(self,sockets:list,board_ip:str): name = sock["name"].replace(" ", "_").replace("-", "_") sock_type = sock["type"] self.allSockets.append({"name": name,"type":sock_type}) - + if sock_type == "ServerSocket": self.ServerSockets.append({"name": name,"type":sock_type,"board_ip":self.board_ip, "port": sock["port"]}) elif sock_type == "Socket": self.Sockets.append({"name": name,"type":sock_type,"board_ip":self.board_ip, "local_port": sock["local_port"], "remote_ip": sock["remote_ip"], "remote_port": sock["remote_port"]}) elif sock_type == "DatagramSocket": self.DatagramSockets.append({"name": name,"type":sock_type,"board_ip":self.board_ip, "port": sock["port"],"remote_ip":sock["remote_ip"]}) - - + + class PacketDescription: def __init__(self, packet:dict,measurements:list, filename:str="Unknown"): self.id =packet["id"] @@ -108,13 +108,13 @@ def __init__(self, packet:dict,measurements:list, filename:str="Unknown"): for variable in packet["variables"]: self.variables.append(variable) self.measurements.append(MeasurmentsDescription(measurements,variable, filename)) - + @staticmethod def check_for_sending(packet:dict): if "period" in packet and "period_type" in packet and "socket" in packet: name = packet["name"].replace(" ", "_").replace("-", "_") return {"name": name,"period": packet["period"],"period_type":packet["period_type"],"socket": packet["socket"]} - + elif "period_ms" in packet and "socket" in packet: name = packet["name"].replace(" ", "_").replace("-", "_") return {"name": name,"period": packet["period_ms"],"period_type":"ms","socket": packet["socket"]} @@ -126,11 +126,11 @@ def __init__(self,measurements:list, variable:str, filename:str="Unknown"): if not hasattr(self.__class__, 'viewed_measurements'): self.__class__.viewed_measurements = {} measurement = self._MeasurementSearch(measurements,variable) - + if measurement is None: print(f"Measurement not found for variable: {variable} in file: {filename}\n") raise Exception(f"Measurement not found for variable: {variable} in file: {filename}") - + self.name = measurement["name"] self.type = (self._unsigned_int_correction(measurement["type"]).replace(" ", "_").replace("-", "_")) if self.type == "enum": @@ -145,8 +145,8 @@ def _Enum_values_correction(values:list): for i in range(len(values)): values[i] = values[i].replace(" ", "_").replace("-", "_") return values - - + + @staticmethod def _MeasurementSearch(measurements:list, variable:str): if variable in MeasurmentsDescription.viewed_measurements: @@ -157,8 +157,8 @@ def _MeasurementSearch(measurements:list, variable:str): MeasurmentsDescription.viewed_measurements[variable] = measurment return measurment return None - - + + @staticmethod def _unsigned_int_correction(type:str): aux_type = type[:4] @@ -166,4 +166,4 @@ def _unsigned_int_correction(type:str): type += "_t" elif type == "float32": type = "float" - return type \ No newline at end of file + return type diff --git a/Core/Inc/Code_generation/Packet_generation/Packet_generation.py b/Core/Inc/Code_generation/Packet_generation/Packet_generation.py index f554949b..d852d367 100644 --- a/Core/Inc/Code_generation/Packet_generation/Packet_generation.py +++ b/Core/Inc/Code_generation/Packet_generation/Packet_generation.py @@ -6,7 +6,7 @@ templates_path = "Core/Inc/Code_generation/Packet_generation" -def Generate_PacketDescription(JSONpath:str,board:str): +def Generate_PacketDescription(JSONpath:str,board:str): with open(JSONpath+"/boards.json") as f: boards = json.load(f) boards_name = [] @@ -20,9 +20,9 @@ def Generate_PacketDescription(JSONpath:str,board:str): else: print(f"Board {board} not found, exiting...") sys.exit() - + return boards_name - + #--------------DataPackets.hpp generation---------------# @@ -36,8 +36,8 @@ def GenerateDataEnum(board:BoardDescription): if hasattr(measurement, "enum")and measurement.enum not in Enums: Enums.append(measurement.enum) return Enums - - + + def GenerateDataPackets(board:BoardDescription): Packets =[] totaldata = [] @@ -50,10 +50,10 @@ def GenerateDataPackets(board:BoardDescription): tempdata +=(str(variable) +",") tempdata_but_pointer +=("&"+str(variable) +",") if tempdata.endswith(","): - tempdata = tempdata[:-1] + tempdata = tempdata[:-1] if tempdata_but_pointer.endswith(","): tempdata_but_pointer = tempdata_but_pointer[:-1] - + packet_variables = [] for measurement in packet_instance.measurements: packet_variables.append({ @@ -67,11 +67,11 @@ def GenerateDataPackets(board:BoardDescription): aux_data = {"type": measurement.type, "name": measurement.id.replace(" ", "_").replace("-", "_")} if not any(x["name"] == aux_data["name"] for x in totaldata): totaldata.append(aux_data) - + return Packets,totaldata - + packets,data = GenerateDataPackets(board) - + def GenerateGroupedSendingPackets(board: BoardDescription): datagram_sockets = [s["name"] for s in board.sockets.DatagramSockets] grouped_lookup = {} @@ -84,17 +84,17 @@ def GenerateGroupedSendingPackets(board: BoardDescription): period = packet["period"] period_type = packet["period_type"] names = packet["name"] - + key = (period, period_type) if key not in grouped_lookup: grouped_lookup[key] = [] - + if isinstance(names, list): for name in names: grouped_lookup[key].append({"socket": socket_name, "name": name}) else: grouped_lookup[key].append({"socket": socket_name, "name": names}) - + grouped_list = [] for (period, period_type), items in grouped_lookup.items(): grouped_list.append({ @@ -123,16 +123,16 @@ def Generate_DataPackets_hpp(board_input:str): if board_instance.data_size == 0: if os.path.exists(data_packets_path): os.remove(data_packets_path) - return - + return + env= jinja2.Environment(loader=jinja2.FileSystemLoader(templates_path)) template = env.get_template("DataTemplate.hpp") context = Get_data_context(board_instance) - + with open(data_packets_path,"w") as Output: Output.write(template.render(context)) - + #--------------OrderPackets.hpp generation---------------# def Get_order_context(board:BoardDescription): @@ -145,8 +145,8 @@ def GenerateOrderEnum(board:BoardDescription): if hasattr(measurement, "enum") and measurement.enum not in Enums: Enums.append(measurement.enum) return Enums - - + + def GenerateOrderPackets(board:BoardDescription): Packets =[] totaldata = [] @@ -159,8 +159,8 @@ def GenerateOrderPackets(board:BoardDescription): tempdata +=(str(variable) +",") tempdata_but_pointer +=("&"+str(variable) +",") if tempdata.endswith(","): - tempdata = tempdata[:-1] - tempdata_but_pointer = tempdata_but_pointer[:-1] + tempdata = tempdata[:-1] + tempdata_but_pointer = tempdata_but_pointer[:-1] packet_variables = [] for measurement in packet_instance.measurements: @@ -175,10 +175,10 @@ def GenerateOrderPackets(board:BoardDescription): aux_data = {"type": measurement.type, "name": measurement.id} if not any(x["name"] == aux_data["name"] for x in totaldata): totaldata.append(aux_data) - + return Packets,totaldata - - + + packets,data = GenerateOrderPackets(board) context = { "board": board.name, @@ -197,14 +197,12 @@ def Generate_OrderPackets_hpp(board_input:str): if board_instance.order_size == 0: if os.path.exists(order_packets_path): os.remove(order_packets_path) - return - + return + env= jinja2.Environment(loader=jinja2.FileSystemLoader(templates_path)) template = env.get_template("OrderTemplate.hpp") context = Get_order_context(board_instance) - + with open(order_packets_path,"w") as Output: Output.write(template.render(context)) - - diff --git a/Core/Inc/Communications/Packets/.gitkeep b/Core/Inc/Communications/Packets/.gitkeep index d3f5a12f..e69de29b 100644 --- a/Core/Inc/Communications/Packets/.gitkeep +++ b/Core/Inc/Communications/Packets/.gitkeep @@ -1 +0,0 @@ - diff --git a/Core/Src/Examples/ExampleMPU.cpp b/Core/Src/Examples/ExampleMPU.cpp index 12a70575..f85a8a66 100644 --- a/Core/Src/Examples/ExampleMPU.cpp +++ b/Core/Src/Examples/ExampleMPU.cpp @@ -549,4 +549,4 @@ void assert_failed(uint8_t* file, uint32_t line) { } } -#endif \ No newline at end of file +#endif diff --git a/Core/Src/Examples/ExamplesHardFault.cpp b/Core/Src/Examples/ExamplesHardFault.cpp index 903222ee..0b2eb1f1 100644 --- a/Core/Src/Examples/ExamplesHardFault.cpp +++ b/Core/Src/Examples/ExamplesHardFault.cpp @@ -56,4 +56,4 @@ int main(void) { } #endif -#endif \ No newline at end of file +#endif diff --git a/Core/Src/Runes/generated_metadata.cpp b/Core/Src/Runes/generated_metadata.cpp index fe7c9755..6350bc43 100644 --- a/Core/Src/Runes/generated_metadata.cpp +++ b/Core/Src/Runes/generated_metadata.cpp @@ -12,4 +12,4 @@ const char DESCRIPTION[255] __attribute__((section(".metadata_pool"))) = "4b3d0ec4" // Board commit // the '=' is used for unparsing ; -} \ No newline at end of file +} diff --git a/Core/Src/config/leds_hard_fault.cpp b/Core/Src/config/leds_hard_fault.cpp index be2d3a0e..2494f3ca 100644 --- a/Core/Src/config/leds_hard_fault.cpp +++ b/Core/Src/config/leds_hard_fault.cpp @@ -21,4 +21,4 @@ uint8_t hard_fault_leds_count = ? sizeof(pins_hard_fault) / sizeof(uint16_t) : 0; #endif -} \ No newline at end of file +} diff --git a/Core/Src/syscalls.c b/Core/Src/syscalls.c index 34280458..6b35c655 100644 --- a/Core/Src/syscalls.c +++ b/Core/Src/syscalls.c @@ -149,4 +149,4 @@ int _execve(char* name, char** argv, char** env) { return -1; } -int _getentropy(void* buffer, size_t length) { return -ENOSYS; } \ No newline at end of file +int _getentropy(void* buffer, size_t length) { return -ENOSYS; } diff --git a/README.md b/README.md index 70aa7730..a5f9b940 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,52 @@ # Template Project -HyperloopUPV's Template project for STM32 development with LwIP. +HyperloopUPV STM32 firmware template based on CMake + VSCode, using `deps/ST-LIB`. -Designed using CMake with defaults for development with VSCode. +## Quickstart -This makes use of the [ST-LIB](https://github.com/HyperloopUPV-H8/ST-LIB) developed by HyperloopUPV's team. +```sh +./tools/init.sh +cmake --preset simulator +cmake --build --preset simulator +ctest --preset simulator-all +``` -## Container Setup -To use it you must install [Dev Containers extension on VSCode](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) and [Docker](https://www.docker.com). Be careful to use the instructions related to your OS, as docker not works in the same way exactly between them. -Then, when you open this folder in VSCode, you will have the ability to reopen it inside the container. Don't worry, the first time you do it takes a loong time. -## SetUp -Create a virtual environment with ->python3 -m venv virtual +## Documentation -at the root of the project -## Modes -### Simulator -The container is fully ready to develop, compile and debug the code in simulator mode, so you don't have to worry about setting your environment -### MCU -The container is ready to develop and compile the code in MCU mode, but it couldn't be possible to flash and debug through it, so you should do it in your local machine. To do it, you must install the [STM32CubeCLT](https://www.st.com/en/development-tools/stm32cubeclt.html). -To flash and debug, you should be outside the container, and use the [Cortex-Debug VSCode extension](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug) +- Template setup: [`docs/template-project/setup.md`](docs/template-project/setup.md) +- Build and debug: [`docs/template-project/build-debug.md`](docs/template-project/build-debug.md) +- Testing and quality: [`docs/template-project/testing.md`](docs/template-project/testing.md) +- ST-LIB docs (inside this repository): [`deps/ST-LIB/docs/setup.md`](deps/ST-LIB/docs/setup.md) +## Main Working Modes -## Notes -If you are going to develop OUTSIDE the container, you MUST change `template-project.code-workspace` file after project creation if you DON'T have STLIB on ../ST-LIB relative path. This template is intended for you to develop always inside the container, so you should not be concerned about this. +- `simulator`: fast local development and tests. +- `nucleo-*` / `board-*`: hardware builds. -## Automatic formatting (commit/push) -This repository uses `pre-commit` + `clang-format` to format C/C++ files automatically on `git commit` and `git push`. +List all presets: -Install hooks once: ```sh -pip install pre-commit -./tools/install-git-hooks.sh +cmake --list-presets ``` -Run manually on all files: +## VSCode Debug + +`launch.json` and `tasks.json` include debug flows for: + +- OpenOCD +- ST-LINK +- simulator tests + +Detailed guide: + +- [`docs/template-project/build-debug.md`](docs/template-project/build-debug.md) + +## `BOARD_NAME` (code generation) + +Packet code generation uses `BOARD_NAME` (a key from JSON_ADE). + +Example: + ```sh -pre-commit run --all-files +cmake --preset board-debug -DBOARD_NAME=TEST ``` diff --git a/STM32H723.svd b/STM32H723.svd index 97fcec91..78eb25e2 100644 --- a/STM32H723.svd +++ b/STM32H723.svd @@ -28,9 +28,9 @@ Copyright (c) 2021 STMicroelectronics. true 4 false - - 8 - 32 + + 8 + 32 0x20 0x0 0xFFFFFFFF @@ -4340,8 +4340,8 @@ Note: The software is allowed to write these bits only when ADEN=1, ADSTART=0 an ADC2 - 0x40022100 - + 0x40022100 + ADC12_Common Analog-to-Digital Converter @@ -4626,9 +4626,9 @@ Note: The software is allowed to write these bits only when ADEN=1, ADSTART=0 an - - - + + + ADC3 Analog-to-Digital Converter ADC @@ -8435,10 +8435,10 @@ Note: The software is allowed to write these bits only when ADEN = 1, ADSTART = read-write - + - + ADC3_Common Analog-to-Digital Converter ADC @@ -11592,7 +11592,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register. - + DAC DAC @@ -12332,7 +12332,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.Allow D1 domain debug in Standby mode 2 1 - + DBGSLPD2 Allow D2 domain debug in Sleep mode @@ -12362,7 +12362,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.Allow debug in D3 Standby mode 8 1 - + TRACECLKEN Trace port clock enable @@ -12386,7 +12386,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.External trigger output enable 28 1 - + @@ -12403,9 +12403,9 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.WWDG1 stop in debug 6 1 - + - + APB3FZ2 APB3FZ2 @@ -12420,9 +12420,9 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.WWDG1 stop in debug 6 1 - + - + APB1LFZ1 APB1LFZ1 @@ -12497,7 +12497,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.WWDG2 stop in debug 11 1 - + DBG_I2C1 I2C1 SMBUS timeout stop in debug @@ -12515,9 +12515,9 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register. I2C3 SMBUS timeout stop in debug 23 1 - + - + APB1LFZ2 APB1LFZ2 @@ -12610,9 +12610,9 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register. I2C3 SMBUS timeout stop in debug 23 1 - + - + APB2FZ1 APB2FZ1 @@ -12651,9 +12651,9 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register. TIM17 stop in debug 18 1 - + - + APB2FZ2 APB2FZ2 @@ -12692,9 +12692,9 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.TIM17 stop in debug 18 1 - + - + APB4FZ1 APB4FZ1 @@ -12751,7 +12751,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register. Independent watchdog for D2 stop in debug 19 1 - + @@ -12810,9 +12810,9 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register. LS watchdog for D2 stop in debug 19 1 - + - + @@ -13303,7 +13303,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register. - + DELAY_Block_SDMMC1 DELAY_Block_SDMMC1 DLYB @@ -13826,7 +13826,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.6 - + CH2CFGR1 CH2CFGR1 @@ -14000,7 +14000,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.6 - + CH3CFGR1 CH3CFGR1 @@ -14174,7 +14174,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.6 - + CH4CFGR1 CH4CFGR1 @@ -14348,7 +14348,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.6 - + CH5CFGR1 CH5CFGR1 @@ -14522,7 +14522,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.6 - + CH6CFGR1 CH6CFGR1 @@ -14696,7 +14696,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.6 - + CH7CFGR1 CH7CFGR1 @@ -14870,7 +14870,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.6 - + DFSDM_FLT0CR1 DFSDM_FLT0CR1 @@ -23406,7 +23406,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register. Delay_Block_OCTOSPI1 0x52006000 - + Delay_Block_OCTOSPI2 0x5200B000 @@ -23461,7 +23461,7 @@ A read from this register resets the RRDY flag in the CORDIC_CSR register.EXTI15_10 EXTI Line[15:10] interrupts 40 - + WKUP WKUP1 to WKUP6 pins @@ -40618,7 +40618,7 @@ When a read access to this register occurs, the read data are the contents of th GPIOH 0x58021C00 - + GPIOJ 0x58022400 @@ -59503,7 +59503,7 @@ When a read access to this register occurs, the read data are the contents of th - + MPU Memory protection unit @@ -61422,12 +61422,12 @@ When a read access to this register occurs, the read data are the contents of th 0x0 0x1000 registers - + OCTOSPI1 OCTOSPI1 global interrupt 92 - + CR @@ -62337,7 +62337,7 @@ When a read access to this register occurs, the read data are the contents of th - OCTOSPI2 + OCTOSPI2 0x5200A000 OCTOSPI2 @@ -73654,7 +73654,7 @@ When a read access to this register occurs, the read data are the contents of th - + OTG2_HS_PWRCLK 0x40080E00 @@ -86289,7 +86289,7 @@ Writing this bit to 1 clears the OVR_RIS bit in PSSI_RIS. SAI4 SAI4 global interrupt 146 - + SCB @@ -87006,7 +87006,7 @@ Writing this bit to 1 clears the OVR_RIS bit in PSSI_RIS. - + SDMMC1 SDMMC1 SDMMC @@ -88417,7 +88417,7 @@ Writing this bit to 1 clears the OVR_RIS bit in PSSI_RIS. SDMMC2 - 0x48022400 + 0x48022400 SDMMC2 SDMMC2 global interrupt @@ -88816,7 +88816,7 @@ Writing this bit to 1 clears the OVR_RIS bit in PSSI_RIS. 13 - + @@ -95890,7 +95890,7 @@ Writing this bit to 1 clears the OVR_RIS bit in PSSI_RIS. - + TIM23 0x4000E000 @@ -95908,7 +95908,7 @@ Writing this bit to 1 clears the OVR_RIS bit in PSSI_RIS. TIM24 global interrupt 162 - + USART1 Universal synchronous asynchronous receiver @@ -96972,7 +96972,7 @@ Writing this bit to 1 clears the OVR_RIS bit in PSSI_RIS. UART9 0x40011800 - + USART10 0x40011C00 @@ -97171,4 +97171,4 @@ Writing this bit to 1 clears the OVR_RIS bit in PSSI_RIS. - \ No newline at end of file + diff --git a/deps/ST-LIB b/deps/ST-LIB index 96abc13c..65d36f43 160000 --- a/deps/ST-LIB +++ b/deps/ST-LIB @@ -1 +1 @@ -Subproject commit 96abc13c0d2f657ce658c76b98eff57c44a1aacf +Subproject commit 65d36f43533e461defac8e84f3e4f7a8ae62a304 diff --git a/docs/template-project/build-debug.md b/docs/template-project/build-debug.md new file mode 100644 index 00000000..8599bd85 --- /dev/null +++ b/docs/template-project/build-debug.md @@ -0,0 +1,59 @@ +# Build and Debug + +## 1. Main CMake Presets + +- `simulator` +- `simulator-asan` +- `nucleo-*` +- `board-*` + +List all presets: + +```sh +cmake --list-presets +``` + +## 2. Build for Simulator + +```sh +cmake --preset simulator +cmake --build --preset simulator +``` + +With sanitizers: + +```sh +cmake --preset simulator-asan +cmake --build --preset simulator-asan +``` + +## 3. Build for MCU + +Example: + +```sh +cmake --preset board-debug-eth-ksz8041 -DBOARD_NAME=TEST +cmake --build --preset board-debug-eth-ksz8041 +``` + +The build output is copied to: + +- `out/build/latest.elf` + +## 4. Debug from VSCode + +Launch configurations available in `.vscode/launch.json`: + +- `MCU | OpenOCD | Build + Debug (RTT)` +- `MCU | OpenOCD | Debug (No Build, RTT)` +- `MCU | OpenOCD | Attach (External Server, RTT)` +- `MCU | ST-LINK | Build + Debug` +- `MCU | ST-LINK | Debug (No Build)` +- `MCU | ST-LINK | Attach (External GDB Server)` +- `SIM | ST-LIB Tests | Debug` + +Useful tasks in `.vscode/tasks.json`: + +- `MCU | OpenOCD | Start Server` +- `MCU | OpenOCD | RTT Console` +- `MCU | ST-LINK | Start GDB Server` diff --git a/docs/template-project/setup.md b/docs/template-project/setup.md new file mode 100644 index 00000000..530d2933 --- /dev/null +++ b/docs/template-project/setup.md @@ -0,0 +1,47 @@ +# Template Project Setup + +## 1. Prerequisites + +- VSCode (recommended) +- CMake + Ninja +- Python 3 + +For MCU build/flash/debug: + +- `STM32CubeCLT` +- `openocd` or `ST-LINK_gdbserver` +- `arm-none-eabi-gdb` + +## 2. Quick Initialization + +From the repository root: + +```sh +./tools/init.sh +``` + +This command: + +- creates `virtual/` +- installs Python dependencies from `requirements.txt` +- initializes template and `deps/ST-LIB` submodules + +On Windows: + +```bat +tools\init.bat +``` + +## 3. `BOARD_NAME` Configuration (codegen) + +Code generation uses `BOARD_NAME` (CMake cache variable), and the value must exist in: + +- `Core/Inc/Code_generation/JSON_ADE/boards.json` + +Example: + +```sh +cmake --preset board-debug -DBOARD_NAME=TEST +``` + +If not set, the default value is `TEST`. diff --git a/docs/template-project/testing.md b/docs/template-project/testing.md new file mode 100644 index 00000000..51c7522f --- /dev/null +++ b/docs/template-project/testing.md @@ -0,0 +1,46 @@ +# Testing and Quality + +## 1. Local Simulator Tests + +```sh +cmake --preset simulator +cmake --build --preset simulator +ctest --preset simulator-all +``` + +Run only ADC tests: + +```sh +ctest --preset simulator-adc +``` + +## 2. Tests with Sanitizers + +```sh +cmake --preset simulator-asan +cmake --build --preset simulator-asan +ctest --preset simulator-all-asan +``` + +## 3. Formatting + +This repository uses `pre-commit` and `clang-format`. + +Install hooks: + +```sh +pip install pre-commit +pre-commit install --install-hooks --hook-type pre-commit --hook-type pre-push +``` + +Run manually: + +```sh +pre-commit run --all-files +``` + +## 4. GitHub Actions CI + +- `Compile Checks`: builds MCU matrix (no simulator tests) +- `Run Simulator Tests`: runs tests using `simulator` preset +- `Format Checks`: validates formatting with `pre-commit` diff --git a/hard_faullt_analysis.py b/hard_faullt_analysis.py index 35e6bfc9..233eb2c0 100644 --- a/hard_faullt_analysis.py +++ b/hard_faullt_analysis.py @@ -60,7 +60,7 @@ def decode_cfsr_bus(cfsr, fault_addr): print(f" BFARVALID : Bus fault address valid -> 0x{fault_addr:08X}") if bus_fault & 0b00000100: print(f"\033[91m Bus fault address imprecise\033[0m (DON'T LOOK CALL STACK)") - + if bus_fault & 0b00100000: print(" LSPERR : Floating Point Unit lazy state preservation error") if bus_fault & 0b00010000: @@ -97,8 +97,8 @@ def decode_cfsr(cfsr, fault_addr): error = decode_cfsr_bus(cfsr, fault_addr) + error error = decode_cfsr_usage(cfsr) + error return error - - + + def addr2line(addr): cmd = ["arm-none-eabi-addr2line", "-e", ELF_FILE, "-f", "-C", hex(addr)] try: @@ -106,7 +106,7 @@ def addr2line(addr): return output except Exception as e: return f"addr2line failed: {e}" - + def analyze_call_stack(calltrace_depth, calltrace_pcs, context=2): """ Muestra el call stack, omitiendo frames sin fuente y mostrando snippet de código. @@ -149,7 +149,7 @@ def print_code_context(lines, context=2): print("Invalid addr2line output") return - file_line = line_list[1].strip() + file_line = line_list[1].strip() split = file_line.rfind(':') file_path = file_line[:split] try: @@ -192,38 +192,38 @@ def hard_fault_analysis(memory_string): "cfsr": raw[9], "fault_addr": raw[10], "calltrace_depth": raw[11], - "calltrace_pcs": raw[12:28] + "calltrace_pcs": raw[12:28] } if(hf["HF_Flag"] != 0xFF00FF00): print("There was no hardfault in your Microcontroller, Kudos for you, I hope...") return print("================HARDFAULT DETECTED ===========") print("Registers:") - + for r in ['r0','r1','r2','r3','r12','lr','pc','psr']: print(f" {r.upper():<4}: 0x{hf[r]:08X}") - + print(f" CFSR: 0x{hf['cfsr']:08X}") error = decode_cfsr(hf["cfsr"], hf["fault_addr"]) print("\nSource Location:") pc_loc = addr2line(hf["pc"]) lr_loc = addr2line(hf["lr"]) print(f" Linker Register : 0x{hf['lr']:08X} -> {lr_loc}") - + print(f" Program Counter : 0x{hf['pc']:08X} -> {pc_loc}") print_code_context(pc_loc) - + analyze_call_stack(hf["calltrace_depth"],hf["calltrace_pcs"]) - + print("======================================================") - - + + print("Note: In Release builds (-O2/-O3) the PC may not point exactly to the failing instruction.") print(" During interrupts, bus faults, or stack corruption, the PC can be imprecise.") print("\nIn case of Imprecise error is dificult to find due to is asynchronous fault") print("The error has to be before PC. But not possible to know exactly when.") print("Check this link to know more : https://interrupt.memfault.com/blog/cortex-m-hardfault-debug#fn:8") - + if __name__ == '__main__': out = read_flash() @@ -241,4 +241,3 @@ def hard_fault_analysis(memory_string): memory_string += mem memory_string = memory_string.replace(" ","") hard_fault_analysis(memory_string) - diff --git a/requirements.txt b/requirements.txt index e9218ce9..97547e29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ Jinja2==3.1.5 -GitPython==3.1.43 \ No newline at end of file +GitPython==3.1.43 diff --git a/state_machine.json b/state_machine.json index a5740155..3aabd215 100644 --- a/state_machine.json +++ b/state_machine.json @@ -1,6 +1,6 @@ { "name" : "Example", - + "states" : [ "name_1", { @@ -119,4 +119,4 @@ } ] } -} \ No newline at end of file +} diff --git a/toolchains/stm32.cmake b/toolchains/stm32.cmake index 9893d325..0c1e0047 100644 --- a/toolchains/stm32.cmake +++ b/toolchains/stm32.cmake @@ -29,4 +29,3 @@ set(CMAKE_FIND_ROOT_PATH ${BINUTILS_PATH}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - diff --git a/tools/binary_metadata_template.cpp b/tools/binary_metadata_template.cpp index d8f838da..d038c950 100644 --- a/tools/binary_metadata_template.cpp +++ b/tools/binary_metadata_template.cpp @@ -1,9 +1,9 @@ /* -* AUTOGENERATED FILE +* AUTOGENERATED FILE * DO NOT MODIFY MANUALLY!!! */ extern "C"{ - const char DESCRIPTION[255] __attribute__((section(".metadata_pool")))= + const char DESCRIPTION[255] __attribute__((section(".metadata_pool")))= "****************" // placeholder for beggining "{{ DateTimeISO8601 }}" // DateTime using ISO-8601 format " " // alignment @@ -12,6 +12,6 @@ extern "C"{ "{{ BOARD_COMMIT }}" // Board commit // the '=' is used for unparsing {% for var_pair in variables -%} - "{{var_pair.name}}={{var_pair.value}}" + "{{var_pair.name}}={{var_pair.value}}" {% endfor %}; -} \ No newline at end of file +} diff --git a/tools/generate_binary_metadata.py b/tools/generate_binary_metadata.py index 62b4a10d..3350ea36 100644 --- a/tools/generate_binary_metadata.py +++ b/tools/generate_binary_metadata.py @@ -1,82 +1,91 @@ -from jinja2 import Environment, FileSystemLoader +#!/usr/bin/env python3 from datetime import datetime -from git import Repo from pathlib import Path import os +import re + +from git import Repo +from jinja2 import Environment, FileSystemLoader + +MACRO_PATTERN = re.compile(r"ADD_TO_FLASH_INFO\(([^)]+)\)") +ACCEPTED_EXTENSIONS = {".cpp", ".c", ".h", ".hpp"} + -def __get_git_root__(): - # Start at the script's directory and walk up to find the .git folder +class Variable: + def __init__(self, name: str, value: str): + self.name = name + self.value = value + + +def get_git_root() -> Path: repo_root = Path(__file__).resolve() while not (repo_root / ".git").exists() and repo_root != repo_root.parent: repo_root = repo_root.parent + if not (repo_root / ".git").exists(): + raise RuntimeError("Could not find repository root (.git)") + return repo_root - return str(repo_root) if (repo_root / ".git").exists() else None -def __get_current_commit__(path): +def get_current_commit(path: Path) -> str: try: repo = Repo(path, search_parent_directories=True) - return repo.head.commit.hexsha[:8] # Get the current commit hash + return repo.head.commit.hexsha[:8] except Exception: - return "--------" # not using that repo, return placeholder for commit - -class Variable: - def __init__(self,name,value): - self.name = name - self.value = value -variables = [] + return "--------" + -def parse_file(file_path): - with open(file_path, 'r') as file: +def parse_file(file_path: Path, variables: list[Variable]) -> None: + with file_path.open("r", encoding="utf-8", errors="ignore") as file: for line in file: - # Look for the expression in the line - if "ADD_TO_FLASH_INFO" in line: - # Remove the 'ADD_TO_FLASH_INFO(' and ')' - line = line.strip()[len("ADD_TO_FLASH_INFO("):-1] - # Split the string by commas - parts = line.split(',') - # Ensure that we have exactly three parts (TYPE, VAR_NAME, VALUE) - if len(parts) == 3: - var_name = parts[1].strip() # Extract VAR_NAME - value = parts[2].strip() # Extract VALUE - if var_name == 'VAR_NAME' and value == 'VALUE)': #make sure to not add the MACRO definition - #in case it was present in that file - pass - else: - variables.append(Variable(var_name, value)) -print("Starting binary metadata generation") - -def search_in_subfolder(root_folder): - accepted_file_extensions = ['.cpp','.c','.h','.hpp'] - for folder_name, subfolders, filenames in os.walk(root_folder): + match = MACRO_PATTERN.search(line) + if not match: + continue + + parts = [part.strip() for part in match.group(1).split(",")] + if len(parts) != 3: + continue + + var_name = parts[1] + value = parts[2] + if var_name == "VAR_NAME" and value == "VALUE": + continue + variables.append(Variable(var_name, value)) + + +def search_core_sources(core_root: Path, variables: list[Variable]) -> None: + for folder_name, _subfolders, filenames in os.walk(core_root): for filename in filenames: - for extension in accepted_file_extensions: - if filename.endswith(extension): - file_path = os.path.join(folder_name, filename) - parse_file(file_path) -def main(): - repo_root = __get_git_root__() - search_in_subfolder(os.path.join(repo_root,"Core/")) + path = Path(folder_name) / filename + if path.suffix in ACCEPTED_EXTENSIONS: + parse_file(path, variables) - environment = Environment(loader=FileSystemLoader(os.path.join(repo_root,'tools'))) - template = environment.get_template(os.path.join('binary_metadata_template.cpp')) - iso_time = datetime.now().strftime("%Y%m%dT%H%M%S") - repo_root = __get_git_root__() - stlib_commit = __get_current_commit__(os.path.join(repo_root,"deps/ST-LIB")) - adj_commit = __get_current_commit__(os.path.join(repo_root,"deps/adj")) ## keep an eye on this - board_commit = __get_current_commit__(repo_root) +def main() -> None: + print("Starting binary metadata generation") + repo_root = get_git_root() + variables: list[Variable] = [] + search_core_sources(repo_root / "Core", variables) - output_file = os.path.join(repo_root,"Core/Src/Runes/generated_metadata.cpp") + environment = Environment(loader=FileSystemLoader(str(repo_root / "tools"))) + template = environment.get_template("binary_metadata_template.cpp") + + iso_time = datetime.now().strftime("%Y%m%dT%H%M%S") + stlib_commit = get_current_commit(repo_root / "deps/ST-LIB") + adj_commit = get_current_commit(repo_root / "deps/adj") + board_commit = get_current_commit(repo_root) + + output_file = repo_root / "Core/Src/Runes/generated_metadata.cpp" content = template.render( DateTimeISO8601=iso_time, - STLIB_COMMIT = stlib_commit, - ADJ_COMMIT = adj_commit, - BOARD_COMMIT = board_commit, - variables=variables) - with open(output_file, mode="w", encoding="utf-8") as message: - message.write(content) - + STLIB_COMMIT=stlib_commit, + ADJ_COMMIT=adj_commit, + BOARD_COMMIT=board_commit, + variables=variables, + ) + output_file.write_text(content, encoding="utf-8") print("Generation completed") + + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/tools/init.bat b/tools/init.bat index a7edc385..59f1e439 100644 --- a/tools/init.bat +++ b/tools/init.bat @@ -1,6 +1,6 @@ @echo off setlocal enabledelayedexpansion -# This script is supposed to be under ${REPO_PATH}/tools/init.bat +REM This script is supposed to be under ${REPO_PATH}/tools/init.bat REM Get the script directory and repo directory set "SCRIPT_DIR=%~dp0" @@ -19,9 +19,16 @@ REM Activate virtual environment call virtual\Scripts\activate.bat REM Install requirements +python -m pip install --upgrade pip pip install -r requirements.txt REM Update git submodules git submodule update --init +where bash >nul 2>&1 +if %errorlevel%==0 ( + bash -lc "./deps/ST-LIB/tools/init-submodules.sh" +) else ( + echo bash was not found in PATH. Run deps/ST-LIB/tools/init-submodules.sh manually. +) echo Setup complete! diff --git a/tools/init.sh b/tools/init.sh index 872b6705..7bcdb9b0 100755 --- a/tools/init.sh +++ b/tools/init.sh @@ -12,6 +12,10 @@ echo "Repository directory: $REPO_DIR" cd "$REPO_DIR" python3 -m venv virtual source ./virtual/bin/activate +python -m pip install --upgrade pip pip install -r requirements.txt git submodule update --init +./deps/ST-LIB/tools/init-submodules.sh + +echo "Setup complete." diff --git a/tools/preflash_check.py b/tools/preflash_check.py index 0a4d4639..314125aa 100644 --- a/tools/preflash_check.py +++ b/tools/preflash_check.py @@ -1,12 +1,11 @@ #!/usr/bin/env python3 """ Pre-flash check script. -Checks for uncommitted changes and commits them if the binary was compiled with BOARD symbol. +Checks for uncommitted changes when the binary was compiled with BOARD symbol. """ import subprocess import sys -import os from pathlib import Path def get_script_dir() -> Path: @@ -32,30 +31,30 @@ def has_uncommitted_changes(workspace_dir: Path) -> bool: def main(): workspace_dir = get_workspace_dir() - + # Default build directory - can be overridden by command line argument if len(sys.argv) > 1: build_dir = Path(sys.argv[1]) else: build_dir = workspace_dir / "out" / "build" - + print(f"Workspace directory: {workspace_dir}") print(f"Build directory: {build_dir}") - + # Check if this is a BOARD build if not check_board_symbol(build_dir): print("Binary was not compiled with BOARD symbol (or marker not found). Skipping pre-flash check.") sys.exit(0) - + print("Binary was compiled with BOARD symbol. Checking for uncommitted changes...") - + if has_uncommitted_changes(workspace_dir): print("Uncommitted changes detected. Aborting Flash...") print("Please before flashing a board make sure all changes are commited"); sys.exit(1) else: print("No uncommitted changes. Proceeding with flash.") - + sys.exit(0) if __name__ == "__main__": diff --git a/tools/retrieve_flash_data.py b/tools/retrieve_flash_data.py index 1ebf07d9..902ba80b 100644 --- a/tools/retrieve_flash_data.py +++ b/tools/retrieve_flash_data.py @@ -1,77 +1,113 @@ -import subprocess +#!/usr/bin/env python3 import argparse -from datetime import datetime import os -def main(): - parser = argparse.ArgumentParser(description="Retrieve the binary metadata from flash given an address") +import subprocess +from datetime import datetime +from pathlib import Path + + +DUMP_FILE = Path("dump.bin") + + +def read_metadata(address: str) -> bytes: + result = subprocess.run( + [ + "STM32_Programmer_CLI", + "-c", + "port=swd", + "mode=ur", + "Freq=4000", + "-u", + address, + "0xFF", + str(DUMP_FILE), + ], + check=False, + ) + if result.returncode != 0: + if DUMP_FILE.exists(): + DUMP_FILE.unlink() + raise RuntimeError( + "Error running STM32_Programmer_CLI. Ensure board power, cable and ST-LINK availability." + ) + return DUMP_FILE.read_bytes() + + +def validate_checksum(binary_raw: bytes, address: str, checksum_length: int) -> None: + if len(binary_raw) < checksum_length: + raise RuntimeError(f"Retrieved data is too short (address: {address})") + if any(byte != ord("*") for byte in binary_raw[:checksum_length]): + raise RuntimeError( + f"Retrieved binary did not pass checksum test. Maybe metadata is not at {address}" + ) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Retrieve binary metadata from flash given an address" + ) parser.add_argument( - "--address", # Argument name - type=str, # Argument type (string) - default="0x080DFD00", # Default value if no argument is provided - help="The memory address to retrieve from, if unsure leave empty" # Help text + "--address", + type=str, + default="0x080DFD00", + help="Memory address to read from", ) args = parser.parse_args() + try: - result = subprocess.run(["STM32_Programmer_CLI", "-c", "port=swd", "mode=ur", "Freq=4000", "-u", args.address, "0xFF", "dump.bin"]) - if result.returncode != 0: - #if we tried to read and it failed, remove the - # existing file to avoid confussion - os.remove("dump.bin") - raise Exception() - except Exception: - print("Error when running STM32_Programmer_CLI, make sure the board is powered and the cable connected,\nalso make sure STLINK is not in use by the debugger or STM32CubeProgrammer") - exit(-1) - - binary_raw = None - - with open("dump.bin","r") as binary: - binary_raw = binary.read() - - #DEFINITIONS FOR LOCATIONS + binary_raw = read_metadata(args.address) + except Exception as exc: + print(exc) + raise SystemExit(1) + checksum_length = 16 iso_time_offset = checksum_length iso_time_length = 15 padding_length = 1 stlib_commit_offset = iso_time_offset + iso_time_length + padding_length stlib_commit_length = 8 - adj_commit_offset = stlib_commit_offset + stlib_commit_length adj_commit_length = 8 - board_commit_offset = adj_commit_offset + adj_commit_length - board_commit_legnth = 8 - - custom_variables_offset = board_commit_offset + board_commit_legnth + board_commit_length = 8 + custom_variables_offset = board_commit_offset + board_commit_length - def __validate_retrieved_binary(): - for i in range(0,checksum_length): - if binary_raw[i] != '*': - raise Exception("Retrieved binary did not pass checksum test\n Maybe the metadata is not at that address? ADDRESS: {}".format(args.address)) - return True - if __validate_retrieved_binary() == True: - print("Found binary metadata!") + try: + validate_checksum(binary_raw, args.address, checksum_length) + except Exception as exc: + print(exc) + raise SystemExit(1) - iso_time = binary_raw[iso_time_offset:iso_time_offset + iso_time_length] - stlib_commit = binary_raw[stlib_commit_offset:stlib_commit_offset + stlib_commit_length] - adj_commit = binary_raw[adj_commit_offset:adj_commit_offset + adj_commit_length] - board_commit = binary_raw[board_commit_offset:board_commit_offset + board_commit_legnth] + print("Found binary metadata!") + iso_time = binary_raw[iso_time_offset : iso_time_offset + iso_time_length].decode( + errors="ignore" + ) + stlib_commit = binary_raw[ + stlib_commit_offset : stlib_commit_offset + stlib_commit_length + ].decode(errors="ignore") + adj_commit = binary_raw[ + adj_commit_offset : adj_commit_offset + adj_commit_length + ].decode(errors="ignore") + board_commit = binary_raw[ + board_commit_offset : board_commit_offset + board_commit_length + ].decode(errors="ignore") dt = datetime.strptime(iso_time, "%Y%m%dT%H%M%S") - # Format to a readable format (e.g., "March 2, 2025, 1:12:41 PM") - readable_time = dt.strftime("%d %B %Y, %H:%M:%S") # 24-hour format (%H) + readable_time = dt.strftime("%d %B %Y, %H:%M:%S") + + print(f"Code was compiled at: {readable_time}") + print(f"STLIB commit {stlib_commit}") + print(f"ADJ commit {adj_commit}") + print(f"Board commit {board_commit}") + custom_payload = binary_raw[custom_variables_offset:255].decode(errors="ignore") + print(custom_payload.rstrip("\x00")) - print("Code was compiled at: {}".format(readable_time)) - print("STLIB commit {}".format(stlib_commit)) - print("ADJ commit {}".format(adj_commit)) - print("Board commit {}".format(board_commit)) + # Keep compatibility with previous behavior: cleanup temporary dump. + if DUMP_FILE.exists(): + os.remove(DUMP_FILE) - for i in range(custom_variables_offset, 255): - if binary_raw[i].isdigit() == True and binary_raw[i+1].isdigit() == False: - print(binary_raw[i],end="\n") - else: - print(binary_raw[i],end="") if __name__ == "__main__": - main() \ No newline at end of file + main()