Author Topic: Reliable reset of (iMX RT) FlexRAM during SW reset  (Read 2324 times)

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3240
    • View Profile
    • uTasker
Reliable reset of (iMX RT) FlexRAM during SW reset
« on: September 06, 2024, 11:50:57 AM »
Hi All

As you are probably aware, the uTasker project typically uses FlexRAM for optimal speed efficiency and divides the FlexRAM into ITC (Instruction Tightly Coupled) for code location and DTC (Data Tightly Coupled) for variables. The FlexRAM partitioning is performed automatically in this mode when the boot loader starts the serial loader or application (when running in RAM).

During intensive testing of SW and watchdog resets it was identified that returning the FlexRAM back to its default configuration was not always guaranteed since this has to be done during the reset command and is not performed automatically in HW during reset. This happened maybe once every 1000 resets or so and is almost certainly due to the fact that the chip's ROM loader - which starts the boot up sequence - fails when OCRAM (general purpose configuration) is disabled during the partitioning (tests where an OCRAM bank was left as it was by default didn't suffer from the issue) and couldn't be returned reliably during the reset operation.

A solution to this, which was tested positively in a long term test of continuous SW and/or watchdog resets, has been added that is recommended to ensure that a board can't remain in an undefined state after a commanded or watchdog reset, noting that it is generally recommended to use an external power-cycling circuit to also capture any potential hanging situation - see https://www.utasker.com/docs/iMX/uTasker_iMX_WDOG.pdf

1. In the bare-minimum (primary) boot loader a routine has been added that can reliably return the FlexRAM settings to their defaults and command reset (the bare-minimum loader operates directly from QSPI flash and can, thus, do this easily.

Code: [Select]
static void fnRestoreFlexRAM(int iReset)
{
    IOMUXC_GPR_GPR16 = IOMUXC_GPR_GPR16_DEFAULT;                         // set the FlexRAM layout back to that taken from the eFuse setting (default configuration)
    if (iReset != 0) {
        APPLICATION_INT_RESET_CTR_REG = (VECTKEY | SYSRESETREQ);         // request cortex core reset, which will cause the software reset bit to be set in the mode controller for recognition after restart
    }
}

2. The location of this routine is added to its Flash configuration, which means that it is - for i.MX RT 10xx parts - at a fixed address of 0x200 offset from the start of QSPI flash:
Code: [Select]
const FLEXSPI_NOR_BOOT_CONFIGURATION __attribute__((section(".boot_hdr.conf"))) __boot_config
= {
    ....
    (unsigned long)fnRestoreFlexRAM                                      // insert the address of a routine that can be called to return the FlexRAM back to default settings and reliably reset the device
};

3. The general SW reset function now uses this call (when it exists) to ensure reliable resets:

Code: [Select]
// This routine is called to reset the processor
//
extern void fnResetBoard(void)
{
#if !defined iMX_BOOTLOADER
    uDisable_Interrupt();
#endif
#if (!defined iMX_BOOTLOADER && !defined NO_STACK_RESET)
    fnStackReset(1);                                                     // {7} if the boot loader supports this method the code will not return
#endif
    APPLICATION_INT_RESET_CTR_REG = (VECTKEY | SYSRESETREQ);             // request cortex core reset, which will cause the software reset bit to be set in the mode controller for recognition after restart
    IOMUXC_GPR_GPR16 = IOMUXC_GPR_GPR16_DEFAULT;                         // set the FlexRAM layout back to that taken from the eFuse setting (default configuration) - although the reset is commanded before executing this line it still operates and avoids the RAM layout being changed when the code is still running
}

and the watchdog interrupt also uses it:

Code: [Select]
// If the watchdog fires this interrupt is called 255 clock cycles before the watchdog reset actually takes place
//
static __interrupt void wdog3_irq(void)
{
    __disable_interrupt();
    #if !defined NO_STACK_RESET
    fnStackReset(0);                                                     // {7} if the boot loader supports this method the code will not return
    #endif
    IOMUXC_GPR_GPR16 = IOMUXC_GPR_GPR16_DEFAULT;                         // set the FlexRAM layout back to that taken from the eFuse setting (default configuration)
    FOREVER_LOOP() {}
}

in both cases as long as the user expressly disables it with
#define NO_STACK_RESET

4. As can be seen from the fnStackReset() code it first checks that the bare-minimum loader does have valid code and so it will use the original technique in case it doesn't.

Code: [Select]
// This routine calls a routine in QSPI flash that reconfigures the FlexRAM back to its default and then conditionally commands a core reset
//
extern void fnStackReset(int iReset)
{
        #if defined SECURITY_OTFAD
    void (*_fnCode)(int) = (void (*)(int))(*(unsigned long *)(FLEXSPI_FLASH_BASE + 0x600)); // fixed location in QSPI flash of code to be executed
        #else
    void (*_fnCode)(int) = (void (*)(int))(*(unsigned long *)(FLEXSPI_FLASH_BASE + 0x200)); // fixed location in QSPI flash of code to be executed
        #endif
    if (((unsigned long)_fnCode < FLEXSPI_FLASH_BASE) || ((unsigned long)_fnCode > (FLEXSPI_FLASH_BASE + 0x8000))) {
        return;                                                          // no valid code exists
    }
    _fnCode(iReset);                                                     // execute (doesn't return but either reset or wdog reset will restart the processor after the flexRAM has been set to default)
}

It is recommended that the bare-minimum loader uses this code (included in recent check-ins) and serial loader and applications the new SW reset and watchdog interrupt code to ensure reset reliability when using code from RAM operation.

Regards

Mark