Dear community,
I am developing a bootloader for the rt1024-evk evaluation board which receives the application data via canbus and flashes it to a custom location on the onboard flexspi flash.
The bootloader is located at position 0 on the flash (0x60000000)
The application should be located at an offset, for example if the bootloader size is ~0x9000 bytes i would place the app at 0x60000000 + 0xA000 to be on the next flash-sector.
Now i have two questions:
1: First of all how do i build and flash the application correctly to place it at the offset in flash (just to test if my bootloader would jump to the application)?
- i have tried using the led blinky example for the imxrt1024 and changed in the memory settings under project setting the flash location to 0x6000A000 and flashed the app via the gui flash tool
2. how to i correctly jump to the application?
- i have tried the attached code (void jump_to_application()), which i reduced from the flashloader example - together with the blinky example hopefully flashed at 0x6000A000 but it didn't work.
Note: - bootloader and application should be on the the flash
- my bootloader is based on the flexspi_nor_polling_transfer example which includes some linkscript modifications (attached as well). The .ldt files are not modified in comparison to the example project, but i had to remove the one for .bss, otherwise it would instantly run into a hardfault.
/////////////////////////////////////////////////////////
jump_to_application code:
/*
* application.cpp
*
*/
#include "utils/timer_counter.h"
#include "../utilities/serial_debug.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "application.h"
#include "../fusemap.h"
#define BL_APP_VECTOR_TABLE_ADDRESS (0x6000A000u)
#define APP_VECTOR_TABLE ((uint32_t *)BL_APP_VECTOR_TABLE_ADDRESS)
#define FREQ_396MHz (396UL * 1000 * 1000)
#define FREQ_528MHz (528UL * 1000 * 1000)
#define FREQ_24MHz (24UL * 1000 * 1000)
#define FREQ_480MHz (480UL * 1000 * 1000)
#ifdef __cplusplus
extern "C" {
#endif
void jump_to_application();
__STATIC_INLINE void NVIC_ClearEnabledIRQs(void);
__STATIC_INLINE void NVIC_ClearAllPendingIRQs(void);
void configure_clocks();
#ifdef __cplusplus
}
#endif
// default constructor
Application::Application()
{
} //application
/* Steps to initialize the Application */
void Application::initialize()
{
checkStartupConditions();
}
/*Start the Application */
void Application::run()
{
while (1)
{
}
}
void Application::checkStartupConditions()
{
jump_to_application();
}
// default destructor
Application::~Application()
{
} //~application
void jump_to_application()
{
PRINTF("Jump1\r\n");
// Clear any IRQs that may be enabled, we only want the IRQs we enable to be active
NVIC_ClearEnabledIRQs();
// Clear any pending IRQs that may have been set
NVIC_ClearAllPendingIRQs();
// Set the VTOR to default.
SCB->VTOR = 0; //Address of the default vector table, which is always 0.
// Restore clock to default before leaving bootloader.
configure_clocks();
// De-initialize hardware such as disabling port clock gate
//deinit_hardware();
CLOCK_DisableClock(kCLOCK_UsbOh3);
// Restore global interrupt.
__enable_irq();
// Memory barriers for good measure.
__ISB();
__DSB();
uint32_t applicationAddress, stackPointer;
applicationAddress = APP_VECTOR_TABLE[1]; //Reset handler
stackPointer = APP_VECTOR_TABLE[0]; //Stack pointer
// Create the function call to the user application.
// Static variables are needed since changed the stack pointer out from under the compiler
// we need to ensure the values we are using are not stored on the previous stack
static uint32_t s_stackPointer = 0;
s_stackPointer = stackPointer;
static void (*farewellBootloader)(void) = 0;
farewellBootloader = (void (*)(void))applicationAddress;
// Set the VTOR to the application vector table address.
SCB->VTOR = (uint32_t)APP_VECTOR_TABLE;
// Set stack pointers to the application stack pointer.
__set_MSP(s_stackPointer);
__set_PSP(s_stackPointer);
// Jump to the application.
farewellBootloader();
// Dummy fcuntion call, should never go to this fcuntion call
jump_to_application();
}
#ifdef __cplusplus
extern "C" {
#endif
/** \brief Clear Enabled IRQs
The function clears all device IRQs
*/
__STATIC_INLINE void NVIC_ClearEnabledIRQs(void)
{
NVIC->ICER[0] = 0xFFFFFFFF;
NVIC->ICER[1] = 0xFFFFFFFF;
NVIC->ICER[2] = 0xFFFFFFFF;
NVIC->ICER[3] = 0xFFFFFFFF;
NVIC->ICER[4] = 0xFFFFFFFF;
NVIC->ICER[5] = 0xFFFFFFFF;
NVIC->ICER[6] = 0xFFFFFFFF;
NVIC->ICER[7] = 0xFFFFFFFF;
}
/** \brief Clear All Pending Interrupts
The function clears the pending bits of all external interrupts.
*/
__STATIC_INLINE void NVIC_ClearAllPendingIRQs(void)
{
NVIC->ICPR[0] = 0xFFFFFFFF;
NVIC->ICPR[1] = 0xFFFFFFFF;
NVIC->ICPR[2] = 0xFFFFFFFF;
NVIC->ICPR[3] = 0xFFFFFFFF;
NVIC->ICPR[4] = 0xFFFFFFFF;
NVIC->ICPR[5] = 0xFFFFFFFF;
NVIC->ICPR[6] = 0xFFFFFFFF;
NVIC->ICPR[7] = 0xFFFFFFFF;
}
void configure_clocks()
{
uint32_t clock_divider = 1;
uint32_t fuse_div = 0;
uint32_t clock_freq = 0;
CLOCK_SetXtal0Freq(CPU_XTAL_CLK_HZ);
// Get the Boot Up CPU Clock Divider
// 00b = divide by 1
// 01b = divide by 2
// 10b = divide by 4
// 11b = divide by 8
fuse_div = ROM_OCOTP_LPB_BOOT_VALUE();
clock_divider = 1 << fuse_div;
// Get the Boot up frequency
// 0 = 396Mhz
// 1 = 528Mhz
clock_freq = ROM_OCOTP_BOOT_FREQ_VALUE();
// Bypass clock configurations if clock is configured
CCM_ANALOG->PLL_SYS |= CCM_ANALOG_PLL_SYS_BYPASS_MASK;
CCM_ANALOG->PLL_USB1 |= CCM_ANALOG_PLL_USB1_BYPASS_MASK;
/* Configure PLL_SYS */
CCM_ANALOG->PLL_SYS &= ~CCM_ANALOG_PLL_SYS_POWERDOWN_MASK;
// Wait Until clock is locked
while ((CCM_ANALOG->PLL_SYS & CCM_ANALOG_PLL_SYS_LOCK_MASK) == 0)
{
}
CCM_ANALOG->PLL_SYS |= CCM_ANALOG_PLL_SYS_BYPASS_MASK;
// Configure SYS_PLL PFD
// PFD0 = 396MHz - uSDHC CLOCK Source
// PFD1 = 396MHz
// PFD2 = 500MHz - SEMC CLOCK Source
// PFD3 = 396MHz
CCM_ANALOG->PFD_528 =
(CCM_ANALOG->PFD_528 & (~(CCM_ANALOG_PFD_528_PFD0_FRAC_MASK | CCM_ANALOG_PFD_528_PFD1_FRAC_MASK |
CCM_ANALOG_PFD_528_PFD2_FRAC_MASK | CCM_ANALOG_PFD_528_PFD3_FRAC_MASK))) |
CCM_ANALOG_PFD_528_PFD0_FRAC(24) | CCM_ANALOG_PFD_528_PFD1_FRAC(24) | CCM_ANALOG_PFD_528_PFD2_FRAC(19) |
CCM_ANALOG_PFD_528_PFD3_FRAC(24);
// Always configure USB1_PLL
CCM_ANALOG->PLL_USB1 =
CCM_ANALOG_PLL_USB1_DIV_SELECT(0) | CCM_ANALOG_PLL_USB1_POWER(1) | CCM_ANALOG_PLL_USB1_ENABLE(1);
while ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_LOCK_MASK) == 0)
{
}
CCM_ANALOG->PLL_USB1 &= ~CCM_ANALOG_PLL_USB1_BYPASS_MASK;
// Configure USB_PLL PFD
// PFD0 = 247MHz - FLEXSPI CLOCK Source
// PFD1 = 247MHz - LPSPI CLOCK Source
// PFD2 = 332MHz
// PFD3 = 576MHz
CCM_ANALOG->PFD_480 =
(CCM_ANALOG->PFD_480 & (~(CCM_ANALOG_PFD_480_PFD0_FRAC_MASK | CCM_ANALOG_PFD_480_PFD1_FRAC_MASK |
CCM_ANALOG_PFD_480_PFD2_FRAC_MASK | CCM_ANALOG_PFD_480_PFD3_FRAC_MASK))) |
CCM_ANALOG_PFD_480_PFD0_FRAC(35) | CCM_ANALOG_PFD_480_PFD1_FRAC(35) | CCM_ANALOG_PFD_480_PFD2_FRAC(26) |
CCM_ANALOG_PFD_480_PFD3_FRAC(15);
// Set up CPU_PODF
if (clock_freq == 1)
{
clock_divider *= 2;
if (clock_divider >
{
clock_divider = 8;
}
}
// Calculate the Final Core Clock, it will be used to calculate the AHB / ARM Core divider later.
uint32_t core_clock = ((clock_freq == 0) ? FREQ_396MHz : FREQ_528MHz) / clock_divider;
// Calculate the AHB clock divider
uint32_t ahb_divider = 1;
while ((core_clock / ahb_divider) > 144000000UL)
{
++ahb_divider;
}
// Set up AXI_PODF - SEMC clock root
// Set up AHB_PODF - CORE clock
// Set up IPG_PODF - BUS clock
CCM->CBCDR = (CCM->CBCDR & (~(CCM_CBCDR_SEMC_PODF_MASK | CCM_CBCDR_AHB_PODF_MASK | CCM_CBCDR_IPG_PODF_MASK))) |
CCM_CBCDR_SEMC_PODF(ahb_divider - 1) | CCM_CBCDR_AHB_PODF(clock_divider - 1) |
CCM_CBCDR_IPG_PODF(ahb_divider - 1);
// LPUART clock configuration, peripheral clock 20MHz
CCM->CSCDR1 =
(CCM->CSCDR1 & (~(CCM_CSCDR1_UART_CLK_SEL_MASK | CCM_CSCDR1_UART_CLK_PODF_MASK))) | CCM_CSCDR1_UART_CLK_PODF(3);
if (clock_freq == 1) // PRE_PERIPH Clock source is SYS_PLL
{
// Pre-peripheral clock configuration
CCM->CBCMR = (CCM->CBCMR & (~CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK)) | CCM_CBCMR_PRE_PERIPH_CLK_SEL(0);
}
else // PRE_PERIPH Clock source is PFD_528_PFD3
{
// Pre-peripheral clock configuration
CCM->CBCMR = (CCM->CBCMR & (~CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK)) | CCM_CBCMR_PRE_PERIPH_CLK_SEL(2);
}
// LPSPI clock configuration, Peripheral clock: 20MHz
CCM->CBCMR = (CCM->CBCMR & (~(CCM_CBCMR_LPSPI_CLK_SEL_MASK | CCM_CBCMR_LPSPI_PODF_MASK))) |
CCM_CBCMR_LPSPI_CLK_SEL(0) | CCM_CBCMR_LPSPI_PODF(3);
// FLEXSPI clock configuration, safe frequency: 30MHz
CCM->CSCMR1 = ((CCM->CSCMR1 & ~(CCM_CSCMR1_FLEXSPI_CLK_SEL_MASK | CCM_CSCMR1_FLEXSPI_PODF_MASK |
CCM_CSCMR1_PERCLK_PODF_MASK | CCM_CSCMR1_PERCLK_CLK_SEL_MASK)) |
CCM_CSCMR1_FLEXSPI_CLK_SEL(3) | CCM_CSCMR1_FLEXSPI_PODF(7) | CCM_CSCMR1_PERCLK_PODF(1));
// NOTE: SEMC clock configuration needs handshake, so it will be handled by SEMC driver itself
// uSDHC1&2 clock configuration
// SEL: PULLL2 PFD0; DIV: 1 (PFD/2, freq=200MHz)
CCM->CSCMR1 = (CCM->CSCMR1 & (~(CCM_CSCMR1_USDHC1_CLK_SEL_MASK | CCM_CSCMR1_USDHC2_CLK_SEL_MASK))) |
CCM_CSCMR1_USDHC1_CLK_SEL(1) | CCM_CSCMR1_USDHC2_CLK_SEL(1);
CCM->CSCDR1 = (CCM->CSCDR1 & (~(CCM_CSCDR1_USDHC1_PODF_MASK | CCM_CSCDR1_USDHC2_PODF_MASK))) |
CCM_CSCDR1_USDHC1_PODF(1) | CCM_CSCDR1_USDHC2_PODF(1);
// Finally, Enable PLL_ARM, PLL_SYS and PLL_USB1
CCM_ANALOG->PLL_SYS &= ~CCM_ANALOG_PLL_SYS_BYPASS_MASK;
CCM_ANALOG->PLL_USB1 &= ~CCM_ANALOG_PLL_USB1_BYPASS_MASK;
// Calculate the Final System Core Clock, it will be used to calculate the AHB / ARM Core divider later.
SystemCoreClock = ((clock_freq == 0) ? FREQ_396MHz : FREQ_528MHz) / clock_divider;
}
#ifdef __cplusplus
}
#endif
I would be very grateful if you could help me!
Best regards,
Maximilian