diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c8e634..83b7b9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ set(DOC_PATH ${ROOT_DIR}/docs/${CAN_PROTOCOL}/README.md) option(USE_PLATFORM_UBUNTU "Build for SITL (Software In The Loop)" OFF) option(USE_PLATFORM_NODE_V2 "Build for stm32f103xB (128 KBytes)" OFF) option(USE_PLATFORM_NODE_V3 "Build for stm32g0b1 (512 KBytes)" OFF) +option(USE_PLATFORM_BOOTLOADER_NODE_V3 "Build for stm32g0b1 (512 KBytes)" OFF) if(USE_PLATFORM_UBUNTU) set(LIBPARAMS_PLATFORM ubuntu) @@ -32,6 +33,13 @@ elseif(USE_PLATFORM_NODE_V3) set(PLATFORM_NAME v3) include(${CMAKE_DIR}/stm32g0b1.cmake) add_compile_definitions(CYPHAL_NUM_OF_CAN_BUSES=2) +elseif(USE_PLATFORM_BOOTLOADER_NODE_V3) + set(LIBPARAMS_PLATFORM stm32g0b1) + set(CAN_PLATFORM fdcan) + set(APP_PLATFORM stm32g0b1) + set(PLATFORM_NAME v3) + include(${CMAKE_DIR}/stm32g0b1_bootloader.cmake) + add_compile_definitions(CYPHAL_NUM_OF_CAN_BUSES=1) else() message(SEND_ERROR "Platform Error: Either v2 (stm32f103), v3 (stm32g0) or SITL (Linux) should be specified.") endif() @@ -44,9 +52,18 @@ endif() # Set build dir based on hardware version and protocol set(BUILD_ROOT_DIR ${ROOT_DIR}/build) +if(DEFINED BUILD_OBJ_DIR) + cmake_path(GET BUILD_OBJ_DIR PARENT_PATH BUILD_TARGET_DIR) + set(BUILD_SRC_DIR ${BUILD_TARGET_DIR}/src) +else() set(BUILD_TARGET_DIR ${BUILD_ROOT_DIR}/${CAN_PROTOCOL}_${PLATFORM_NAME}) set(BUILD_SRC_DIR ${BUILD_TARGET_DIR}/src) set(BUILD_OBJ_DIR ${BUILD_TARGET_DIR}/obj) +endif() + +message(STATUS "BUILD_TARGET_DIR is set to ${BUILD_TARGET_DIR}") +message(STATUS "BUILD_OBJ_DIR is set to ${BUILD_OBJ_DIR}") +message(STATUS "BUILD_SRC_DIR is set to ${BUILD_SRC_DIR}") if(USE_PLATFORM_UBUNTU) add_definitions(-DLIBPARAMS_PARAMS_DIR="${BUILD_SRC_DIR}") @@ -60,7 +77,7 @@ list(APPEND APPLICATION_HEADERS ${ROOT_DIR}/Src ${APPLICATION_DIR}) include(${APPLICATION_DIR}/CMakeLists.txt) # Set compile options -set(WARNING_FLAGS "-Wall -Wextra -Wfloat-equal -Werror -Wundef -Wshadow -Wpointer-arith -Wunreachable-code -Wstrict-overflow=5 -Wwrite-strings -Wswitch-default") +set(WARNING_FLAGS "-Wall -Wextra -Wfloat-equal -Werror -Wundef -Wno-error=attributes -Wshadow -Wpointer-arith -Wunreachable-code -Wstrict-overflow=5 -Wwrite-strings -Wswitch-default") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS} -Wno-volatile") set(CMAKE_CXX_STANDARD 20) diff --git a/Makefile b/Makefile index b44e9ad..3d1968a 100644 --- a/Makefile +++ b/Makefile @@ -48,6 +48,14 @@ v3: checks generate_dsdl clean mkdir -p ${BUILD_DIR}/both_v3/obj cd ${BUILD_DIR}/both_v3/obj && cmake -DCAN_PROTOCOL=both -DUSE_PLATFORM_NODE_V3=ON -G "Unix Makefiles" ../../.. && make +# Bootloader +bootloader: bootloader_v3 +bootloader_v3: generate_dsdl + ./scripts/install_bootloader.sh + + mkdir -p ${BUILD_DIR}/bootloader_v3/obj + cd ${BUILD_DIR}/bootloader_v3/obj && cmake -DCAN_PROTOCOL=bootloader -DUSE_PLATFORM_BOOTLOADER_NODE_V3=ON -DBUILD_OBJ_DIR=${BUILD_DIR}/bootloader_v3/obj -G "Unix Makefiles" ../../.. && make + # Common: checks: @python scripts/prebuild_check.py || (echo "Requirements verification failed. Stopping build." && exit 1) diff --git a/Src/applications/bootloader/CMakeLists.txt b/Src/applications/bootloader/CMakeLists.txt new file mode 100644 index 0000000..785de91 --- /dev/null +++ b/Src/applications/bootloader/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright (C) 2023 Dmitry Ponomarev +# Distributed under the terms of the GPL v3 license, available in the file LICENSE. + +set(APPLICATION_DIR ${CMAKE_CURRENT_LIST_DIR}) +cmake_path(GET CMAKE_CURRENT_LIST_DIR PARENT_PATH APPLICATIONS_DIR) +cmake_path(GET APPLICATIONS_DIR PARENT_PATH SRC_DIR) + +add_definitions(-DRELEASE_BUILD=1) + + +include(${SRC_DIR}/modules/bootloader/CMakeLists.txt) +# include(${SRC_DIR}/modules/cyphal/core/CMakeLists.txt) +include(${SRC_DIR}/modules/system/CMakeLists.txt) + +list(APPEND APPLICATION_SOURCES + ${libparamsSrc} + ${ROOT_DIR}/Libs/Cyphal/Libs/o1heap/o1heap/o1heap.c + ${ROOT_DIR}/Libs/Cyphal/platform_specific/fdcan/cyphal_transport_can.cpp +) + +list(APPEND APPLICATION_HEADERS + ${libparamsHeaders} + ${ROOT_DIR}/Libs/Cyphal/include + ${ROOT_DIR}/Libs/Cyphal/Libs/libcanard/libcanard + ${ROOT_DIR}/build/nunavut_out + ${ROOT_DIR}/Libs/Cyphal/Libs/o1heap/o1heap +) diff --git a/Src/modules/bootloader/CMakeLists.txt b/Src/modules/bootloader/CMakeLists.txt new file mode 100644 index 0000000..78c7547 --- /dev/null +++ b/Src/modules/bootloader/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (C) 2023 Dmitry Ponomarev +# Distributed under the terms of the GPL v3 license, available in the file LICENSE. + +# Include guard +if(BOOTLOADER_MODULE_DIR) + return() +endif() +set(BOOTLOADER_MODULE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +list(APPEND APPLICATION_HEADERS + ${CMAKE_CURRENT_LIST_DIR} + ${ROOT_DIR}/build/bootloader_v3/libs/kocherga/kocherga/ +) + +list(APPEND APPLICATION_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/application.cpp + ${CMAKE_CURRENT_LIST_DIR}/bootloader_interface.cpp + ${CMAKE_CURRENT_LIST_DIR}/main.cpp + ${CMAKE_CURRENT_LIST_DIR}/my_can_driver.cpp +) + +list(APPEND LIBPARAMS_PARAMS + ${CMAKE_CURRENT_LIST_DIR}/params.yaml +) + +include(${ROOT_DIR}/Src/peripheral/led/CMakeLists.txt) diff --git a/Src/modules/bootloader/application.cpp b/Src/modules/bootloader/application.cpp new file mode 100644 index 0000000..7779bab --- /dev/null +++ b/Src/modules/bootloader/application.cpp @@ -0,0 +1,262 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include "application.hpp" +#include +#include "kocherga.hpp" +#include "my_can_driver.hpp" +#include "kocherga_can.hpp" +#include "main.h" +#include "cyphalNode/cyphal.hpp" +#include "libparams_error_codes.h" +#include "params.hpp" +#include "flash_driver.h" +#include "rom.h" + +// Please, specify config here: +#define BOOTLOADER_SIZE_PAGES 32 + +// Automatically calculated: +#define RESERVED_PAGES 3 +#define BOOTLOADER_SIZE_BYTES (flashGetPageSize() * BOOTLOADER_SIZE_PAGES) +#define APP_FIRST_PAGE_IDX BOOTLOADER_SIZE_PAGES +#define APP_START_ADDRESS (FLASH_START_ADDR + BOOTLOADER_SIZE_BYTES) +#define APP_MAX_SIZE_PAGES (flashGetNumberOfPages() - BOOTLOADER_SIZE_PAGES - RESERVED_PAGES) +#define APP_MAX_SIZE_BYTES (APP_MAX_SIZE_PAGES * flashGetPageSize()) + +#define PARAMS_SECTION_REL_ADDR (APP_MAX_SIZE_PAGES + 2) * flashGetPageSize() +#define RESERVED_SECTION_REL_ADDR (APP_MAX_SIZE_PAGES + 1) * flashGetPageSize() + +class MyROMBackend final : public kocherga::IROMBackend { +public: + MyROMBackend() + { + rom = romInit(APP_FIRST_PAGE_IDX, APP_MAX_SIZE_PAGES + RESERVED_PAGES); + } + +private: + auto write(const std::size_t offset, const std::byte* const data, const std::size_t size) + -> std::optional override + { + if (romWrite(&rom, offset, (const uint8_t*)data, size)) { + return size; + } + return {}; + } + + auto read(const std::size_t offset, std::byte* const out_data, const std::size_t size) const + -> std::size_t override + { + return romRead(&rom, offset, (uint8_t*)out_data, size); + } + + void beginWrite() override + { + romBeginWrite(&rom); + } + + void endWrite() override + { + romEndWrite(&rom); + } + + static inline RomDriverInstance rom; +}; + + +#define RED_PORT INTERNAL_LED_RED_GPIO_Port +#define RED_PIN INTERNAL_LED_RED_Pin +#define GREEN_PORT INTERNAL_LED_GREEN_GPIO_Port +#define GREEN_PIN INTERNAL_LED_GREEN_Pin +#define BLUE_PORT INTERNAL_LED_BLUE_GPIO_Port +#define BLUE_PIN INTERNAL_LED_BLUE_Pin + +enum class BootloaderCommand { + UNKNOWN_BOOT = 0, + FORCE_RUN_CYPHAL_APPLICATION = 1, + FORCE_RUN_DRONECAN_APPLICATION = 2, +}; + +void boot_app() { + typedef void (*pFunction)(void); + __disable_irq(); + uint32_t app_jump_address = *( uint32_t*) (APP_START_ADDRESS + 4); + pFunction Jump_To_Application = (pFunction)app_jump_address; + SCB->VTOR = APP_START_ADDRESS; + __set_MSP(*(__IO uint32_t*) APP_START_ADDRESS); + Jump_To_Application(); +} + +void reboot() { + HAL_NVIC_SystemReset(); +} + +void ledsTurnOffAll() { + HAL_GPIO_WritePin(RED_PORT, RED_PIN, GPIO_PIN_SET); + HAL_GPIO_WritePin(GREEN_PORT, GREEN_PIN, GPIO_PIN_SET); + HAL_GPIO_WritePin(BLUE_PORT, BLUE_PIN, GPIO_PIN_SET); +} + +GPIO_PinState timeToPinState(uint32_t period) { + return (HAL_GetTick() % period) > (period / 2) ? GPIO_PIN_SET : GPIO_PIN_RESET; +} + +void ledsToggleBlue(uint32_t period = 0) { + if (period == 0) { + HAL_GPIO_TogglePin(BLUE_PORT, BLUE_PIN); + } else { + HAL_GPIO_WritePin(BLUE_PORT, BLUE_PIN, timeToPinState(period)); + } +} + +void ledsToggleRed(uint32_t period = 0) { + if (period == 0) { + HAL_GPIO_TogglePin(RED_PORT, RED_PIN); + } else { + HAL_GPIO_WritePin(RED_PORT, RED_PIN, timeToPinState(period)); + } +} + +void ledsToggleGreen(uint32_t period = 0) { + if (period == 0) { + HAL_GPIO_TogglePin(GREEN_PORT, GREEN_PIN); + } else { + HAL_GPIO_WritePin(GREEN_PORT, GREEN_PIN, timeToPinState(period)); + } +} + +void ledsTogglePurple(uint32_t period = 0) { + if (period == 0) { + HAL_GPIO_TogglePin(BLUE_PORT, BLUE_PIN); + HAL_GPIO_TogglePin(RED_PORT, RED_PIN); + } else { + auto pin_state = timeToPinState(period); + HAL_GPIO_WritePin(BLUE_PORT, BLUE_PIN, pin_state); + HAL_GPIO_WritePin(RED_PORT, RED_PIN, pin_state); + } +} + +void catch_error(uint32_t period_ms) { + ledsTurnOffAll(); + + uint32_t next_toggle_led_ts_ms = 500; + uint32_t crnt_ts_ms = 0; + while (true) { + crnt_ts_ms = HAL_GetTick(); + if (crnt_ts_ms > next_toggle_led_ts_ms) { + next_toggle_led_ts_ms += period_ms; + ledsToggleRed(); + } + } +} + +void assert_true(bool is_true) { + if (!is_true) { + catch_error(100); + } +} + +uint8_t determine_node_id() { + auto node_id = 42; // paramsGetIntegerValue(PARAM_NODE_ID); + + if (node_id < 1 || node_id >= 127) { + node_id = 42; + } + + return node_id; +} + +uint8_t determine_protocol_version(MyCANDriver& can_driver) { + ledsTurnOffAll(); + uint8_t protocol_version; + + auto cmd = static_cast(paramsGetIntegerValue(PARAM_BOOTLOADER_INTERNAL)); + kocherga::can::ICANDriver::PayloadBuffer payload_buffer; + if (cmd == BootloaderCommand::UNKNOWN_BOOT) { + while (true) { + auto frame = can_driver.pop(payload_buffer); + ledsToggleBlue(500); + if (frame == std::nullopt || frame->second == 0) { + continue; + } + ledsToggleGreen(); + + uint8_t last_byte_idx = frame->second - 1; + uint8_t tail_byte_without_transfer_id = payload_buffer[last_byte_idx] & 0b11100000; + if (tail_byte_without_transfer_id == 0b11000000) { + protocol_version = 0; + break; + } else if (tail_byte_without_transfer_id == 0b11100000) { + protocol_version = 1; + break; + } + } + } else if (cmd == BootloaderCommand::FORCE_RUN_DRONECAN_APPLICATION) { + protocol_version = 1; + } else if (cmd == BootloaderCommand::FORCE_RUN_CYPHAL_APPLICATION) { + protocol_version = 1; + } else { + protocol_version = 0; + } + + return protocol_version; +} + +void bootloader_entry_point() { + ledsTurnOffAll(); + + paramsInit(2, 1, -1, 1); + paramsLoad(); + + MyROMBackend rom_backend; + + kocherga::SystemInfo system_info = { + .node_name = "Bootloader" + }; + + MyCANDriver can_driver; + can_driver.init(); + kocherga::can::CANNode can_node( + can_driver, + system_info.unique_id, + kocherga::can::ICANDriver::Bitrate{1000000, 1000000}, + determine_protocol_version(can_driver), + determine_node_id() + ); + + kocherga::Bootloader bootloader( + rom_backend, + system_info, + APP_MAX_SIZE_BYTES, ///< max_app_size + false, ///< linger + std::chrono::seconds(10) + ); + assert_true(bootloader.addNode(&can_node)); + + ledsTurnOffAll(); + while (true) { + const auto uptime = GET_TIME_SINCE_BOOT(); + const auto fin = bootloader.poll( + std::chrono::duration_cast(uptime) + ); + + auto state = bootloader.getState(); + if (state == kocherga::State::NoAppToBoot) { + ledsTogglePurple(100); + } else if (state == kocherga::State::BootDelay) { + ledsToggleGreen(100); + } else if (state == kocherga::State::BootCanceled) { + ledsToggleBlue(100); + } else if (state == kocherga::State::AppUpdateInProgress) { + ledsToggleRed(100); + } + + uint32_t crnt_time_ms = HAL_GetTick(); + if (crnt_time_ms > 10000 && *fin == kocherga::Final::BootApp) { + boot_app(); + } else if (*fin == kocherga::Final::Restart) { + reboot(); + } + } +} diff --git a/Src/modules/bootloader/application.hpp b/Src/modules/bootloader/application.hpp new file mode 100644 index 0000000..14a29fa --- /dev/null +++ b/Src/modules/bootloader/application.hpp @@ -0,0 +1,18 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#ifndef SRC_APPLICATION_HPP_ +#define SRC_APPLICATION_HPP_ + +#ifdef __cplusplus +extern "C" { +#endif + +void bootloader_entry_point(); + +#ifdef __cplusplus +} +#endif + +#endif // SRC_APPLICATION_HPP_ diff --git a/Src/modules/bootloader/bootloader_interface.cpp b/Src/modules/bootloader/bootloader_interface.cpp new file mode 100644 index 0000000..8168b70 --- /dev/null +++ b/Src/modules/bootloader/bootloader_interface.cpp @@ -0,0 +1,43 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include +#include + +#define VCS_REVISION_ID 12345 +#define FW_VERSION_MAJOR 10 +#define FW_VERSION_MINOR 11 +#define DIRTY_BUILD 1 +#define BUILD_TIMESTAMP_UTC 1234567 + + +alignas(16) struct AppDescriptor +{ + std::uint64_t magic = 0x5E44'1514'6FC0'C4C7ULL; + std::array signature{{'A', 'P', 'D', 'e', 's', 'c', '0', '0'}}; + + std::uint64_t image_crc = 0; // Populated after build + std::uint32_t image_size = 0; // Populated after build + [[maybe_unused]] std::array _reserved_a{}; + std::uint8_t version_major = 1; + std::uint8_t version_minor = 1; + std::uint8_t flags = +#if RELEASE_BUILD + Flags::ReleaseBuild; +#else + 0; +#endif + [[maybe_unused]] std::array _reserved_b{}; + std::uint32_t build_timestamp_utc = 0; + std::uint64_t vcs_revision_id = 0; + [[maybe_unused]] std::array _reserved_c{}; + + struct Flags + { + static constexpr std::uint8_t ReleaseBuild = 1U; + static constexpr std::uint8_t DirtyBuild = 2U; + }; +}; +static const volatile AppDescriptor _app_descriptor + __attribute__((used, section(".app_descriptor"))); diff --git a/Src/modules/bootloader/main.cpp b/Src/modules/bootloader/main.cpp new file mode 100644 index 0000000..92afe18 --- /dev/null +++ b/Src/modules/bootloader/main.cpp @@ -0,0 +1,19 @@ +/** + * This program is free software under the GNU General Public License v3. + * See for details. + * Author: Dmitry Ponomarev + */ + +#include "main.hpp" +#include "application.hpp" +// #include "common/algorithms.hpp" +// #include "params.hpp" + +REGISTER_MODULE(BootloaderModule) + +void BootloaderModule::init() { +} + +void BootloaderModule::spin_once() { + bootloader_entry_point(); +} diff --git a/Src/modules/bootloader/main.hpp b/Src/modules/bootloader/main.hpp new file mode 100644 index 0000000..d84750d --- /dev/null +++ b/Src/modules/bootloader/main.hpp @@ -0,0 +1,33 @@ +/** + * This program is free software under the GNU General Public License v3. + * See for details. + * Author: Dmitry Ponomarev + */ + +#ifndef SRC_MODULES_BOOTLOADER_HPP_ +#define SRC_MODULES_BOOTLOADER_HPP_ + +#include "module.hpp" +#include "common/logging.hpp" + +#ifdef __cplusplus +extern "C" { +#endif + +class BootloaderModule : public Module { +public: + BootloaderModule() : Module(2, Protocol::CYPHAL_AND_DRONECAN) {} + void init() override; + +protected: + void spin_once() override; + +private: + static inline Logging logger{"BOOT"}; +}; + +#ifdef __cplusplus +} +#endif + +#endif // SRC_MODULES_BOOTLOADER_HPP_ diff --git a/Src/modules/bootloader/my_can_driver.cpp b/Src/modules/bootloader/my_can_driver.cpp new file mode 100644 index 0000000..1483d5c --- /dev/null +++ b/Src/modules/bootloader/my_can_driver.cpp @@ -0,0 +1,104 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#include "my_can_driver.hpp" +#include "cyphalNode/cyphal_transport_can.hpp" +#include "o1heap.h" +#include "main.h" + +static cyphal::CyphalTransportCan transport_can_driver; +static uint8_t base[cyphal::HEAP_SIZE] __attribute__ ((aligned (O1HEAP_ALIGNMENT))); +static O1HeapInstance* my_allocator; + +std::optional> CAN_POP(unsigned char* data) { + std::optional> out; + cyphal::CanardFrame can_frame; + if (transport_can_driver.receive(&can_frame)) { + out.emplace(std::pair{ + can_frame.extended_can_id, + can_frame.payload_size + }); + memcpy(data, can_frame.payload, can_frame.payload_size); + } + return out; +} +bool CAN_PUSH(uint32_t extended_can_id, std::size_t size, const uint8_t arr[]) { + cyphal::CanardTxQueueItem transfer; + transfer.frame.extended_can_id = extended_can_id; + transfer.frame.payload_size = size; + transfer.frame.payload = arr; + return transport_can_driver.transmit(&transfer); +} +auto MY_MALLOC(std::size_t amount) -> void* { + return o1heapAllocate(my_allocator, amount); +} +auto MY_FREE(void* pointer) -> void { + o1heapFree(my_allocator, pointer); +} +std::chrono::microseconds GET_TIME_SINCE_BOOT() { + return (std::chrono::microseconds)(HAL_GetTick() * 1000); +} +auto kocherga::getRandomByte() -> std::uint8_t { + return static_cast( + std::rand() * std::numeric_limits::max() / RAND_MAX + ); +} +void WAIT_FOR_EVENT() { + HAL_Delay(1); +} + +MyCANDriver::MyCANDriver() : tx_queue_{&MY_MALLOC, &MY_FREE} {}; + +auto MyCANDriver::configure(const Bitrate& bitrate, + const bool silent, + const kocherga::can::CANAcceptanceFilterConfig& filter) + -> std::optional +{ + (void)bitrate; + (void)silent; + (void)filter; + + init(); + return Mode::Classic; +} + +void MyCANDriver::init() +{ + if (!is_configured) { + my_allocator = o1heapInit(base, cyphal::HEAP_SIZE); + tx_queue_.clear(); + transport_can_driver.init(1000000, 0); + is_configured = true; + } +} + +auto MyCANDriver::push(const bool force_classic_can, + const std::uint32_t extended_can_id, + const std::uint8_t payload_size, + const void* const payload) -> bool +{ + const std::chrono::microseconds now = GET_TIME_SINCE_BOOT(); + const bool ok = tx_queue_.push(now, force_classic_can, extended_can_id, payload_size, payload); + pollTxQueue(now); + return ok; +} + +auto MyCANDriver::pop(PayloadBuffer& payload_buffer) + -> std::optional> +{ + // originally it was without arguments... + pollTxQueue(GET_TIME_SINCE_BOOT()); + auto res = CAN_POP(payload_buffer.data()); + return res; +} + +void MyCANDriver::pollTxQueue(const std::chrono::microseconds now) +{ + if (const auto* const item = tx_queue_.peek()) { + const bool expired = now > (item->timestamp + kocherga::can::SendTimeout); + if (expired || CAN_PUSH(item->extended_can_id, item->payload_size, item->payload)) { + tx_queue_.pop(); + } + } +} diff --git a/Src/modules/bootloader/my_can_driver.hpp b/Src/modules/bootloader/my_can_driver.hpp new file mode 100644 index 0000000..68a4a3a --- /dev/null +++ b/Src/modules/bootloader/my_can_driver.hpp @@ -0,0 +1,44 @@ +/// This software is distributed under the terms of the MIT License. +/// Copyright (c) 2022 Dmitry Ponomarev. +/// Author: Dmitry Ponomarev + +#ifndef BOARDS_BOOTLOADER_MY_CAN_DRIVER_HPP_ +#define BOARDS_BOOTLOADER_MY_CAN_DRIVER_HPP_ + +#include "cyphalNode/cyphal.hpp" +#include "o1heap.h" +#include "kocherga.hpp" +#include "kocherga_can.hpp" + +std::chrono::microseconds GET_TIME_SINCE_BOOT(); + +class MyCANDriver final : public kocherga::can::ICANDriver { +public: + MyCANDriver(); + + void init(); + + auto pop(PayloadBuffer& payload_buffer) + -> std::optional> override; + + auto configure(const Bitrate& bitrate, + const bool silent, + const kocherga::can::CANAcceptanceFilterConfig& filter + ) -> std::optional override; + + auto push(const bool force_classic_can, + const std::uint32_t extended_can_id, + const std::uint8_t payload_size, + const void* const payload) -> bool override; + + + void pollTxQueue(const std::chrono::microseconds now); + +private: + typedef void* (*MemoryAllocate)(std::size_t); + typedef void (*MemoryDeallocate)(void*); + kocherga::can::TxQueue tx_queue_; + bool is_configured{false}; +}; + +#endif // BOARDS_BOOTLOADER_MY_CAN_DRIVER_HPP_ diff --git a/Src/modules/bootloader/params.yaml b/Src/modules/bootloader/params.yaml new file mode 100644 index 0000000..61cc8a0 --- /dev/null +++ b/Src/modules/bootloader/params.yaml @@ -0,0 +1,8 @@ +system.internal: + type: "Integer" + enum: "PARAM_BOOTLOADER_INTERNAL" + flags: "mutable" + default: 0 + min: 0 + max: 2147483647 + note: Reserved diff --git a/cmake/stm32g0b1_bootloader.cmake b/cmake/stm32g0b1_bootloader.cmake new file mode 100644 index 0000000..f417083 --- /dev/null +++ b/cmake/stm32g0b1_bootloader.cmake @@ -0,0 +1,13 @@ +# Copyright (C) 2023-2024 Dmitry Ponomarev +# Distributed under the terms of the GPL v3 license, available in the file LICENSE. + +set(TARGET_ARCHITECTURE cortex-m0plus) +set(CPU STM32G0B1xx) +set(stm32cubeMxProjectPath ${ROOT_DIR}/build/bootloader_v3/libs/bootloader-ioc) +FILE(GLOB ldFile ${stm32cubeMxProjectPath}/*_FLASH.ld) +FILE(GLOB coreSources ${stm32cubeMxProjectPath}/Core/Src/*) +FILE(GLOB driversSources ${stm32cubeMxProjectPath}/Drivers/*/*/*.c) +FILE(GLOB startupFile ${stm32cubeMxProjectPath}/*.s + ${stm32cubeMxProjectPath}/Core/Startup/*.s +) +include(${CMAKE_CURRENT_LIST_DIR}/Toolchain-arm-none-eabi.cmake) diff --git a/scripts/install_bootloader.sh b/scripts/install_bootloader.sh new file mode 100755 index 0000000..36b2ffc --- /dev/null +++ b/scripts/install_bootloader.sh @@ -0,0 +1,37 @@ +#!/bin/bash +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +REPOSITORY_PATH="$(dirname "$SCRIPT_DIR")" + +LIBS_DIR="$REPOSITORY_PATH/build/bootloader_v3/libs" +KOCHERGA_REPO="https://github.com/Zubax/kocherga.git" +KOCHERGA_COMMIT="85f24d4" +BOOTLOADER_IOC_REPO="https://github.com/RaccoonLabHardware/v3-software-template.git" +BOOTLOADER_IOC_COMMIT="dc90159" + +# Step 1: Create the libs directory if it doesn't exist +if [ ! -d "$LIBS_DIR" ]; then + echo "Creating directory $LIBS_DIR" + mkdir -p "$LIBS_DIR" +fi + +# Step 2: Clone and checkout kocherga if it doesn't exist +if [ ! -d "$LIBS_DIR/kocherga" ]; then + echo "Cloning kocherga repository" + git clone "$KOCHERGA_REPO" "$LIBS_DIR/kocherga" + cd "$LIBS_DIR/kocherga" || exit + echo "Checking out commit $KOCHERGA_COMMIT" + git checkout "$KOCHERGA_COMMIT" +else + echo "kocherga folder already exists in $LIBS_DIR" +fi + +# Step 3: Clone and checkout bootloader-ioc if it doesn't exist +if [ ! -d "$LIBS_DIR/bootloader-ioc" ]; then + echo "Cloning bootloader-ioc repository" + git clone "$BOOTLOADER_IOC_REPO" "$LIBS_DIR/bootloader-ioc" + cd "$LIBS_DIR/bootloader-ioc" || exit + echo "Checking out commit $BOOTLOADER_IOC_COMMIT" + git checkout "$BOOTLOADER_IOC_COMMIT" +else + echo "bootloader-ioc folder already exists in $LIBS_DIR" +fi