diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..42b3e46 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,6 @@ +{ + "image": "mcr.microsoft.com/devcontainers/universal:2", + "features": { + }, + "postCreateCommand": "chmod +x ./.devcontainer/postcreate.sh && ./.devcontainer/postcreate.sh" +} diff --git a/.devcontainer/postcreate.sh b/.devcontainer/postcreate.sh new file mode 100755 index 0000000..282c207 --- /dev/null +++ b/.devcontainer/postcreate.sh @@ -0,0 +1,27 @@ +mkdir -p ../arm-none-eabi/ +cd ../arm-none-eabi/ + +if [ ! -f ./arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi.tar.xz ] +then + echo "Downloading arm-none-eabi-gcc" + wget https://developer.arm.com/-/media/Files/downloads/gnu/12.3.rel1/binrel/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi.tar.xz +fi + +echo "Unpacking arm-none-eabi-gcc" +tar xf arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi.tar.xz + +sudo rm -f /usr/bin/arm-none-eabi-gcc || true +sudo rm -f /usr/bin/arm-none-eabi-g++ || true +sudo rm -f /usr/bin/arm-none-eabi-gdb || true +sudo rm -f /usr/bin/arm-none-eabi-size || true +sudo rm -f /usr/bin/arm-none-eabi-objcopy || true +sudo rm -f /usr/bin/arm-none-eabi-objdump || true + +echo "Linking arm-none-eabi" + +sudo ln -s `pwd`/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-gcc /usr/bin/arm-none-eabi-gcc +sudo ln -s `pwd`/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-g++ /usr/bin/arm-none-eabi-g++ +sudo ln -s `pwd`/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-gdb /usr/bin/arm-none-eabi-gdb +sudo ln -s `pwd`/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-size /usr/bin/arm-none-eabi-size +sudo ln -s `pwd`/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-objcopy /usr/bin/arm-none-eabi-objcopy +sudo ln -s `pwd`/arm-gnu-toolchain-12.3.rel1-x86_64-arm-none-eabi/bin/arm-none-eabi-objdump /usr/bin/arm-none-eabi-objdump diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..008b7b2 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,38 @@ +name: flash loader template + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: arm-none-eabi-gcc install + uses: carlosperate/arm-none-eabi-gcc-action@v1.6.0 + with: + release: '12.2.Rel1' + + - name: arm-none-eabi-gcc version + run: arm-none-eabi-gcc --version + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: CC=arm-none-eabi-gcc CXX=arm-none-eabi-g++ cmake -B ${{github.workspace}}/build + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e524d79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.vscode/ +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..167e862 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,97 @@ +# set minimum version of CMake. +cmake_minimum_required(VERSION 3.13) + +# The Generic system name is used for embedded targets (targets without OS) in +# CMake +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR ARM) + +# Supress Error when trying to test the compiler +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(BUILD_SHARED_LIBS OFF) + +# set project name and version +project(flash_loader VERSION 0.0.1) + +# enable assembly +enable_language(ASM) + +set(SOURCES + ${CMAKE_SOURCE_DIR}/entry/entry.c + ${CMAKE_SOURCE_DIR}/entry/cortex-vector.cpp + ${CMAKE_SOURCE_DIR}/flash/main.cpp + ${CMAKE_SOURCE_DIR}/flash/flash_device.cpp +) + +set(HEADERS + ${CMAKE_SOURCE_DIR}/entry/entry.hpp +) + +# add our executable +add_executable(flash_loader + ${SOURCES} ${HEADERS} +) + +# enable C++20 support for the library +target_compile_features(flash_loader PUBLIC cxx_std_20) + +# set the output filename +set_target_properties(flash_loader PROPERTIES OUTPUT_NAME "flash_loader" SUFFIX ".elf") + +# compiler optimisations +target_compile_options(flash_loader PRIVATE "-g") +target_compile_options(flash_loader PRIVATE "-Os") + +# set the cpu options for the compiler +target_compile_options(flash_loader PRIVATE "-march=armv7e-m") +target_compile_options(flash_loader PRIVATE "-mcpu=cortex-m4") +target_compile_options(flash_loader PRIVATE "-mthumb") + +# other compiler settings +target_compile_options(flash_loader PRIVATE "-Wno-attributes") +target_compile_options(flash_loader PRIVATE "-fno-non-call-exceptions") +target_compile_options(flash_loader PRIVATE "-fno-common") +target_compile_options(flash_loader PRIVATE "-ffunction-sections") +target_compile_options(flash_loader PRIVATE "-fdata-sections") +target_compile_options(flash_loader PRIVATE "-fno-exceptions") +target_compile_options(flash_loader PRIVATE "-Wno-maybe-uninitialized") +target_compile_options(flash_loader PRIVATE "-Wno-unused-local-typedefs") +target_compile_options(flash_loader PRIVATE "-Wno-unused-but-set-variable") +target_compile_options(flash_loader PRIVATE "-Wno-unused-function") +target_compile_options(flash_loader PRIVATE "-fomit-frame-pointer") +target_compile_options(flash_loader PRIVATE "-Wall") +target_compile_options(flash_loader PRIVATE "-Werror") + +# set the c++ only options +target_compile_options(flash_loader PUBLIC $<$:-fno-threadsafe-statics>) +target_compile_options(flash_loader PUBLIC $<$:-fno-rtti>) +target_compile_options(flash_loader PUBLIC $<$:-fconcepts>) +target_compile_options(flash_loader PUBLIC $<$:-fno-use-cxa-get-exception-ptr>) +target_compile_options(flash_loader PUBLIC $<$:-fno-use-cxa-atexit>) +target_compile_options(flash_loader PUBLIC $<$:-Wno-volatile>) + +# set the cpu options for the linker +target_link_options(flash_loader PRIVATE "-march=armv7e-m") +target_link_options(flash_loader PRIVATE "-mcpu=cortex-m4") +target_link_options(flash_loader PRIVATE "-mthumb") + +# other linker options +target_link_options(flash_loader PUBLIC "-ffunction-sections") +target_link_options(flash_loader PUBLIC "-fdata-sections") +target_link_options(flash_loader PUBLIC "-nostdlib") +target_link_options(flash_loader PUBLIC "-nodefaultlibs") +target_link_options(flash_loader PUBLIC "-nostartfiles") +target_link_options(flash_loader PUBLIC "-Wl,--gc-sections") +target_link_options(flash_loader PUBLIC "-Wl,-fatal-warnings") +target_link_options(flash_loader PUBLIC "-Wl,-cref,-Map=flash_loader.map") +target_link_options(flash_loader PUBLIC "-Wl,--print-memory-usage") +target_link_options(flash_loader PUBLIC "-Wl,--no-warn-rwx-segments") + +# link to the linkerscript of the target cpu +target_link_options(flash_loader PUBLIC "-T${CMAKE_SOURCE_DIR}/linkerscript.ld") + +# Custom commands for processing the build binary and show some statistics and debug info +add_custom_command(TARGET flash_loader DEPENDS ${CMAKE_BINARY_DIR}/flash_loader.elf POST_BUILD COMMAND arm-none-eabi-objcopy ARGS -O binary -R .bss -R .stack flash_loader.elf flash_loader.bin) +add_custom_command(TARGET flash_loader DEPENDS ${CMAKE_BINARY_DIR}/flash_loader.elf POST_BUILD COMMAND arm-none-eabi-objdump ARGS -C -S flash_loader.elf > flash_loader.lss) +add_custom_command(TARGET flash_loader DEPENDS ${CMAKE_BINARY_DIR}/flash_loader.elf POST_BUILD COMMAND arm-none-eabi-objdump ARGS -C -sj PrgCode -sj DevDscr -sj .bss -sj .data -sj .rodata -S flash_loader.elf > flash_loader.memory) +add_custom_command(TARGET flash_loader DEPENDS ${CMAKE_BINARY_DIR}/flash_loader.elf POST_BUILD COMMAND arm-none-eabi-size ARGS -A flash_loader.elf -x) \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..f2b5273 --- /dev/null +++ b/Readme.md @@ -0,0 +1,5 @@ +# Flash loader template +Flash loader template without the need for a Keil/Segger DSK license. Uses Cmake and arm-none-eabi. Uses parts of [klib](https://github.com/itzandroidtab/klib/) + +## Targets of this project +Compatible with Segger J-link (Rip Open flash loader (OFL)) diff --git a/entry/cortex-vector.cpp b/entry/cortex-vector.cpp new file mode 100644 index 0000000..f4878e8 --- /dev/null +++ b/entry/cortex-vector.cpp @@ -0,0 +1,24 @@ +// include the header with the global reset handler and the default handler +#include "entry.hpp" + +// array with the initial vector table. Interrupt handlers should all point +// to the default handler as the irq class should be used to register the +// the handlers for interrupts. +void (*const volatile __vectors[])(void) __attribute__ ((section(".vectors"))) = { + (void (*)(void)) &__stack_end, + __reset_handler, // The reset handler + __default_handler, // The NMI handler + __default_handler, // The hard fault handler + __default_handler, // The MPU fault handler + __default_handler, // The bus fault handler + __default_handler, // The usage fault handler + 0, // Reserved + 0, // Reserved + 0, // Reserved + 0, // Reserved + __default_handler, // SVCall handler + __default_handler, // Debug monitor handler + 0, // Reserved + __default_handler, // The PendSV handler + __default_handler // The SysTick handler +}; \ No newline at end of file diff --git a/entry/entry.c b/entry/entry.c new file mode 100644 index 0000000..8586d44 --- /dev/null +++ b/entry/entry.c @@ -0,0 +1,83 @@ +#include +#include + +// type for constructors +typedef void (*entry_constructor)(void); + +// declaration to the main function +int main(); + +/** + * @brief Reset handler when the target starts running. This function + * initilizes the bss and data segements + * + * @details declares all linker variables as extern. Then we refer to the + * value using the &operator as the variables is at a valid data address. + * + * Functions that need to be called before main are run should have the + * attribute "__constructor__". When marked the function will be added to + * the ".init_array" segment and called before main is called. + * + */ +void __attribute__((__noreturn__, __naked__)) __reset_handler() { + // initialize the stack pointer. As we are running from ram + // the stack pointer is not setup yet. Move it to the stack + // end segment to prevent a hardfault + extern uint32_t __stack_end; + asm volatile ("mov sp, %0" : : "r" (&__stack_end) : ); + + extern uint8_t __bss_start; + extern uint8_t __bss_end; + + // set the bss section to 0x00 + for (uint32_t i = 0; i < (&__bss_end - &__bss_start); i++) { + ((volatile uint8_t*)(&__bss_start))[i] = 0x00; + } + + extern const entry_constructor __preinit_array_start; + extern const entry_constructor __preinit_array_end; + + // excecute all the preinit constructors + for (uint32_t i = 0; i < (&__preinit_array_end - &__preinit_array_start); i++) { + // call the preinit calls + (&__preinit_array_start)[i](); + } + + extern const entry_constructor __init_array_start; + extern const entry_constructor __init_array_end; + + // excecute all the global constructors + for (uint32_t i = 0; i < (&__init_array_end - &__init_array_start); i++) { + // call every constructor we have + (&__init_array_start)[i](); + } + + // run main + (void)main(); + + extern const entry_constructor __fini_array_start; + extern const entry_constructor __fini_array_end; + + // run all the destructors. Should never be called but if it happens + // it should work + for (uint32_t i = 0; i < (&__fini_array_end - &__fini_array_start); i++) { + // call every destructor we have + (&__fini_array_start)[i](); + } + + // we should never be here. If this happens loop to make + // sure we never exit the reset handler + while (true) {}; +} + +/** + * @brief Default handler that locks the cpu. + * + */ +void __default_handler() { + // do nothing and wait + while (true) {} +} + +// called when a vft entry is not yet filled in +void __cxa_pure_virtual() {} \ No newline at end of file diff --git a/entry/entry.hpp b/entry/entry.hpp new file mode 100644 index 0000000..1c7a5ec --- /dev/null +++ b/entry/entry.hpp @@ -0,0 +1,41 @@ +#ifndef KLIB_ENTRY_HPP +#define KLIB_ENTRY_HPP + +#include + +// extern c for c linkage in c++. No ifdef cplusplus as targets +// should be implemented using c++ +extern "C" { + // pointer to the end of the stack. Definition is done in the + // linkerscript. Only the address of the variable should be used. The + // address points to the correct location of the variable + extern const uint32_t __stack_end; + + // pointer to the start of the heap. Definition is done in the + // linkerscript. Only the address of the variable should be used. The + // address points to the correct location of the variable + extern const uint32_t __heap_start; + + // pointer to the end of the heap. Definition is done in the + // linkerscript. Only the address of the variable should be used. The + // address points to the correct location of the variable + extern const uint32_t __heap_end; + + /** + * @brief Generic reset handler that initializes the .bss and .data + * segments. It calls all the constructors and runs main. When code + * before main needs to be executed the "__constructor__" attribute + * can be added to the function. + * + */ + void __reset_handler(); + + /** + * @brief Default handler. Should be used to initialize the default + * arm vector table. + * + */ + void __default_handler(); +} + +#endif \ No newline at end of file diff --git a/flash/flash_device.cpp b/flash/flash_device.cpp new file mode 100644 index 0000000..170d423 --- /dev/null +++ b/flash/flash_device.cpp @@ -0,0 +1,233 @@ +#include +#include "flash_os.hpp" + +/** + * @brief Smallest amount of data that can be programmed + * + * = 2 ^ Shift. Shift = 3 => = 2^3 = 8 bytes + * + */ +#define PAGE_SIZE_SHIFT (8) + +/** + * @brief If value is false the device does not support native read. This + * makes the loader use the read function instead of using memory mapped + * io. + * + */ +#define NATIVE_READ (false) + +/** + * @brief Marks if the device supports a chip erase. This can speed up erasing + * a chip. + * + */ +#define CHIP_ERASE (false) + +/** + * @brief If value is true only uniform sectors are allowed on the device. + * Speeds up erasing as it can erase multiple sectors at once. + * + */ +#define UNIFORM_SECTORS (true) + +/** + * @brief Sector size shift for when using uniform sector erase + * + * = 2 ^ Shift. Shift = 12 => = 2 ^ 12 = 4096 bytes + * + */ +#define SECTOR_SIZE_SHIFT (12) + +/** + * @brief Use a custom verify. Is optional. Speeds up verifying + * + */ +#define CUSTOM_VERIFY (false) + +/** + * @brief Device specific infomation + * + */ +extern "C" { + // declaration for the flash device. If we initialize it here we get + // a wrong name in the symbol table + extern const struct flash_device FlashDevice; + + // Mark start of segment. Non-static to make sure linker can keep this + // symbol. Dummy needed to make sure that section in resulting ELF file + // is present. Needed by open flash loader logic on PC side + volatile int PRGDATA_StartMarker __attribute__ ((section ("PrgData"), __used__)); +} + +// definition for the flash device +const __attribute__ ((section("DevDscr"), __used__)) flash_device FlashDevice = { + flash_drv_version, // driver version + "test device", // device name + device_type::on_chip, // device type + 0xA0000000, // base address + 0x00000400, // flash size + 4, // page size + 0, // reserved + 0xff, // blank value + 100, // page program timeout + 3000, // sector erase timeout + + // flash sectors + { + {0x00000400, 0x00000000}, + end_of_sectors + } +}; + +// function overrides when parts are not in use +#if NATIVE_READ + #define BLANK_CHECK_FUNC nullptr + #define OPEN_READ_FUNC nullptr +#else + #define BLANK_CHECK_FUNC BlankCheck + #define OPEN_READ_FUNC SEGGER_OPEN_Read +#endif + +#if CUSTOM_VERIFY + #define VERIFY_FUNC Verify +#else + #define VERIFY_FUNC nullptr +#endif + +#if CHIP_ERASE + #define CHIP_ERASE_FUNC EraseChip +#else + #define CHIP_ERASE_FUNC nullptr +#endif + +#if UNIFORM_SECTORS + #define UNIFORM_ERASE_FUNC SEGGER_OPEN_Erase +#else + #define UNIFORM_ERASE_FUNC nullptr +#endif + +/** + * @brief array with all the functions for the segger software + * + */ +extern "C" { + // declaration for the OFL Api. If we initialize it here we get + // a wrong name in the symbol table + extern const uint32_t SEGGER_OFL_Api[13]; +} + +// definition of OFL Api +const uint32_t SEGGER_OFL_Api[13] __attribute__ ((section ("PrgCode"), __used__)) = { + reinterpret_cast(FeedWatchdog), + reinterpret_cast(Init), + reinterpret_cast(UnInit), + reinterpret_cast(EraseSector), + reinterpret_cast(ProgramPage), + reinterpret_cast(BLANK_CHECK_FUNC), + reinterpret_cast(CHIP_ERASE_FUNC), + reinterpret_cast(VERIFY_FUNC), + reinterpret_cast(nullptr), // SEGGER_OPEN_CalcCRC + reinterpret_cast(OPEN_READ_FUNC), + reinterpret_cast(SEGGER_OPEN_Program), + reinterpret_cast(UNIFORM_ERASE_FUNC), + reinterpret_cast(nullptr), // SEGGER_OPEN_Start for turbo mode +}; + +void __attribute__ ((noinline)) FeedWatchdog(void) { + // TODO: implement something to keep the watchdog happy + return; +} + +int __attribute__ ((noinline)) Init(const uint32_t address, const uint32_t frequency, const uint32_t function) { + // TODO: implement init + + return 0; +} + +int __attribute__ ((noinline)) UnInit(const uint32_t function) { + // TODO: implement uninit + + return 0; +} + +int __attribute__ ((noinline)) EraseSector(const uint32_t sector_address) { + // TODO: implement sector erase + + return 0; +} + +int __attribute__ ((noinline)) ProgramPage(const uint32_t address, const uint32_t size, const uint8_t *const data) { + // TODO: implement program page + + return 0; +} + +int __attribute__ ((noinline)) SEGGER_OPEN_Program(uint32_t address, uint32_t size, uint8_t *data) { + // get the amount of pages to write + const uint32_t pages = size >> PAGE_SIZE_SHIFT; + + for (uint32_t i = 0; i < pages; i++) { + // program a page + int r = ProgramPage(address + (i * PAGE_SIZE_SHIFT), (0x1 << PAGE_SIZE_SHIFT), &data[i * PAGE_SIZE_SHIFT]); + + // check if something went wrong + if (r) { + // return a error + return 1; + } + } + + // return everything went oke + return 0; +} + +#if CHIP_ERASE == true + int __attribute__ ((noinline)) EraseChip(void) { + // TODO: implement chip erase + return 0; + } +#endif + +#if UNIFORM_SECTORS + int __attribute__ ((noinline)) SEGGER_OPEN_Erase(uint32_t SectorAddr, uint32_t SectorIndex, uint32_t NumSectors) { + // feed the watchdog + FeedWatchdog(); + + for (uint32_t i = 0; i < NumSectors; i++) { + // erase a sector + int r = EraseSector(SectorAddr); + + // check for errors + if (r) { + // return we have a error + return 1; + } + } + + // return everything went oke + return 0; + } +#endif + +#if CUSTOM_VERIFY + uint32_t __attribute__ ((noinline, __used__)) Verify(uint32_t Addr, uint32_t NumBytes, uint8_t *pBuff) { + // TODO: implement verify + + return (Addr + NumBytes); + } +#endif + +#if !NATIVE_READ + int __attribute__ ((noinline, __used__)) BlankCheck(const uint32_t address, const uint32_t size, const uint8_t blank_value) { + // TODO: implement verify + + return 0; + } + + int __attribute__ ((noinline, __used__)) SEGGER_OPEN_Read(const uint32_t address, const uint32_t size, uint8_t *const data) { + // TODO: add read implementation + + return size; + } +#endif \ No newline at end of file diff --git a/flash/flash_os.hpp b/flash/flash_os.hpp new file mode 100644 index 0000000..caa362d --- /dev/null +++ b/flash/flash_os.hpp @@ -0,0 +1,205 @@ +#ifndef FLASH_OS_HPP +#define FLASH_OS_HPP + +#include + +// driver version. Do not modify +constexpr static uint16_t flash_drv_version = 0x101; + +// max amount of sectors in the flash device. Can be modified +// to allow more sectors in the flash device +constexpr static uint32_t max_sectors = 4; + +/** + * @brief Flash device types + * + */ +enum class device_type: uint8_t { + unknown = 0, + on_chip = 1, + external_8_bit = 2, + external_16_bit = 3, + external_32_bit = 4, + external_spi = 5 +}; + +/** + * @brief Information about a flash sector + * + */ +struct flash_sector { + // Sector size in bytes + uint32_t size; + + // Address offset on the base address + uint32_t offset; +}; + +// end of the sector list. Must be present at the end of the sector list +constexpr static flash_sector end_of_sectors = {0xffffffff, 0xffffffff}; + +/** + * @brief Information about the flash device + * + */ +struct flash_device { + // version number + uint16_t version; + + // name of the flash device. + char name[128]; + + // type of the flash device + device_type type; + + // flash base address + uint32_t base_address; + + // total flash size of the device + uint32_t size; + + // programming page size + uint32_t page_size; + + // reserved for future extension. should be 0 + uint32_t reserved; + + // value after erasing a sector/page (0xFF for most devices) + uint8_t erase_value; + + // timeout to program a page in msec + uint32_t programming_timeout; + + // timeout to erase a sector in msec + uint32_t erase_timeout; + + // flash sector layout definition + flash_sector sectors[max_sectors]; +}; + +/** + * @brief Extern C as the Segger application is only searching the + * elf for C functions. This prevents a error popup. + * + */ +extern "C" { + /** + * @brief Keil / CMSIS API + * + */ + + /** + * @brief Initialize Flash Programming Functions + * + * @warning Mandatory + * + * @param Addr Address to init + * @param Freq Clock frequency (Hz) + * @param Func function code. (1 - Erase, 2 = Program, 3 = Verify) + * @return int 0 = OK, 1 = Failed + */ + int Init(const uint32_t address, const uint32_t frequency, const uint32_t function); + + /** + * @brief De-Initialize Flash Programming Functions + * + * @warning Mandatory + * + * @param Func function code. (1 - Erase, 2 = Program, 3 = Verify) + * @return int 0 = OK, 1 = Failed + */ + int UnInit(const uint32_t function); + + /** + * @brief Erase Sector in Flash Memory + * + * @warning Mandatory + * + * @param Addr + * @return int 0 = OK, 1 = Failed + */ + int EraseSector(const uint32_t sector_address); + + /** + * @brief Program a page in flash memory + * + * @warning Mandatory + * + * @param Addr + * @param NumBytes + * @param pSrcBuff + * @return int 0 = OK, 1 = Failed + */ + int ProgramPage(const uint32_t address, const uint32_t size, const uint8_t *const data); + + /** + * @brief Checks if the flash is "Blank". Necessary if flash is not memory mapped + * + * @param Addr + * @param NumBytes + * @param BlankData + * @return int 0 = OK, 1 = OK not blank, < 0 = Error + */ + int BlankCheck(const uint32_t address, const uint32_t size, const uint8_t blank_data); + + /** + * @brief Erase the complete device. When not provided erase sector is called for + * every sector + * + * @return int 0 = OK, 1 = Failed + */ + int EraseChip(void); + + /** + * @brief Verifies the memory. Necessary if flash is not memory mapped + * + * @param Addr + * @param NumBytes + * @param pSrcBuff + * @return uint32_t == Address + Size = OK, != Address + Size = Failed + */ + uint32_t Verify(uint32_t Addr, uint32_t NumBytes, uint8_t *pSrcBuff); + + /** + * @brief Segger extensions + * + */ + + /** + * @brief Feed the watchdog of the device + * + */ + void FeedWatchdog(); + + /** + * @brief Read from memory. Necessary if flash is not memory mapped + * + * @param Addr + * @param NumBytes + * @param pDestBuff + * @return int < 0 = Error, >= 0 OK number of bytes read + */ + int SEGGER_OPEN_Read(uint32_t Addr, uint32_t NumBytes, uint8_t *pDestBuff); + + /** + * @brief Programs multiple pages at once in 1 ramcode call. Speeds up programming. + * + * @param DestAddr + * @param NumBytes + * @param pSrcBuff + * @return int 0 = OK, 1 = Failed + */ + int SEGGER_OPEN_Program(uint32_t DestAddr, uint32_t NumBytes, uint8_t *pSrcBuff); + + /** + * @brief Erases multiple pages at once. Only works when uniform pages are enabled + * + * @param SectorAddr + * @param SectorIndex + * @param NumSectors + * @return int 0 = OK, 1 = Failed + */ + int SEGGER_OPEN_Erase(uint32_t SectorAddr, uint32_t SectorIndex, uint32_t NumSectors); +} + +#endif \ No newline at end of file diff --git a/flash/main.cpp b/flash/main.cpp new file mode 100644 index 0000000..415dff7 --- /dev/null +++ b/flash/main.cpp @@ -0,0 +1,18 @@ +#include + +#include "flash_os.hpp" + +int main() { + // flag to mark if we want to run the test code + constexpr static bool debug = false; + + // check if we want to run the debug code + if constexpr (!debug) { + // we do not want to run the debug code exit + return 0; + } + + // TODO: add a example here to test the flash algorithm + + return 0; +}; \ No newline at end of file diff --git a/linkerscript.ld b/linkerscript.ld new file mode 100644 index 0000000..0fb1403 --- /dev/null +++ b/linkerscript.ld @@ -0,0 +1,191 @@ +SEARCH_DIR(.); + +/* +Format configurations +*/ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm"); +OUTPUT_ARCH(arm); + + +/* +The stack size used by the application. NOTE: you need to adjust according to your application. +*/ +STACK_SIZE = 0x100; + +/* +Memories definitions +*/ +MEMORY +{ + ram (rwx) : org = 0x20000000, len = 32k +} + +/* +Entry point +*/ +ENTRY( __reset_handler ); + +/* +Sections +*/ +SECTIONS +{ + /* Flash os information */ + PrgCode : + { + . = ALIGN(4); + KEEP(*(PrgCode PrgCode.*)); + . = ALIGN(4); + } > ram + + /* Vector table. Has the initial stack pointer and the initial + structure for the arm interrupts */ + .vectors : + { + . = ALIGN(128); + /* vector table */ + KEEP(*(.vectors .vectors.*)); + . = ALIGN(4); + } > ram + + /* Text segment, stores all user code */ + .text : + { + . = ALIGN(4); + /* text segment */ + *(.text .text.* .gnu.linkonce.t.*); + /* glue arm to thumb code, glue thumb to arm code*/ + *(.glue_7t .glue_7); + /* c++ exceptions */ + *(.eh_frame .eh_frame_hdr) + /* exception unwinding information */ + + . = ALIGN(4); + *(.ARM.extab* .gnu.linkonce.armextab.*); + *(.ARM.exidx*) + + . = ALIGN(4); + KEEP(*(.init)); + . = ALIGN(4); + KEEP(*(.fini)); + + . = ALIGN(4); + } > ram + + /* Read only data */ + .rodata : + { + . = ALIGN(4); + + /* Constands, strings, etc */ + *(.rodata .rodata.* .gnu.linkonce.r.*); + + . = ALIGN(4); + } > ram + + /* Support C constructors, and C destructors in both user code + and the C library. This also provides support for C++ code. */ + .preinit_array : + { + . = ALIGN(4); + PROVIDE(__preinit_array_start = .); + + KEEP(*(.preinit_array)) + + PROVIDE(__preinit_array_end = .); + } > ram + + .init_array : + { + . = ALIGN(4); + PROVIDE(__init_array_start = .); + + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + + PROVIDE(__init_array_end = .); + } > ram + + .fini_array : + { + . = ALIGN(4); + PROVIDE(__fini_array_start = .); + + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + + PROVIDE(__fini_array_end = .); + } > ram + + PrgData : + { + . = ALIGN(4); + KEEP(*(PrgData PrgData.*)) + . = ALIGN(4); + } > ram + + /* Data that needs to be initialized to a value different than 0 */ + .data : + { + . = ALIGN(4); + PROVIDE(__data_init_start = LOADADDR(.data)); + PROVIDE(__data_start = .); + + . = ALIGN(4); + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(4); + + PROVIDE(__data_end = .); + PROVIDE(__data_init_end = LOADADDR(.data)); + } > ram + + /* Data that needs to be initialized to 0 */ + .bss (NOLOAD) : + { + . = ALIGN(4); + PROVIDE(__bss_start = .); + + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON); + . = ALIGN(4); + + PROVIDE(__bss_end = .); + } > ram + + /* Stack segment */ + .stack (NOLOAD) : + { + . = ALIGN(8); + PROVIDE(__stack_start = .); + + . = . + STACK_SIZE; + PROVIDE(__stack_end = .); + } > ram + + /* Heap segment */ + .heap (NOLOAD) : + { + . = ALIGN(4); + PROVIDE(__heap_start = .); + PROVIDE(__heap_end = (ORIGIN(ram) + LENGTH(ram))); + } > ram + + /* Flash device information */ + DevDscr : + { + . = ALIGN(4); + KEEP(*(DevDscr DevDscr.*)); + . = ALIGN(4); + } > ram + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + *(.note.GNU-stack) + } + + .ARM.attributes 0 : { KEEP(*(.ARM.attributes)) } +}