From d8e22e0b118dde1560483ffd76830f5707933554 Mon Sep 17 00:00:00 2001 From: Silvano Seva Date: Mon, 18 Dec 2023 08:12:49 +0100 Subject: [PATCH] Boot code for AT32F421 --- .../mcu/AT32F421/boot/arch_registers_impl.h | 11 + platform/mcu/AT32F421/boot/bsp.cpp | 69 +++++ .../mcu/AT32F421/boot/libc_integration.cpp | 61 +++++ platform/mcu/AT32F421/boot/startup.cpp | 243 ++++++++++++++++++ platform/mcu/AT32F421/linker_script.ld | 165 ++++++++++++ 5 files changed, 549 insertions(+) create mode 100644 platform/mcu/AT32F421/boot/arch_registers_impl.h create mode 100644 platform/mcu/AT32F421/boot/bsp.cpp create mode 100644 platform/mcu/AT32F421/boot/libc_integration.cpp create mode 100644 platform/mcu/AT32F421/boot/startup.cpp create mode 100644 platform/mcu/AT32F421/linker_script.ld diff --git a/platform/mcu/AT32F421/boot/arch_registers_impl.h b/platform/mcu/AT32F421/boot/arch_registers_impl.h new file mode 100644 index 00000000..47256cc8 --- /dev/null +++ b/platform/mcu/AT32F421/boot/arch_registers_impl.h @@ -0,0 +1,11 @@ + +#ifndef ARCH_REGISTERS_IMPL_H +#define ARCH_REGISTERS_IMPL_H + +#include "at32f421.h" +#include "core_cm4.h" +#include "system_at32f421.h" + +#define RCC_SYNC() //Workaround for a bug in stm32f42x + +#endif //ARCH_REGISTERS_IMPL_H diff --git a/platform/mcu/AT32F421/boot/bsp.cpp b/platform/mcu/AT32F421/boot/bsp.cpp new file mode 100644 index 00000000..e706939f --- /dev/null +++ b/platform/mcu/AT32F421/boot/bsp.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2023 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO, * + * Federico Terraneo * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +/*********************************************************************** +* bsp.cpp Part of the Miosix Embedded OS. +* Board support package, this file initializes hardware. +************************************************************************/ + +#include +#include +#include + +namespace miosix +{ + +// +// Initialization +// + +void IRQbspInit() +{ + CRM->ahben |= (1 << 17) // Enable GPIOA + | (1 << 18) // Enable GPIOB + | (1 << 19) // Enable GPIOC + | (1 << 22); // Enable GPIOF + + // Configure SysTick + SysTick->LOAD = SystemCoreClock / miosix::TICK_FREQ; +} + +void bspInit2() +{ + +} + +// +// Shutdown and reboot +// + +void shutdown() +{ + reboot(); +} + +void reboot() +{ + disableInterrupts(); + miosix_private::IRQsystemReboot(); +} + +} //namespace miosix diff --git a/platform/mcu/AT32F421/boot/libc_integration.cpp b/platform/mcu/AT32F421/boot/libc_integration.cpp new file mode 100644 index 00000000..91a103ef --- /dev/null +++ b/platform/mcu/AT32F421/boot/libc_integration.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2023 by Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include + +using namespace std; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \internal + * _write_r, write to a file + */ +int _write_r(struct _reent *ptr, int fd, const void *buf, size_t cnt) +{ + (void) ptr; + (void) fd; + (void) buf; + (void) cnt; + + /* If fd is not stdout or stderr */ + ptr->_errno = EBADF; + return -1; +} + +/** + * \internal + * _read_r, read from a file. + */ +int _read_r(struct _reent *ptr, int fd, void *buf, size_t cnt) +{ + (void) ptr; + (void) fd; + (void) buf; + (void) cnt; + ptr->_errno = EBADF; + + return -1; +} + +#ifdef __cplusplus +} +#endif diff --git a/platform/mcu/AT32F421/boot/startup.cpp b/platform/mcu/AT32F421/boot/startup.cpp new file mode 100644 index 00000000..d81800d0 --- /dev/null +++ b/platform/mcu/AT32F421/boot/startup.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + * Copyright (C) 2023 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO, * + * Federico Terraneo * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +/* + * startup.cpp + * STM32 C++ startup. + * NOTE: for stm32f4 devices ONLY. + * Supports interrupt handlers in C++ without extern "C" + * Developed by Terraneo Federico, based on ST startup code. + * Additionally modified to boot Miosix. + */ + +#include "interfaces/arch_registers.h" +#include "kernel/stage_2_boot.h" +#include "core/interrupts.h" //For the unexpected interrupt call +#include + +/** + * Called by Reset_Handler, performs initialization and calls main. + * Never returns. + */ +void program_startup() __attribute__((noreturn)); +void program_startup() +{ + //Cortex M3 core appears to get out of reset with interrupts already enabled + __disable_irq(); + + //SystemInit() is called *before* initializing .data and zeroing .bss + //Despite all startup files provided by ST do the opposite, there are three + //good reasons to do so: + //First, the CMSIS specifications say that SystemInit() must not access + //global variables, so it is actually possible to call it before + //Second, when running Miosix with the xram linker scripts .data and .bss + //are placed in the external RAM, so we *must* call SystemInit(), which + //enables xram, before touching .data and .bss + //Third, this is a performance improvement since the loops that initialize + //.data and zeros .bss now run with the CPU at full speed instead of 8MHz + SystemInit(); + + //These are defined in the linker script + extern unsigned char _etext asm("_etext"); + extern unsigned char _data asm("_data"); + extern unsigned char _edata asm("_edata"); + extern unsigned char _bss_start asm("_bss_start"); + extern unsigned char _bss_end asm("_bss_end"); + + //Initialize .data section, clear .bss section + unsigned char *etext=&_etext; + unsigned char *data=&_data; + unsigned char *edata=&_edata; + unsigned char *bss_start=&_bss_start; + unsigned char *bss_end=&_bss_end; + memcpy(data, etext, edata-data); + memset(bss_start, 0, bss_end-bss_start); + + //Move on to stage 2 + _init(); + + //If main returns, reboot + NVIC_SystemReset(); + for(;;) ; +} + +/** + * Reset handler, called by hardware immediately after reset + */ +void Reset_Handler() __attribute__((__interrupt__, noreturn)); +void Reset_Handler() +{ + /* + * Initialize process stack and switch to it. + * This is required for booting Miosix, a small portion of the top of the + * heap area will be used as stack until the first thread starts. After, + * this stack will be abandoned and the process stack will point to the + * current thread's stack. + */ + asm volatile("ldr r0, =_heap_end \n\t" + "msr psp, r0 \n\t" + "movw r0, #2 \n\n" //Privileged, process stack + "msr control, r0 \n\t" + "isb \n\t":::"r0"); + + program_startup(); +} + +/** + * All unused interrupts call this function. + */ +extern "C" void Default_Handler() +{ + unexpectedInterrupt(); +} + +//System handlers +void /*__attribute__((weak))*/ Reset_Handler(); //These interrupts are not +void /*__attribute__((weak))*/ NMI_Handler(); //weak because they are +void /*__attribute__((weak))*/ HardFault_Handler(); //surely defined by Miosix +void /*__attribute__((weak))*/ MemManage_Handler(); +void /*__attribute__((weak))*/ BusFault_Handler(); +void /*__attribute__((weak))*/ UsageFault_Handler(); +void /*__attribute__((weak))*/ SVC_Handler(); +void /*__attribute__((weak))*/ DebugMon_Handler(); +void /*__attribute__((weak))*/ PendSV_Handler(); +void /*__attribute__((weak))*/ SysTick_Handler(); + +//Interrupt handlers +void __attribute__((weak)) WWDT_IRQHandler(); +void __attribute__((weak)) PVM_IRQHandler(); +void __attribute__((weak)) ERTC_IRQHandler(); +void __attribute__((weak)) FLASH_IRQHandler(); +void __attribute__((weak)) CRM_IRQHandler(); +void __attribute__((weak)) EXINT1_0_IRQHandler(); +void __attribute__((weak)) EXINT3_2_IRQHandler(); +void __attribute__((weak)) EXINT15_4_IRQHandler(); +void __attribute__((weak)) DMA1_Channel1_IRQHandler(); +void __attribute__((weak)) DMA1_Channel3_2_IRQHandler(); +void __attribute__((weak)) DMA1_Channel5_4_IRQHandler(); +void __attribute__((weak)) ADC1_CMP_IRQHandler(); +void __attribute__((weak)) TMR1_BRK_OVF_TRG_HALL_IRQHandler(); +void __attribute__((weak)) TMR1_CH_IRQHandler(); +void __attribute__((weak)) TMR3_GLOBAL_IRQHandler(); +void __attribute__((weak)) TMR6_GLOBAL_IRQHandler(); +void __attribute__((weak)) TMR14_GLOBAL_IRQHandler(); +void __attribute__((weak)) TMR15_GLOBAL_IRQHandler(); +void __attribute__((weak)) TMR16_GLOBAL_IRQHandler(); +void __attribute__((weak)) TMR17_GLOBAL_IRQHandler(); +void __attribute__((weak)) I2C1_EVT_IRQHandler(); +void __attribute__((weak)) I2C2_EVT_IRQHandler(); +void __attribute__((weak)) SPI1_IRQHandler(); +void __attribute__((weak)) SPI2_IRQHandler(); +void __attribute__((weak)) USART1_IRQHandler(); +void __attribute__((weak)) USART2_IRQHandler(); +void __attribute__((weak)) I2C1_ERR_IRQHandler(); +void __attribute__((weak)) I2C2_ERR_IRQHandler(); + +//Stack top, defined in the linker script +extern char _main_stack_top asm("_main_stack_top"); + +//Interrupt vectors, must be placed @ address 0x00000000 +//The extern declaration is required otherwise g++ optimizes it out +extern void (* const __Vectors[])(); +void (* const __Vectors[])() __attribute__ ((section(".isr_vector"))) = +{ + reinterpret_cast(&_main_stack_top),/* Stack pointer*/ + Reset_Handler, + NMI_Handler, + HardFault_Handler, + MemManage_Handler, + BusFault_Handler, + UsageFault_Handler, + 0, + 0, + 0, + 0, + SVC_Handler, + DebugMon_Handler, + 0, + PendSV_Handler, + SysTick_Handler, + + WWDT_IRQHandler, /* Window Watchdog Timer */ + PVM_IRQHandler, /* PVM through EXINT Line detect */ + ERTC_IRQHandler, /* ERTC */ + FLASH_IRQHandler, /* Flash */ + CRM_IRQHandler, /* CRM */ + EXINT1_0_IRQHandler, /* EXINT Line 1 & 0 */ + EXINT3_2_IRQHandler, /* EXINT Line 3 & 2 */ + EXINT15_4_IRQHandler, /* EXINT Line 15 ~ 4 */ + 0, /* Reserved */ + DMA1_Channel1_IRQHandler, /* DMA1 Channel 1 */ + DMA1_Channel3_2_IRQHandler, /* DMA1 Channel 3 & 2 */ + DMA1_Channel5_4_IRQHandler, /* DMA1 Channel 5 & 4 */ + ADC1_CMP_IRQHandler, /* ADC1 & Comparator */ + TMR1_BRK_OVF_TRG_HALL_IRQHandler, /* TMR1 brake overflow trigger and hall */ + TMR1_CH_IRQHandler, /* TMR1 channel */ + 0, /* Reserved */ + TMR3_GLOBAL_IRQHandler, /* TMR3 */ + TMR6_GLOBAL_IRQHandler, /* TMR6 */ + 0, /* Reserved */ + TMR14_GLOBAL_IRQHandler, /* TMR14 */ + TMR15_GLOBAL_IRQHandler, /* TMR15 */ + TMR16_GLOBAL_IRQHandler, /* TMR16 */ + TMR17_GLOBAL_IRQHandler, /* TMR17 */ + I2C1_EVT_IRQHandler, /* I2C1 Event */ + I2C2_EVT_IRQHandler, /* I2C2 Event */ + SPI1_IRQHandler, /* SPI1 */ + SPI2_IRQHandler, /* SPI2 */ + USART1_IRQHandler, /* USART1 */ + USART2_IRQHandler, /* USART2 */ + 0, /* Reserved */ + 0, /* Reserved */ + 0, /* Reserved */ + I2C1_ERR_IRQHandler, /* I2C1 Error */ + 0, /* Reserved */ + I2C2_ERR_IRQHandler /* I2C2 Error */ +}; + +#pragma weak WWDT_IRQHandler = Default_Handler +#pragma weak PVM_IRQHandler = Default_Handler +#pragma weak ERTC_IRQHandler = Default_Handler +#pragma weak FLASH_IRQHandler = Default_Handler +#pragma weak CRM_IRQHandler = Default_Handler +#pragma weak EXINT1_0_IRQHandler = Default_Handler +#pragma weak EXINT3_2_IRQHandler = Default_Handler +#pragma weak EXINT15_4_IRQHandler = Default_Handler +#pragma weak DMA1_Channel1_IRQHandler = Default_Handler +#pragma weak DMA1_Channel3_2_IRQHandler = Default_Handler +#pragma weak DMA1_Channel5_4_IRQHandler = Default_Handler +#pragma weak ADC1_CMP_IRQHandler = Default_Handler +#pragma weak TMR1_BRK_OVF_TRG_HALL_IRQHandler = Default_Handler +#pragma weak TMR1_CH_IRQHandler = Default_Handler +#pragma weak TMR3_GLOBAL_IRQHandler = Default_Handler +#pragma weak TMR6_GLOBAL_IRQHandler = Default_Handler +#pragma weak TMR14_GLOBAL_IRQHandler = Default_Handler +#pragma weak TMR15_GLOBAL_IRQHandler = Default_Handler +#pragma weak TMR16_GLOBAL_IRQHandler = Default_Handler +#pragma weak TMR17_GLOBAL_IRQHandler = Default_Handler +#pragma weak I2C1_EVT_IRQHandler = Default_Handler +#pragma weak I2C2_EVT_IRQHandler = Default_Handler +#pragma weak SPI1_IRQHandler = Default_Handler +#pragma weak SPI2_IRQHandler = Default_Handler +#pragma weak USART1_IRQHandler = Default_Handler +#pragma weak USART2_IRQHandler = Default_Handler +#pragma weak I2C1_ERR_IRQHandler = Default_Handler +#pragma weak I2C2_ERR_IRQHandler = Default_Handler diff --git a/platform/mcu/AT32F421/linker_script.ld b/platform/mcu/AT32F421/linker_script.ld new file mode 100644 index 00000000..3871e3f0 --- /dev/null +++ b/platform/mcu/AT32F421/linker_script.ld @@ -0,0 +1,165 @@ +/* + * C++ enabled linker script + * Developed by TFT: Terraneo Federico Technologies + * Optimized for use with the Miosix kernel + */ + +/* + * This linker script puts: + * - read only data and code (.text, .rodata, .eh_*) in flash + * - stacks, heap and sections .data and .bss in the internal ram + * - the external ram (if available) is not used. + */ + +/* + * The main stack is used for interrupt handling by the kernel. + * + * *** Readme *** + * This linker script places the main stack (used by the kernel for interrupts) + * at the bottom of the ram, instead of the top. This is done for two reasons: + * + * - as an optimization for microcontrollers with little ram memory. In fact + * the implementation of malloc from newlib requests memory to the OS in 4KB + * block (except the first block that can be smaller). This is probably done + * for compatibility with OSes with an MMU and paged memory. To see why this + * is bad, consider a microcontroller with 8KB of ram: when malloc finishes + * up the first 4KB it will call _sbrk_r asking for a 4KB block, but this will + * fail because the top part of the ram is used by the main stack. As a + * result, the top part of the memory will not be used by malloc, even if + * available (and it is nearly *half* the ram on an 8KB mcu). By placing the + * main stack at the bottom of the ram, the upper 4KB block will be entirely + * free and available as heap space. + * + * - In case of main stack overflow the cpu will fault because access to memory + * before the beginning of the ram faults. Instead with the default stack + * placement the main stack will silently collide with the heap. + * Note: if increasing the main stack size also increase the ORIGIN value in + * the MEMORY definitions below accordingly. + */ +_main_stack_size = 0x00000200; /* main stack = 512Bytes */ +_main_stack_top = 0x20000000 + _main_stack_size; +ASSERT(_main_stack_size % 8 == 0, "MAIN stack size error"); + +_heap_end = 0x20004000; /* end of available ram */ + +/* identify the Entry Point */ +ENTRY(_Z13Reset_Handlerv) + +/* specify the memory areas */ +MEMORY +{ + flash(rx) : ORIGIN = 0x08001400, LENGTH = 59K + ram(wx) : ORIGIN = 0x20000200, LENGTH = 16K - 0x200 +} + +/* now define the output sections */ +SECTIONS +{ + . = 0; + + /* .text section: code goes to flash */ + .text : + { + /* Startup code must go at address 0 */ + KEEP(*(.isr_vector)) + + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + /* these sections for thumb interwork? */ + *(.glue_7) + *(.glue_7t) + /* these sections for C++? */ + *(.gcc_except_table) + *(.gcc_except_table.*) + *(.ARM.extab*) + *(.gnu.linkonce.armextab.*) + + . = ALIGN(4); + /* .rodata: constant data */ + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r.*) + + /* C++ Static constructors/destructors (eabi) */ + . = ALIGN(4); + KEEP(*(.init)) + + . = ALIGN(4); + __miosix_init_array_start = .; + KEEP (*(SORT(.miosix_init_array.*))) + KEEP (*(.miosix_init_array)) + __miosix_init_array_end = .; + + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + + . = ALIGN(4); + KEEP(*(.fini)) + + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + + /* C++ Static constructors/destructors (elf) */ + . = ALIGN(4); + _ctor_start = .; + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*crtend.o(.ctors)) + _ctor_end = .; + + . = ALIGN(4); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*crtend.o(.dtors)) + } > flash + + /* .ARM.exidx is sorted, so has to go in its own output section. */ + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > flash + __exidx_end = .; + + /* .data section: global variables go to ram, but also store a copy to + flash to initialize them */ + .data : ALIGN(8) + { + _data = .; + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + . = ALIGN(8); + _edata = .; + } > ram AT > flash + + _etext = LOADADDR(.data); + + /* .bss section: uninitialized global variables go to ram */ + _bss_start = .; + .bss : + { + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b.*) + . = ALIGN(8); + } > ram + _bss_end = .; + + _end = .; + PROVIDE(end = .); +}