マイコンの基礎固め5

前回までの記事でメモリの割り振りを決めてリンカスクリプトも作成したので、今回はマイコンが起動した後の処理について記述していく。

STM32CubeIDEで空のプロジェクトを作成すると「Startup」というフォルダの下に「startup_stm32f767zitx.s」などという名前でマイコンが起動した直後の処理を記述したアセンブラファイルが作成される。

内容を見てみたところ、ここで行われているのは以下の3つである。
1. .data領域の初期化
2. .bss領域の初期化
3. ISRベクタテーブルのメモリへの配置

マイコンの基礎固め3にも書いたように、私の場合はRAM上で実行する処理を保存する場所としてITCM上に.ramfuncセクションを、SRAM上にSRAM1とSRAM2専用のセクションとして.bss_sram1、.bss_sram2セクションを設けているため、これの初期化処理も追加する必要がある。
大した処理ではなかったのでアセンブラではなくCで書くことにした。

ISRベクタテーブルの配置コード

#define DEFAULT_ISR(name) void name(void) __attribute__((weak, alias("Default_Handler")))

extern void _estack(void);
extern void Reset_Handler(void);

/***************************************************************************************************
関数が定義されていない割込みベクタに飛んだ場合はこの関数が呼ばれる
***************************************************************************************************/
void Default_Handler(void)
{
    while(1);
}

/***************************************************************************************************
割込みベクタテーブルに配置するアドレスを取得するため、関数をweakで宣言する
宣言のみであり定義ではないため実体がなく、他のソースコード内で同じ名前の関数が定義されていない場合は
alias属性によりDefault_Handler関数が呼ばれる
***************************************************************************************************/
DEFAULT_ISR(NMI_Handler);
DEFAULT_ISR(HardFault_Handler);
DEFAULT_ISR(MemManage_Handler);
DEFAULT_ISR(BusFault_Handler);
DEFAULT_ISR(UsageFault_Handler);
DEFAULT_ISR(SVC_Handler);
DEFAULT_ISR(DebugMon_Handler);
DEFAULT_ISR(PendSV_Handler);
DEFAULT_ISR(SysTick_Handler);
DEFAULT_ISR(WWDG_IRQHandler);
DEFAULT_ISR(PVD_IRQHandler);
DEFAULT_ISR(TAMP_STAMP_IRQHandler);
DEFAULT_ISR(RTC_WKUP_IRQHandler);
DEFAULT_ISR(FLASH_IRQHandler);
DEFAULT_ISR(RCC_IRQHandler);
DEFAULT_ISR(EXTI0_IRQHandler);
DEFAULT_ISR(EXTI1_IRQHandler);
DEFAULT_ISR(EXTI2_IRQHandler);
DEFAULT_ISR(EXTI3_IRQHandler);
DEFAULT_ISR(EXTI4_IRQHandler);
DEFAULT_ISR(DMA1_Stream0_IRQHandler);
DEFAULT_ISR(DMA1_Stream1_IRQHandler);
DEFAULT_ISR(DMA1_Stream2_IRQHandler);
DEFAULT_ISR(DMA1_Stream3_IRQHandler);
DEFAULT_ISR(DMA1_Stream4_IRQHandler);
DEFAULT_ISR(DMA1_Stream5_IRQHandler);
DEFAULT_ISR(DMA1_Stream6_IRQHandler);
DEFAULT_ISR(ADC_IRQHandler);
DEFAULT_ISR(CAN1_TX_IRQHandler);
DEFAULT_ISR(CAN1_RX0_IRQHandler);
DEFAULT_ISR(CAN1_RX1_IRQHandler);
DEFAULT_ISR(CAN1_SCE_IRQHandler);
DEFAULT_ISR(EXTI9_5_IRQHandler);
DEFAULT_ISR(TIM1_BRK_TIM9_IRQHandler);
DEFAULT_ISR(TIM1_UP_TIM10_IRQHandler);
DEFAULT_ISR(TIM1_TRG_COM_TIM11_IRQHandler);
DEFAULT_ISR(TIM1_CC_IRQHandler);
DEFAULT_ISR(TIM2_IRQHandler);
DEFAULT_ISR(TIM3_IRQHandler);
DEFAULT_ISR(TIM4_IRQHandler);
DEFAULT_ISR(I2C1_EV_IRQHandler);
DEFAULT_ISR(I2C1_ER_IRQHandler);
DEFAULT_ISR(I2C2_EV_IRQHandler);
DEFAULT_ISR(I2C2_ER_IRQHandler);
DEFAULT_ISR(SPI1_IRQHandler);
DEFAULT_ISR(SPI2_IRQHandler);
DEFAULT_ISR(USART1_IRQHandler);
DEFAULT_ISR(USART2_IRQHandler);
DEFAULT_ISR(USART3_IRQHandler);
DEFAULT_ISR(EXTI15_10_IRQHandler);
DEFAULT_ISR(RTC_ALARM_IRQHandler);
DEFAULT_ISR(OTG_FS_WKUP_IRQHandler);
DEFAULT_ISR(TIM8_BRK_TIM12_IRQHandler);
DEFAULT_ISR(TIM8_UP_TIM13_IRQHandler);
DEFAULT_ISR(TIM8_TRG_COM_TIM14_IRQHandler);
DEFAULT_ISR(TIM8_CC_IRQHandler);
DEFAULT_ISR(DMA1_Stream7_IRQHandler);
DEFAULT_ISR(FMC_IRQHandler);
DEFAULT_ISR(SDMMC1_IRQHandler);
DEFAULT_ISR(TIM5_IRQHandler);
DEFAULT_ISR(SPI3_IRQHandler);
DEFAULT_ISR(UART4_IRQHandler);
DEFAULT_ISR(UART5_IRQHandler);
DEFAULT_ISR(TIM6_DAC_IRQHandler);
DEFAULT_ISR(TIM7_IRQHandler);
DEFAULT_ISR(DMA2_Stream0_IRQHandler);
DEFAULT_ISR(DMA2_Stream1_IRQHandler);
DEFAULT_ISR(DMA2_Stream2_IRQHandler);
DEFAULT_ISR(DMA2_Stream3_IRQHandler);
DEFAULT_ISR(DMA2_Stream4_IRQHandler);
DEFAULT_ISR(ETH_IRQHandler);
DEFAULT_ISR(ETH_WKUP_IRQHandler);
DEFAULT_ISR(CAN2_TX_IRQHandler);
DEFAULT_ISR(CAN2_RX0_IRQHandler);
DEFAULT_ISR(CAN2_RX1_IRQHandler);
DEFAULT_ISR(CAN2_SCE_IRQHandler);
DEFAULT_ISR(OTG_FS_IRQHandler);
DEFAULT_ISR(DMA2_Stream5_IRQHandler);
DEFAULT_ISR(DMA2_Stream6_IRQHandler);
DEFAULT_ISR(DMA2_Stream7_IRQHandler);
DEFAULT_ISR(USART6_IRQHandler);
DEFAULT_ISR(I2C3_EV_IRQHandler);
DEFAULT_ISR(I2C3_ER_IRQHandler);
DEFAULT_ISR(OTG_HS_EP1_OUT_IRQHandler);
DEFAULT_ISR(OTG_HS_EP1_IN_IRQHandler);
DEFAULT_ISR(OTG_HS_WKUP_IRQHandler);
DEFAULT_ISR(OTG_HS_IRQHandler);
DEFAULT_ISR(DCMI_IRQHandler);
DEFAULT_ISR(CRYP_IRQHandler);
DEFAULT_ISR(HASH_RNG_IRQHandler);
DEFAULT_ISR(FPU_IRQHandler);
DEFAULT_ISR(UART7_IRQHandler);
DEFAULT_ISR(UART8_IRQHandler);
DEFAULT_ISR(SPI4_IRQHandler);
DEFAULT_ISR(SPI5_IRQHandler);
DEFAULT_ISR(SPI6_IRQHandler);
DEFAULT_ISR(SAI1_IRQHandler);
DEFAULT_ISR(LCD_TFT_IRQHandler);
DEFAULT_ISR(LCD_TFT_1_IRQHandler);
DEFAULT_ISR(DMA2D_IRQHandler);
DEFAULT_ISR(SAI2_IRQHandler);
DEFAULT_ISR(QuadSPI_IRQHandler);
DEFAULT_ISR(LP_Timer1_IRQHandler);
DEFAULT_ISR(I2C4_EV_IRQHandler);
DEFAULT_ISR(I2C4_ER_IRQHandler);
DEFAULT_ISR(SPDIFRX_IRQHandler);
DEFAULT_ISR(DSIHOST_IRQHandler);
DEFAULT_ISR(DFSDM1_FLT0_IRQHandler);
DEFAULT_ISR(DFSDM1_FLT1_IRQHandler);
DEFAULT_ISR(DFSDM1_FLT2_IRQHandler);
DEFAULT_ISR(DFSDM1_FLT3_IRQHandler);
DEFAULT_ISR(SDMMC2_IRQHandler);
DEFAULT_ISR(CAN3_TX_IRQHandler);
DEFAULT_ISR(CAN3_RX0_IRQHandler);
DEFAULT_ISR(CAN3_RX1_IRQHandler);
DEFAULT_ISR(CAN3_SCE_IRQHandler);
DEFAULT_ISR(JPEG_IRQHandler);
DEFAULT_ISR(MDIOS_IRQHandler);

/***************************************************************************************************
割込みベクタテーブル
***************************************************************************************************/
__attribute__((section(".isr_vector"))) volatile const void (*isr_vector_table[])(void) =
{
    _estack,
    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,
    WWDG_IRQHandler,
    PVD_IRQHandler,
    TAMP_STAMP_IRQHandler,
    RTC_WKUP_IRQHandler,
    FLASH_IRQHandler,
    RCC_IRQHandler,
    EXTI0_IRQHandler,
    EXTI1_IRQHandler,
    EXTI2_IRQHandler,
    EXTI3_IRQHandler,
    EXTI4_IRQHandler,
    DMA1_Stream0_IRQHandler,
    DMA1_Stream1_IRQHandler,
    DMA1_Stream2_IRQHandler,
    DMA1_Stream3_IRQHandler,
    DMA1_Stream4_IRQHandler,
    DMA1_Stream5_IRQHandler,
    DMA1_Stream6_IRQHandler,
    ADC_IRQHandler,
    CAN1_TX_IRQHandler,
    CAN1_RX0_IRQHandler,
    CAN1_RX1_IRQHandler,
    CAN1_SCE_IRQHandler,
    EXTI9_5_IRQHandler,
    TIM1_BRK_TIM9_IRQHandler,
    TIM1_UP_TIM10_IRQHandler,
    TIM1_TRG_COM_TIM11_IRQHandler,
    TIM1_CC_IRQHandler,
    TIM2_IRQHandler,
    TIM3_IRQHandler,
    TIM4_IRQHandler,
    I2C1_EV_IRQHandler,
    I2C1_ER_IRQHandler,
    I2C2_EV_IRQHandler,
    I2C2_ER_IRQHandler,
    SPI1_IRQHandler,
    SPI2_IRQHandler,
    USART1_IRQHandler,
    USART2_IRQHandler,
    USART3_IRQHandler,
    EXTI15_10_IRQHandler,
    RTC_ALARM_IRQHandler,
    OTG_FS_WKUP_IRQHandler,
    TIM8_BRK_TIM12_IRQHandler,
    TIM8_UP_TIM13_IRQHandler,
    TIM8_TRG_COM_TIM14_IRQHandler,
    TIM8_CC_IRQHandler,
    DMA1_Stream7_IRQHandler,
    FMC_IRQHandler,
    SDMMC1_IRQHandler,
    TIM5_IRQHandler,
    SPI3_IRQHandler,
    UART4_IRQHandler,
    UART5_IRQHandler,
    TIM6_DAC_IRQHandler,
    TIM7_IRQHandler,
    DMA2_Stream0_IRQHandler,
    DMA2_Stream1_IRQHandler,
    DMA2_Stream2_IRQHandler,
    DMA2_Stream3_IRQHandler,
    DMA2_Stream4_IRQHandler,
    ETH_IRQHandler,
    ETH_WKUP_IRQHandler,
    CAN2_TX_IRQHandler,
    CAN2_RX0_IRQHandler,
    CAN2_RX1_IRQHandler,
    CAN2_SCE_IRQHandler,
    OTG_FS_IRQHandler,
    DMA2_Stream5_IRQHandler,
    DMA2_Stream6_IRQHandler,
    DMA2_Stream7_IRQHandler,
    USART6_IRQHandler,
    I2C3_EV_IRQHandler,
    I2C3_ER_IRQHandler,
    OTG_HS_EP1_OUT_IRQHandler,
    OTG_HS_EP1_IN_IRQHandler,
    OTG_HS_WKUP_IRQHandler,
    OTG_HS_IRQHandler,
    DCMI_IRQHandler,
    CRYP_IRQHandler,
    HASH_RNG_IRQHandler,
    FPU_IRQHandler,
    UART7_IRQHandler,
    UART8_IRQHandler,
    SPI4_IRQHandler,
    SPI5_IRQHandler,
    SPI6_IRQHandler,
    SAI1_IRQHandler,
    LCD_TFT_IRQHandler,
    LCD_TFT_1_IRQHandler,
    DMA2D_IRQHandler,
    SAI2_IRQHandler,
    QuadSPI_IRQHandler,
    LP_Timer1_IRQHandler,
    0,
    I2C4_EV_IRQHandler,
    I2C4_ER_IRQHandler,
    SPDIFRX_IRQHandler,
    DSIHOST_IRQHandler,
    DFSDM1_FLT0_IRQHandler,
    DFSDM1_FLT1_IRQHandler,
    DFSDM1_FLT2_IRQHandler,
    DFSDM1_FLT3_IRQHandler,
    SDMMC2_IRQHandler,
    CAN3_TX_IRQHandler,
    CAN3_RX0_IRQHandler,
    CAN3_RX1_IRQHandler,
    CAN3_SCE_IRQHandler,
    JPEG_IRQHandler,
    MDIOS_IRQHandler,
};


#define DEFAULT_ISR(name) void name(void) __attribute__((weak, alias("Default_Handler")))

となっているマクロがあるが、これは例えば「DEFAULT_ISR(NMI_Handler)」として使った場合に「NMI_Handler」という名前の関数が定義されていなければ「Default_Handler」という名前の関数として定義しなさいという意味である。
Default_Handler関数の処理は無限ループとしている。

attribute((section(".isr_vector"))) volatile const void (*isr_vector_table[])(void)

は「.isr_vectorセクションに isr_vector_tableという名前のvoid型関数のポインタのテーブルを配置しなさい」という意味である。

_estackとReset_Handlerはマイコンの基礎固め4の記事で書いたリンカスクリプトにて定義されているため、extern宣言を付けて定義することでCで書かれたファイルでも使えるようにしている。

セクションの初期化コード

#include "stm32f767xx.h"
#include "main.h"

extern uint32_t _sidata;
extern uint32_t _sdata;
extern uint32_t _edata;
extern uint32_t _siramfunc;
extern uint32_t _sramfunc;
extern uint32_t _eramfunc;
extern uint32_t _sbss;
extern uint32_t _ebss;
extern uint32_t _sbss_sram1;
extern uint32_t _ebss_sram1;
extern uint32_t _sbss_sram2;
extern uint32_t _ebss_sram2;

void Reset_Handler(void)
{
    uint32_t *lma_add;
    uint32_t *vma_add;
    uint32_t *end_add;
    
    //FLASHからITCM領域に.ramfuncセクションをコピー
    lma_add = &_siramfunc;
    vma_add = &_sramfunc;
    end_add = &_eramfunc;
    
    while(vma_add < end_add)
    {
        *vma_add = *lma_add;
        vma_add++;
        lma_add++;
    }
    
    //FLASHからDTCM領域に.dataセクションをコピー
    lma_add = &_sidata;
    vma_add = &_sdata;
    end_add = &_edata;
    
    while(vma_add < end_add)
    {
        *vma_add = *lma_add;
        vma_add++;
        lma_add++;
    }
    
    //DTCM領域のbssセクションを全て0クリア
    vma_add = &_sbss;
    end_add = &_ebss;
    
    while(vma_add < end_add)
    {
        *vma_add = 0;
        vma_add++;
    }
    
    //SRAM1領域はbssセクションとして使うため全て0クリア
    vma_add = &_sbss_sram1;
    end_add = &_ebss_sram1;
    
    while(vma_add < end_add)
    {
        *vma_add = 0;
        vma_add++;
    }
    
    //SRAM2領域はbssセクションとして使うため全て0クリア
    vma_add = &_sbss_sram2;
    end_add = &_ebss_sram2;
    
    while(vma_add < end_add)
    {
        *vma_add = 0;
        vma_add++;
    }
    
    main();
}

セクションの初期化は非常に簡単である。
まずリンカスクリプトにて定義されたロケーションカウンタのシンボルをextern宣言を付けることによってCで書かれたファイルでも使えるようにする。
ロケーションカウンタはつまりメモリのアドレスであるため、シンボル=メモリのアドレスということになる。

変数の初期値は全てROM領域(FLASH領域)にあるため、.dataセクションはシンボルのアドレスを使ってFLASH領域から初期値をコピーしている。
.ramfuncセクションの内容は関数(命令)であるが、これも同じくFLASH領域からコピーしてきてITCMに保存するだけである。

.bss、.bss_sram1、.bss_sram2のセクションについては全て0クリアしているだけである。

以上が終わったら最後にmain()を呼び出す。
今まで漠然とmainからコードを書いていたが実際にはリンカスクリプトまでさかのぼってこんな処理がされていたのかと勉強になった。