DA14580 啓動過程分析

IC Booting

官方原話:

The DA1458x operates in two modes, namely the ‘Normal Mode’ and the ‘Development/Calibration Mode’ hereafter addressed as ‘DevMode’. The decision which mode the chip enters after power-up, is taken by the boot code residing in the ROM. A complete flow chart of the booting code is illustrated in the datasheet of the DA1458x.

DevMode will be entered when the OTP header contains a value zero at the first two addresses, when read by the CPU. This implies that the OTP is not programmed and the DA1458x should switch to the DevMode, so that users get access to download code from external devices into the internal SRAM (SysRAM). However, when the OTP contains specific values (magic numbers) in these locations the DA1458x will enter Normal Mode and proceed with mirroring of the OTP contents into the SysRAM in the booting sequence, as described in the datasheet.

To allow for maximum flexibility, a predefined number of pins are examined and utilised at boot time to communicate with external devices using the three serial interfaces available on chip: UART, SPI and I2C. SPI and I2C can be masters on the DA1458x side expecting to communicate with an external slave device and SPI can also be slave expecting to communicate with an external master.

渣翻譯:

DA1458x工作在兩種模式下,即“Normal Mode”和“Development/Calibration Mode”,以下稱爲“DevMode”。上電之後芯片進入模式的決定由駐留在ROM中的引導代碼取得。 DA1458x的數據表中給出了引導代碼的完整流程圖。

當OTP頭在CPU的前兩個地址中包含零值時,意味着OTP沒有被編程,DA1458x將會切換到DevMode,以便用戶可以訪問從外部設備下載代碼到內部SRAM(SysRAM)。但是,當OTP在這些位置包含特定值(magic numbers幻數)時,DA1458x將進入正常模式,並按照數據手冊中的說明,在引導順序中將OTP內容鏡像到SysRAM中。

爲了實現最大的靈活性,在引導時檢查並使用預定義數量的引腳,以使用芯片上提供的三個串行接口(UART,SPI和I2C)與外部設備進行通信。 SPI和I2C可以作爲DA1458x端的主設備,期望與外部從設備進行通信,SPI也可以作爲從設備與外部主設備進行通信。

在啓動過程中,端口P0用於檢查是否存在外部設備。 下面的表1給出了引腳分配以及引導代碼激活接口的順序。

Table 1
Table 1

將OTP的代碼裝載到RAM

OTP 即 One Time Programmable (memory) 只能燒錄一次。

如果對成本要求嚴格,可以將最終出貨版本的代碼燒錄在OTP,這樣就不用外掛Flash,可以有效降低成本。將OTP的代碼裝載到RAM後,啓動流程也就到此爲止了。

流程圖如下:

secondary_bootloader booting

如果需要進行OTA升級操作,則需要外掛Flash。這時需要將OTP內燒錄爲secondary_bootloader,進行二次引導,啓動Flash中的代碼。

從secondary_bootloader到Flash中的用戶代碼

secondary_bootloader的代碼在\utilities\secondary_bootloader路徑下,開發者可以根據自己的產品需求而定製自己的secondary_bootloader。

官方默認的secondary_bootloader是從Flash 0x1f000地址讀取跳轉的頭信息。根據頭信息,選擇執行到Firmware1 的代碼還是 Firmware2 的代碼。

官方默認的flash layout如下
flash layout

從secondary_bootloader到用戶代碼的啓動流程如下:

啓動用戶代碼流程

關於arch_main函數的分析如下

我們先來看下main_func函數代碼

int main_func(void)
{
    sleep_mode_t sleep_mode;

    //global initialise
    system_init();

    /*
     ************************************************************************************
     * Platform initialization
     ************************************************************************************
     */
    while(1)
    {
        do {
            // schedule all pending events
            schedule_while_ble_on();
        }
        while (app_asynch_proc() != GOTO_SLEEP);    //grant control to the application, try to go to power down
                                                              //if the application returns GOTO_SLEEP
              //((STREAMDATA_QUEUE)&& stream_queue_more_data())); //grant control to the streamer, try to go to power down
                                                                //if the application returns GOTO_SLEEP

        //wait for interrupt and go to sleep if this is allowed
        if (((!BLE_APP_PRESENT) && (check_gtl_state())) || (BLE_APP_PRESENT))
        {
          ...//省略
        }
    }
}

system_init();函數完成了系統的初始化操作。

主循環由兩部分組成。 第一部分在CPU處於活動狀態時執行,只要內核或應用程序希望CPU保持活動狀態。 在主循環的第二部分,程序試圖進入掉電模式。 它將嘗試關閉BLE硬件,然後關閉其餘外設,並在等待來自某個外部引腳或BLE編程事件的中斷(WFI)的同時將CPU置於低功耗狀態。

系統初始化

system_init();函數完成了系統的初始化操作。
具體代碼如下

/**
 ****************************************************************************************
 * @brief  Global System Init
 * @return void
 ****************************************************************************************
 */
void system_init(void)
{
    sys_startup_flag = true;
    /*
     ************************************************************************************
     * Platform initialization
     ************************************************************************************
     */
    //initialise the Watchdog unit
    wdg_init(0);

    //confirm XTAL 16 MHz calibration
    xtal16_calibration_check ();

    //set the system clocks
    set_system_clocks();

    //Initiliaze the GPIOs
    GPIO_init();

    // Initialize NVDS module
    nvds_init((uint8_t *)NVDS_FLASH_ADDRESS, NVDS_FLASH_SIZE);

    // Check and read BD address
    nvds_read_bdaddr();

    //Peripherals initilization
    periph_init();

    // Initialize random process
    srand(1);

    //Trim the radio from the otp
    iq_trim_from_otp();

    /*
     ************************************************************************************
     * BLE initialization
     ************************************************************************************
     */
    init_pwr_and_clk_ble();

    // Initialize BLE stack
    rwip_clear_interrupts ();

    // Initialize rw
    rwip_init(error);

    //Patch llm task
    patch_llm_task();

    //Patch gtl task
    if (BLE_GTL_PATCH)
        patch_gtl_task();

#if !defined( __DA14581__)
    //Patch llc task
    patch_llc_task();
#endif

#if !defined(__DA14581__)
    patch_atts_task();
#endif

#if (BLE_HOST_PRESENT)
    patch_gapc_task();
#endif

    //Enable the BLE core
    SetBits32(BLE_RWBTLECNTL_REG, RWBLE_EN, 1);

    // Initialise random number generator seed using random bits acquired from TRNG
    if (USE_TRNG)
        init_rand_seed_from_trng();

    //send a message to the host if an error occured
    if (BLE_HCIC_ITF)
        hcic_reset_message();

    rcx20_calibrate ();

    arch_disable_sleep();
    /*
     ************************************************************************************
     * Application initializations
     ************************************************************************************
     */
    // Initialise APP

#if (BLE_APP_PRESENT)
        app_init();         // Initialize APP
#endif

     if (user_app_main_loop_callbacks.app_on_init !=NULL)
          user_app_main_loop_callbacks.app_on_init();

    //Initialise lld_sleep
    lld_sleep_init_func();

    /*
     ************************************************************************************
     * XTAL16M trimming settings
     ************************************************************************************
     */
    //set trim and bias of xtal16
    xtal16__trim_init();

     // Enable the TX_EN/RX_EN interrupts, depending on the RF mode of operation (PLL-LUT and MGC_KMODALPHA combinations)
    enable_rf_diag_irq(RF_DIAG_IRQ_MODE_RXTX);

    /*
     ************************************************************************************
     * Watchdog
     ************************************************************************************
     */
    if(USE_WDOG)
    {
        wdg_reload(WATCHDOG_DEFAULT_PERIOD);
        wdg_resume ();
    }

#ifndef __DA14581__
# if (BLE_CONNECTION_MAX_USER > 4)
    cs_table[0] = cs_table[0];
# endif

#else //DA14581

# if (BLE_CONNECTION_MAX_USER > 1)
    cs_table[0] = cs_table[0];
# endif
#endif //__DA14581__

}

Main Loop

在SDK版本5.0.2(或更高版本)中,Main Loop被重寫了。我們不應該隨便主Main Loop,應將它認爲是SDK的一部分。不過我們可以通過Main Loop 來理解應用程序的流程。
main loop

主循環由兩部分組成。 第一部分在CPU處於活動狀態時執行,只要內核或應用程序希望CPU保持活動狀態。 在主循環的第二部分,程序試圖進入掉電模式。 它將嘗試關閉BLE硬件,然後關閉其餘外設,並在等待來自某個外部引腳或BLE編程事件的中斷(WFI)的同時將CPU置於低功耗狀態。

Main Loop 的活動部分

通過rwip_schedule()函數讓內核來進行調度控制。(注:rwip_schedule()在schedule_while_ble_on()函數中被調用。因爲內核要運行的話,BLE硬件需處於活動狀態。)

在schedule_while_ble_on()內,應用程序也通過user_app_main_loop_callbacks.app_on_ble_powered函數指針授予控制權。

根據app_on_ble_powered的返回值,應用程序可以強制主循環保持在schedule_while_ble_on()內,因爲BLE保持活動狀態。

如果應用程序和內核都允許scheduler_while_ble_on()返回,則將通過app_asynch_proc()函數和user_app_main_loop_callbacks.app_on_system_powered函數指針,再次獲得控制權限,決定是否開始進入休眠。

app_on_system_powered的返回值與while循環類似。如果應用程序決定不做其他事情,軟件將嘗試進入休眠狀態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章