diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index c1fa02e0..9a14f0fa 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -33,3 +33,30 @@ jobs: command: manifest #token: ${{ steps.token.outputs.token }} token: ${{ secrets.AUTOMATIC_RELEASE_TOKEN }} + build_upload_packages: + name: Build & Upload Packages + needs: release_please + if: ${{ needs.release_please.outputs.releases_created }} + permissions: + contents: write + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + persist-credentials: false + - uses: hendrikmuhs/ccache-action@6d1841ec156c39a52b1b23a810da917ab98da1f4 # v1.2.10 + with: + key: ${{ github.job }}-${{ matrix.os }} + variant: sccache + - uses: lukka/run-cmake@2ce8982be71b8e9a3c4d5e432135035afd1e76a7 # v10.7 + with: + configurePreset: "host-single-MinSizeRel" + buildPreset: "release-package" + configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=sccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=sccache']" + - run: gh release upload ${{ needs.release_please.outputs.tag_name }} build/**/hal_st-*.zip --clobber + shell: bash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CMakeLists.txt b/CMakeLists.txt index c1e5a3ec..f8dab87c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,6 @@ cmake_minimum_required(VERSION 3.24) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_INSTALL_PREFIX}") if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(HALST_STANDALONE On) @@ -40,6 +42,7 @@ else() set(HALST_EXCLUDE_FROM_ALL "EXCLUDE_FROM_ALL") endif() +add_subdirectory(tools) add_subdirectory(st) add_subdirectory(hal_st) add_subdirectory(hal_st_lwip) @@ -53,6 +56,32 @@ if (HALST_STANDALONE) emil_folderize_all_targets() endif() +if (EMIL_HOST_BUILD) + install(EXPORT halstTargets + FILE hal_stTargets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/hal_st + ) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake" + COMPATIBILITY SameMajorVersion + # When cross-compiling for a 32-bit architecture and re-using host tooling from a 64-bit architecture + # ARCH_INDEPENDENT is necessary here. See: https://cmake.org/cmake/help/latest/module/CMakePackageConfigHelpers.html. + ARCH_INDEPENDENT + ) + + configure_package_config_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) + + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + ) +endif() + set(CPACK_GENERATOR "ZIP;TGZ") set(CPACK_SOURCE_IGNORE_FILES ".vs/;.git/;build/") set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in new file mode 100644 index 00000000..3394a994 --- /dev/null +++ b/cmake/Config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/hal_stTargets.cmake") + +check_required_components(TableGenerator) diff --git a/hal_st/stm32fxxx/CMakeLists.txt b/hal_st/stm32fxxx/CMakeLists.txt index 6ae04a1e..304eb585 100644 --- a/hal_st/stm32fxxx/CMakeLists.txt +++ b/hal_st/stm32fxxx/CMakeLists.txt @@ -1,3 +1,5 @@ +include(table_generator.cmake) + add_library(hal_st.stm32fxxx STATIC) emil_build_for(hal_st.stm32fxxx TARGET_MCU_VENDOR st PREREQUISITE_BOOL HALST_STANDALONE) @@ -123,17 +125,11 @@ if (TARGET_MCU_VENDOR STREQUAL st) generate_xslt(hal_st.stm32fxxx generated/stm32fxxx/PinoutTableDefault.cpp GeneratePinoutTableCpp.xsl "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PinoutTableDefaultStructure.xml") generate_xslt(hal_st.stm32fxxx generated/stm32fxxx/PinoutTableDefault.hpp GeneratePinoutTableHpp.xsl "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PinoutTableDefaultStructure.xml") - generate_xslt(hal_st.stm32fxxx generated/stm32fxxx/PeripheralTableStructure.xml GeneratePeripheralTableStructure.xsl $,PeripheralTableWbxx.xml,PeripheralTableFxxx.xml> - --stringparam mcu-document ${mcu_xml}) - generate_xslt(hal_st.stm32fxxx generated/stm32fxxx/PeripheralTable.cpp GeneratePeripheralTableCpp.xsl "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PeripheralTableStructure.xml") - generate_xslt(hal_st.stm32fxxx generated/stm32fxxx/PeripheralTable.hpp GeneratePeripheralTableHpp.xsl "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PeripheralTableStructure.xml") - target_sources(hal_st.stm32fxxx PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PinoutTableDefaultStructure.xml" "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PinoutTableDefault.cpp" "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PinoutTableDefault.hpp" - "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PeripheralTableStructure.xml" - "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PeripheralTable.cpp" - "${CMAKE_CURRENT_BINARY_DIR}/generated/stm32fxxx/PeripheralTable.hpp" ) + + st_table_generator(hal_st.stm32fxxx ${mcu_xml}) endif() diff --git a/hal_st/stm32fxxx/DefaultClockNucleoG070RB.cpp b/hal_st/stm32fxxx/DefaultClockNucleoG070RB.cpp index 45189f33..f06aec4f 100644 --- a/hal_st/stm32fxxx/DefaultClockNucleoG070RB.cpp +++ b/hal_st/stm32fxxx/DefaultClockNucleoG070RB.cpp @@ -12,7 +12,7 @@ * VDD(V) = 3.3 * Main regulator output voltage = Scale1 mode */ -void ConfigureDefaultClockNucleo070RB() +void ConfigureDefaultClockNucleoG070RB() { RCC_OscInitTypeDef RCC_OscInitStruct = { 0 }; RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 }; diff --git a/hal_st/stm32fxxx/DefaultClockNucleoG070RB.hpp b/hal_st/stm32fxxx/DefaultClockNucleoG070RB.hpp index 8d80a348..08b72501 100644 --- a/hal_st/stm32fxxx/DefaultClockNucleoG070RB.hpp +++ b/hal_st/stm32fxxx/DefaultClockNucleoG070RB.hpp @@ -1,6 +1,6 @@ #ifndef HAL_ST_DEFAULT_CLOCK_HPP #define HAL_ST_DEFAULT_CLOCK_HPP -void ConfigureDefaultClockNucleo070RB(); +void ConfigureDefaultClockNucleoG070RB(); #endif diff --git a/hal_st/stm32fxxx/PeripheralTableG0xx.xml b/hal_st/stm32fxxx/PeripheralTableG0xx.xml new file mode 100644 index 00000000..c6448ac5 --- /dev/null +++ b/hal_st/stm32fxxx/PeripheralTableG0xx.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/hal_st/stm32fxxx/table_generator.cmake b/hal_st/stm32fxxx/table_generator.cmake new file mode 100644 index 00000000..c79a3928 --- /dev/null +++ b/hal_st/stm32fxxx/table_generator.cmake @@ -0,0 +1,75 @@ +function(hal_st_fetch_table_generator) + # This function will first try to use `find_package` to import + # amp-hal-st table generators. If there is an installed version + # of amp-hal-st present those table generators will be used. + # Otherwise the latest hal-st release will be downloaded and those + # table generators will be used instead. + # + # (See: https://cmake.org/cmake/help/latest/module/FetchContent.html#commands) + + if (EMIL_HOST_BUILD AND NOT CMAKE_CROSSCOMPILING) + # In a host build where we are not cross-compiling we use the built table generators + return() + endif() + + FetchContent_GetProperties(table_generator) + if (table_generator_POPULATED) + return() + endif() + + set(hal_st_version "2.1.0") # x-release-please-version + + if (CMAKE_HOST_WIN32) + set(os_postfix "win64") + set(host_executable_postfix ".exe") + elseif (CMAKE_HOST_APPLE) + set(os_postfix "Darwin") + elseif (CMAKE_HOST_UNIX) + set(os_postfix "Linux") + else() + message(FATAL_ERROR "No suitable table generators found for ${CMAKE_HOST_SYSTEM_NAME} (${CMAKE_HOST_SYSTEM_PROCESSOR})") + endif() + + FetchContent_Declare(table_generator + URL https://github.com/philips-software/amp-hal-st/releases/download/v${hal_st_version}/hal_st-${hal_st_version}-${os_postfix}.zip + FIND_PACKAGE_ARGS NAMES hal_st GLOBAL + ) + FetchContent_MakeAvailable(table_generator) + + if (NOT ${table_generators_FOUND}) + if (NOT TARGET tools.st_table_generator) + add_executable(tools.st_table_generator IMPORTED GLOBAL) + set_target_properties(tools.st_table_generator PROPERTIES + IMPORTED_LOCATION "${table_generators_SOURCE_DIR}/bin/tools.st_table_generator${host_executable_postfix}" + ) + endif() + else() + message(STATUS "Using table generator from installed location") + endif() +endfunction() + +function(st_table_generator target mcu_xml) + hal_st_fetch_table_generator() + + cmake_path(SET generated_dir_tables "generated/stm32fxxx") + cmake_path(ABSOLUTE_PATH generated_dir_tables BASE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} NORMALIZE OUTPUT_VARIABLE generated_dir_tables) + + set(generated_files "${generated_dir_tables}/PeripheralTable.cpp" "${generated_dir_tables}/PeripheralTable.hpp") + + add_custom_command( + OUTPUT ${generated_files} + COMMAND ${CMAKE_COMMAND} -E make_directory ${generated_dir_tables} + COMMAND tools.st_table_generator "${mcu_xml}" ${generated_dir_tables} + MAIN_DEPENDENCY "${mcu_xml}" + DEPENDS tools.st_table_generator + ) + + target_sources(${target} PRIVATE + ${generated_files} + ) + + target_include_directories(${target} PUBLIC + "$" + "$" + ) +endfunction() diff --git a/st/CMakeLists.txt b/st/CMakeLists.txt index 161b3fb0..e01867ba 100644 --- a/st/CMakeLists.txt +++ b/st/CMakeLists.txt @@ -27,7 +27,18 @@ target_compile_definitions(st.hal_driver_stm32g0xx PUBLIC USE_HAL_DRIVER=1 STM32G0=1 DEVICE_HEADER="stm32g0xx.h" + $<$:STM32G030xx=1> + $<$:STM32G031xx=1> + $<$:STM32G041xx=1> + $<$:STM32G050xx=1> + $<$:STM32G051xx=1> + $<$:STM32G061xx=1> $<$:STM32G070xx=1> + $<$:STM32G071xx=1> + $<$:STM32G081xx=1> + $<$:STM32G0B0xx=1> + $<$:STM32G0B1xx=1> + $<$:STM32G0C1xx=1> ) add_hal_driver(st.hal_driver_stm32g4xx STM32G4xx_HAL_Driver CMSIS_STM32G4xx) diff --git a/st/ldscripts/sections.ld b/st/ldscripts/sections.ld index ab4b1195..e7ec0bb9 100644 --- a/st/ldscripts/sections.ld +++ b/st/ldscripts/sections.ld @@ -22,6 +22,8 @@ SECTIONS KEEP(*(.isr_vector)) *(.after_vectors .after_vectors.*) + *(.text.Reset_Handler) + *(.text.Default_Handler) } >FLASH .inits : ALIGN(4) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 00000000..0a293e27 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(st_table_generator) diff --git a/tools/st_table_generator/CMakeLists.txt b/tools/st_table_generator/CMakeLists.txt new file mode 100644 index 00000000..d19a4fca --- /dev/null +++ b/tools/st_table_generator/CMakeLists.txt @@ -0,0 +1,12 @@ +if (NOT CMAKE_CROSSCOMPILING) + add_executable(tools.st_table_generator) + install(TARGETS tools.st_table_generator EXPORT halstTargets DESTINATION bin) + + target_sources(tools.st_table_generator PRIVATE Main.cpp) + + target_link_libraries(tools.st_table_generator PRIVATE + args + hal.generic + infra.syntax + ) +endif() diff --git a/tools/st_table_generator/Main.cpp b/tools/st_table_generator/Main.cpp new file mode 100644 index 00000000..252466ce --- /dev/null +++ b/tools/st_table_generator/Main.cpp @@ -0,0 +1,357 @@ +#include "args.hxx" +#include "google/protobuf/compiler/code_generator.h" +#include "google/protobuf/compiler/cpp/helpers.h" +#include "google/protobuf/compiler/plugin.h" +#include "google/protobuf/io/printer.h" +#include "google/protobuf/io/zero_copy_stream_impl.h" +#include "google/protobuf/stubs/strutil.h" +#include "hal/generic/FileSystemGeneric.hpp" +#include "infra/syntax/CppFormatter.hpp" +#include "infra/syntax/XmlNavigator.hpp" +#include +#include +#include + +template +std::vector Compact(const std::vector>& v) +{ + std::vector result; + + for (auto& x : v) + if (x != infra::none) + result.push_back(*x); + + return result; +} + +struct PeripheralCandidate +{ + std::string name; + std::string type; + bool interrupt; + std::vector ipNames; + infra::Optional prefix; + infra::Optional base; +}; + +struct Peripheral +{ + std::string name; + int position; + infra::Optional clockEnable; + infra::Optional base; +}; + +struct PeripheralGroup +{ + std::string groupName; + std::string type; + bool interrupt; + std::vector peripherals; +}; + +class TableGenerator +{ +public: + TableGenerator(hal::FileSystem& fileSystem, const std::filesystem::path& inputPath, const std::vector& candidates); + + void Write(const std::filesystem::path& outputDirectory); + +private: + void ReadCandidates(const std::vector& candidates); + void ReadCandidate(const PeripheralCandidate& candidate); + + std::string MakeArrayType(const std::string& type, int size) const; + std::string PeripheralInitializer(const std::vector& peripherals) const; + std::string IrqInitializer(const std::vector& peripherals) const; + std::string EnableClockBody(const std::vector& peripherals) const; + std::string DisableClockBody(const std::vector& peripherals) const; + std::string ReplaceAll(std::string_view names, std::string_view from, std::string_view to) const; + std::string InvokeAll(std::string_view names) const; + + static std::string Concatenate(const std::vector& lines); + +private: + std::filesystem::path inputDirectory; + std::string input; + infra::XmlNodeNavigator navigator; + std::vector candidates; + std::vector peripheralGroups; +}; + +TableGenerator::TableGenerator(hal::FileSystem& fileSystem, const std::filesystem::path& inputPath, const std::vector& candidates) + : inputDirectory(inputPath.parent_path()) + , input(Concatenate(fileSystem.ReadFile(inputPath))) + , navigator(input) +{ + ReadCandidates(candidates); +} + +std::string ToUpper(const std::string& s) +{ + std::string result(s.size(), ' '); + std::transform(s.begin(), s.end(), result.begin(), ::toupper); + return result; +} + +void TableGenerator::Write(const std::filesystem::path& outputDirectory) +{ + for (const auto& peripheralGroup : peripheralGroups) + { + std::cout << "Group: " << peripheralGroup.groupName << std::endl; + for (const auto& peripheral : peripheralGroup.peripherals) + { + std::cout << " Peripheral " << peripheral.name << " pos " << peripheral.position; + if (peripheral.clockEnable != infra::none) + std::cout << " Clock Enable: " << *peripheral.clockEnable; + std::cout << std::endl; + } + } + + application::IncludeGuard formatter("include_guard_hpp"); + + auto includesByHeader = std::make_shared(); + includesByHeader->Path("infra/util/MemoryRange.hpp"); + includesByHeader->PathMacro("DEVICE_HEADER"); + formatter.Add(includesByHeader); + + auto includesBySource = std::make_shared(); + includesBySource->Path("generated/stm32fxxx/PeripheralTable.hpp"); + includesBySource->PathSystem("array"); + includesBySource->PathSystem("cstdlib"); + formatter.Add(includesBySource); + + auto halNamespace = std::make_shared("hal"); + formatter.Add(halNamespace); + + for (const auto& group : peripheralGroups) + { + auto definitionName = "HAS_PERIPHERAL_" + ToUpper(group.groupName); + auto variableName = "peripheral" + group.groupName; + + if (group.peripherals.empty()) + halNamespace->Add(std::make_shared(definitionName)); + else + { + halNamespace->Add(std::make_shared(definitionName)); + halNamespace->Add(std::make_shared(variableName + "Array", MakeArrayType("unsigned int", group.peripherals.size()), PeripheralInitializer(group.peripherals))); + halNamespace->Add(std::make_shared(variableName, "const infra::MemoryRange<" + group.type + " const>", "infra::ReinterpretCastMemoryRange<" + group.type + " const>(infra::MakeRange(" + variableName + "Array))")); + + if (group.interrupt) + { + halNamespace->Add(std::make_shared(variableName + "IrqArray", MakeArrayType("IRQn_Type const", group.peripherals.size()), IrqInitializer(group.peripherals))); + halNamespace->Add(std::make_shared(variableName + "Irq", "const infra::MemoryRange", "infra::MakeRange(" + variableName + "IrqArray)")); + } + + auto EnableClock = std::make_shared("EnableClock" + group.groupName, EnableClockBody(group.peripherals), "void", 0); + EnableClock->Parameter("std::size_t index"); + halNamespace->Add(EnableClock); + auto DisableClock = std::make_shared("DisableClock" + group.groupName, DisableClockBody(group.peripherals), "void", 0); + DisableClock->Parameter("std::size_t index"); + halNamespace->Add(DisableClock); + } + } + + std::ofstream header(outputDirectory / "PeripheralTable.hpp"); + std::unique_ptr headerStream = std::make_unique(&header); + google::protobuf::io::Printer headerPrinter(headerStream.get(), '$', nullptr); + formatter.PrintHeader(headerPrinter); + + std::ofstream source(outputDirectory / "PeripheralTable.cpp"); + std::unique_ptr sourceStream = std::make_unique(&source); + google::protobuf::io::Printer sourcePrinter(sourceStream.get(), '$', nullptr); + formatter.PrintSource(sourcePrinter, ""); +} + +void TableGenerator::ReadCandidates(const std::vector& candidates) +{ + for (const auto& candidate : candidates) + ReadCandidate(candidate); +} + +void TableGenerator::ReadCandidate(const PeripheralCandidate& candidate) +{ + const infra::XmlNodeNavigatorToken mcu{ "Mcu" }; + const infra::XmlTransformArrayNavigatorToken> ip{ "IP", [this, &candidate](const infra::XmlNodeNavigator& navigator) + { + static const infra::XmlStringAttributeNavigatorToken name{ "Name" }; + static const infra::XmlStringAttributeNavigatorToken instance_name{ "InstanceName" }; + static const infra::XmlOptionalStringAttributeNavigatorToken clock_enable_mode{ "ClockEnableMode" }; + + auto ipName = navigator / name; + for (auto& candidateIpName : candidate.ipNames) + if (ipName == candidateIpName) + { + Peripheral result; + + result.name = navigator / instance_name; + + if (candidate.prefix != infra::none) + result.position = std::stoi(result.name.substr(candidate.prefix->size())); + else if (result.name == ipName) + result.position = 1; + else + result.position = std::stoi(result.name.substr(ipName.size())); + + auto clockEnable = navigator / clock_enable_mode; + if (clockEnable != infra::none) + result.clockEnable = clockEnable; + + result.base = candidate.base; + + return infra::MakeOptional(result); + } + + return infra::Optional(); + } }; + + peripheralGroups.push_back(PeripheralGroup{ candidate.name, candidate.type, candidate.interrupt, Compact(navigator / mcu / ip) }); +} + +std::string TableGenerator::MakeArrayType(const std::string& type, int size) const +{ + std::ostringstream stream; + + stream << "constexpr std::array<" << type << ", " << size << ">"; + + return stream.str(); +} + +std::string TableGenerator::PeripheralInitializer(const std::vector& peripherals) const +{ + std::ostringstream stream; + + stream << "\n {{\n"; + for (auto& p : peripherals) + stream << " " << p.name << "_BASE,\n"; + stream << " }}"; + + return stream.str(); +} + +std::string TableGenerator::IrqInitializer(const std::vector& peripherals) const +{ + std::ostringstream stream; + + stream << "\n {{\n"; + for (auto& p : peripherals) + stream << " " << p.name << "_IRQn,\n"; + stream << " }}"; + + return stream.str(); +} + +std::string TableGenerator::EnableClockBody(const std::vector& peripherals) const +{ + std::ostringstream stream; + + stream << "switch (index)\n{\n"; + + for (auto& p : peripherals) + if (p.clockEnable) + stream << " case " << std::distance(peripherals.data(), &p) << ": " << InvokeAll(*p.clockEnable) << " break;\n"; + else + stream << " case " << std::distance(peripherals.data(), &p) << ": __HAL_RCC_" << p.name << "_CLK_ENABLE(); break;\n"; + + stream << " default:\n std::abort();\n}\n"; + + return stream.str(); +} + +std::string TableGenerator::DisableClockBody(const std::vector& peripherals) const +{ + std::ostringstream stream; + + stream << "switch (index)\n{\n"; + + for (auto& p : peripherals) + if (p.clockEnable) + stream << " case " << std::distance(peripherals.data(), &p) << ": " << ReplaceAll(InvokeAll(*p.clockEnable), "ENABLE", "DISABLE") << " break;\n"; + else + stream << " case " << std::distance(peripherals.data(), &p) << ": __HAL_RCC_" << p.name << "_CLK_DISABLE(); break;\n"; + + stream << " default:\n std::abort();\n}\n"; + + return stream.str(); +} + +std::string TableGenerator::ReplaceAll(std::string_view names, std::string_view from, std::string_view to) const +{ + std::string result(names); + + for (auto pos = result.find(from); pos != std::string::npos; pos = result.find(from)) + result.replace(pos, from.size(), to); + + return result; +} + +std::string TableGenerator::InvokeAll(std::string_view names) const +{ + std::string result; + + while (true) + { + auto pos = names.find(';'); + + result.append(names.substr(0, pos)); + result.append("(); "); + + if (pos == std::string::npos) + break; + + names = names.substr(pos + 1); + } + + return result; +} + +std::string TableGenerator::Concatenate(const std::vector& lines) +{ + std::string result; + + for (const auto& line : lines) + result += line; + + return result; +} + +int main(int argc, const char* argv[], const char* env[]) +{ + args::ArgumentParser parser("Generate tables for ST microcontrollers"); + args::Group positionals(parser, "Positional arguments:"); + args::Positional inputArgument(positionals, "input", "input xml file", args::Options::Required); + args::Positional directoryArgument(positionals, "directory", "directory for generated files", args::Options::Required); + args::HelpFlag help(parser, "help", "display this help menu.", { 'h', "help" }); + + try + { + parser.ParseCLI(argc, argv); + + hal::FileSystemGeneric fileSystem; + + std::vector candidates{ + PeripheralCandidate{ "Uart", "USART_TypeDef*", true, { "UART", "USART" }, infra::none }, + PeripheralCandidate{ "Spi", "SPI_TypeDef*", true, { "SPI" }, infra::none }, + PeripheralCandidate{ "Timer", "TIM_TypeDef*", true, { "TIM1_8", "TIM6_7", "TIM1_8F7", "TIM6_7F7", "TIM1_8F37", "TIM6_7F37", "TIM1_8F77", "TIM6_7F77" }, infra::MakeOptional("TIM") }, + PeripheralCandidate{ "Adc", "ADC_TypeDef*", true, { "ADC" }, infra::none }, + PeripheralCandidate{ "Rtc", "RTC_TypeDef*", false, { "RTC" }, infra::none }, + }; + + TableGenerator tableGenerator(fileSystem, std::filesystem::path(args::get(inputArgument)), candidates); + + const auto outputDirectory = args::get(directoryArgument); + tableGenerator.Write(outputDirectory); + } + catch (const args::Help&) + { + std::cout << parser; + return 1; + } + catch (const std::exception& ex) + { + std::cout << ex.what() << std::endl; + return 1; + } + + return 0; +}