Most of the examples in this book use startup codes (or boot codes) that are written in assembly language. It is possible to have the startup codes written in C. However, this requires importing compiler-specific symbols and in some case compiler-specific directives. So the C startup codes are tool chain dependent, just like assembly.
An example C startup code for Keil MDK-ARM (for STM32L053C8T6 device)
#include <rt_misc.h>
// Define where the top of memory is.
#define TOP_OF_RAM 0x20002000U
extern void __main(void); // Use C-library initialization function.
extern void NMI_Handler(void);
extern void HardFault_Handler(void);
extern void Reset_Handler(void);
extern void SVC_Handler(void);
extern void PendSV_Handler(void);
extern void SysTick_Handler(void);
extern void WWDG_IRQHandler(void);
extern void PVD_IRQHandler(void);
extern void RTC_IRQHandler(void);
extern void FLASH_IRQHandler(void);
extern void RCC_CRS_IRQHandler(void);
extern void EXTI0_1_IRQHandler(void);
extern void EXTI2_3_IRQHandler(void);
extern void EXTI4_15_IRQHandler(void);
extern void TSC_IRQHandler(void);
extern void DMA1_Channel1_IRQHandler(void);
extern void DMA1_Channel2_3_IRQHandler(void);
extern void DMA1_Channel4_5_6_7_IRQHandler(void);
extern void ADC1_COMP_IRQHandler(void);
extern void LPTIM1_IRQHandler(void);
extern void TIM2_IRQHandler(void);
extern void TIM6_DAC_IRQHandler(void);
extern void TIM21_IRQHandler(void);
extern void TIM22_IRQHandler(void);
extern void I2C1_IRQHandler(void);
extern void I2C2_IRQHandler(void);
extern void SPI1_IRQHandler(void);
extern void SPI2_IRQHandler(void);
extern void USART1_IRQHandler(void);
extern void USART2_IRQHandler(void);
extern void RNG_LPUART1_IRQHandler(void);
extern void LCD_IRQHandler(void);
extern void USB_IRQHandler(void);
extern void SystemInit(void);
//------------------------------------------------------------------------------
// Define location of C stack and heap
//------------------------------------------------------------------------------
// Initialize stack and heap to span from the end of the zero-initialized
// region to the value defined by TOP_OF_RAM; see the "ARM Compiler toolchain
// Linker Reference" (ARM DUI 0493) and the "ARM Compiler toolchain Using ARM
// C and C++ Libraries and Floating-Point Support" (ARM DUI 0475) for further
// details.
extern unsigned int Image$$ZI$$Limit;
struct __initial_stackheap
__user_initial_stackheap
(unsigned int r0, // heap_base
unsigned int r1, // stack_base
unsigned int r2, // heap limit
unsigned int r3)__value_in_regs //stacklimit
{
struct __initial_stackheap sh;
sh.heap_base = Image$$ZI$$Limit;
sh.stack_base = TOP_OF_RAM; // Place Stack at top of SRAM
sh.heap_limit = sh.stack_base; // Or if Heap size is known
// sh.heap_limit = Image$$ZI$$Limit + HEAP_SIZE
sh.stack_limit = sh.heap_base; // Or if Stack size is known
// sh.stack_limit = TOP_OF_RAM - STACK_SIZE
return sh;
}
//------------------------------------------------------------------------------
// Implement the vector table in its own area to facilitate linking first
//------------------------------------------------------------------------------
typedef void(∗ const ExecFuncPtr)(void) __irq;
/∗ Place table in separate section ∗/
#pragma arm section rodata="Vectors"
__attribute__ ((section("Vectors")))
ExecFuncPtr __Vectors[] = {
(ExecFuncPtr) TOP_OF_RAM, // Initial value for stack pointer.
(ExecFuncPtr) __main, // Reset handler is C initialization.
(ExecFuncPtr) NMI_Handler, // NMI handler
(ExecFuncPtr) HardFault_Handler, // HardFault
0,
0,
0,
0,
0,
0,
0,
(ExecFuncPtr) SVC_Handler,
0,
0,
(ExecFuncPtr) PendSV_Handler,
(ExecFuncPtr) SysTick_Handler,
(ExecFuncPtr) WWDG_IRQHandler, // Window Watchdog
(ExecFuncPtr) PVD_IRQHandler, // PVD through EXTI Line detect
(ExecFuncPtr) RTC_IRQHandler, // RTC through EXTI Line
(ExecFuncPtr) FLASH_IRQHandler, // FLASH
(ExecFuncPtr) RCC_CRS_IRQHandler, // RCC and CRS
(ExecFuncPtr) EXTI0_1_IRQHandler, // EXTI Line 0 and 1
(ExecFuncPtr) EXTI2_3_IRQHandler, // EXTI Line 2 and 3
(ExecFuncPtr) EXTI4_15_IRQHandler, // EXTI Line 4 to 15
(ExecFuncPtr) TSC_IRQHandler, // TSC
(ExecFuncPtr) DMA1_Channel1_IRQHandler, // DMA1 Channel 1
(ExecFuncPtr) DMA1_Channel2_3_IRQHandler, // DMA1 Channel 2 and Channel 3
(ExecFuncPtr) DMA1_Channel4_5_6_7_IRQHandler, // DMA1 Channel 4 to 7
(ExecFuncPtr) ADC1_COMP_IRQHandler, // ADC1, COMP1 and COMP2
(ExecFuncPtr) LPTIM1_IRQHandler, // LPTIM1
0, // Reserved
(ExecFuncPtr) TIM2_IRQHandler, // TIM2
(ExecFuncPtr) 0, // Reserved
(ExecFuncPtr) TIM6_DAC_IRQHandler, // TIM6 and DAC
0, // Reserved
0, // Reserved
(ExecFuncPtr) TIM21_IRQHandler, // TIM21
0, // Reserved
(ExecFuncPtr) TIM22_IRQHandler, // TIM22
(ExecFuncPtr) I2C1_IRQHandler, // I2C1
(ExecFuncPtr) I2C2_IRQHandler, // I2C2
(ExecFuncPtr) SPI1_IRQHandler, // SPI1
(ExecFuncPtr) SPI2_IRQHandler, // SPI2
(ExecFuncPtr) USART1_IRQHandler, // USART1
(ExecFuncPtr) USART2_IRQHandler, // USART2
(ExecFuncPtr) RNG_LPUART1_IRQHandler, // RNG and LPUART1
(ExecFuncPtr) LCD_IRQHandler, // LCD
(ExecFuncPtr) USB_IRQHandler, // USB
};
#pragma arm section
void Reset_Handler(void)
{
SystemInit();
__main();
}
__attribute__ ((weak)) void NMI_Handler(void)
{ while(1); }
__attribute__ ((weak)) void HardFault_Handler(void)
{ while(1); }
__attribute__ ((weak)) void SVC_Handler(void)
{ while(1); }
__attribute__ ((weak)) void PendSV_Handler(void)
{ while(1); }
__attribute__ ((weak)) void SysTick_Handler(void)
{ while(1); }
__attribute__ ((weak)) void WWDG_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void PVD_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void RTC_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void FLASH_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void RCC_CRS_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void EXTI0_1_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void EXTI2_3_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void EXTI4_15_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void TSC_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void DMA1_Channel1_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void DMA1_Channel2_3_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void DMA1_Channel4_5_6_7_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void ADC1_COMP_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void LPTIM1_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void TIM2_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void TIM6_DAC_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void TIM21_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void TIM22_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void I2C1_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void I2C2_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void SPI1_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void SPI2_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void USART1_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void USART2_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void RNG_LPUART1_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void LCD_IRQHandler(void)
{ while(1); }
__attribute__ ((weak)) void USB_IRQHandler(void)
{ while(1); }
In typical software development environment, the example software package provided by the microcontroller vendor would have the startup code and header files for various tool chains. So you do not have to worry about creating your own startup code and header files for the microcontroller devices.
The initial value for the Main Stack Pointer (MSP), however, still needs to point to a RAM region that does not require initialization because some of the exceptions (e.g., NMI, HardFault) could happen at the beginning of the boot process.