DFU /Dual boot in teensy 4.0

Hello,


I’m working on implementing Device Firmware Update (DFU) on the Teensy 4.0. In my primary firmware, I attempt to launch a secondary firmware image located at an offset within the flash memory using the following code:

C:
#if 1
FLASHMEM void disableCache() {
  // Disable Data and Instruction caches, and the MPU
  SCB_MPU_CTRL = 0;  // Turn off MPU
  SYST_CSR = 0;      // Disable SysTick

  // Disable all interrupts
  for (int i = 0; i < NVIC_NUM_INTERRUPTS; i++) {
    NVIC_DISABLE_IRQ(i);
  }

  // Disable D-Cache
  SCB->CSSELR = 0U;
  asm("dsb");
  SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk;
  asm("dsb");

  // Clean & Invalidate D-Cache
  uint32_t ccsidr = SCB->CCSIDR;
  uint32_t sets = CCSIDR_SETS(ccsidr);
  do {
    uint32_t ways = CCSIDR_WAYS(ccsidr);
    do {
      SCB->DCCISW = (sets << SCB_DCCISW_SET_Pos) | (ways << SCB_DCCISW_WAY_Pos);
    } while (--ways);
  } while (--sets);
  asm("dsb");
  asm("isb");

  // Disable I-Cache
  SCB->CCR &= ~(uint32_t)SCB_CCR_IC_Msk;
  SCB->ICIALLU = 0UL;
  asm("dsb");
  asm("isb");
}
#endif



To verify a valid image and jump to it:

C:
const uint32_t FLASH_BASEADDRESS = 0x60000000;

bool checkForValidImage(uint32_t addressInFlash) {
  uint32_t SPIFlashConfigMagicWord = *((uint32_t*)addressInFlash);
  uint32_t VectorTableMagicWord = *((uint32_t*)(addressInFlash + 0x1000));
  if ((SPIFlashConfigMagicWord == 0x42464346) && (VectorTableMagicWord == 0x432000D1))
    return true;

  Serial.println("Invalid magic numbers, no flash image found");
  return false;
}

typedef void (*pFunction)(void);

FLASHMEM bool runApp(uint32_t offsetFromStart) {
  uint32_t imageStartAddress = FLASH_BASEADDRESS + offsetFromStart;
  if (!checkForValidImage(imageStartAddress)) {
    while (true) {
      Serial.printf("No valid image found at 0x%08X\r\n", imageStartAddress);
      delay(1000);
    }
  }

  uint32_t firstInstructionPtr = imageStartAddress + 0x1000 + sizeof(uint32_t);
  Serial.printf("First instruction pointer is at address 0x%08X\r\n", firstInstructionPtr);
  uint32_t firstInstructionAddr = *(uint32_t*)firstInstructionPtr;

#if 1
  if ((firstInstructionAddr < (imageStartAddress + 0x1000)) || (firstInstructionAddr > (imageStartAddress + 0x3000))) {
    while (true) {
      Serial.printf("Address of first instruction %08X is not valid. The image may have been built incorrectly.\r\n", firstInstructionAddr);
    }
  }
#endif

  Serial.printf("Jumping to code at 0x%08X\r\n", firstInstructionAddr);
  delay(10);

  pFunction Target_Code_Address = (pFunction)firstInstructionAddr;
  Target_Code_Address();

  while (true) {
    Serial.println("Execution returned unexpectedly.");
    delay(1000);
  }
}


For the secondary application, I’ve configured the linker script to build it at 0x60100000 (i.e., 1MB into flash). I then attempt to launch it from the primary firmware using:
runApp(0x100000);

Issue Encountered​


When compiling, I get the following error:
src\main.cpp:63:3: error: 'SCB' was not declared in this scope; did you mean 'SCL'?

If I comment out or remove the disableCache() function, the firmware continues to execute from the default flash address instead of jumping to the new address.
I suspect this could be due to either:
  • A missing or incorrect include for CMSIS system registers (SCB), i have tired including #include "core_cm7.h" but it seems teensy does not support that
  • A configuration issue in the linker script or memory alignment.

i have uploaded the custom linker script shortly for your review. Please let me know if you notice any issues or missing elements that may cause this behavior.

This is the thread i have refered for this:
 

Attachments

  • custom_linker.txt
    704 bytes · Views: 13
I'm using the standard arduino build tools and process.
To build with an offset I make two changes to files within the arduino teensy libraries.
In imxrt1062_t41.ld I change the FLASH ORIGIN and LENGTH values by the offset I'm adding.
In bootdata.c I change the code at the start:
Code:
__attribute__ ((section(".bootdata"), used))
const uint32_t BootData[3] = {
    0x60000000,
   (uint32_t)&_flashimagelen,
    0
};
To reflect the new FLASH start address

Other than those two changes the build process is the stock ardunio one, I hit verify in the Arduino IDE and copy the .hex that produces.
I have a batch files to swaps those two files from the stock ones to my modified ones and back.
 
Thanks for the quicky reply

I have modified the aforementioned files and built the image (I am using Teensy in vs code)
MEMORY
{
ITCM (rwx): ORIGIN = 0x00000000, LENGTH = 512K
DTCM (rwx): ORIGIN = 0x20000000, LENGTH = 512K
RAM (rwx): ORIGIN = 0x20200000, LENGTH = 512K
FLASH (rwx): ORIGIN = 0x60100000, LENGTH = 7936K
ERAM (rwx): ORIGIN = 0x70000000, LENGTH = 16384K
}
__attribute__ ((section(".bootdata"), used))
const uint32_t BootData[3] = {
0x60100000,
(uint32_t)&_flashimagelen,
0
};

But my program get stuck on disableCache(); if i tired commenting it out the firmware continues to execute from the default flash address instead of jumping to the new address.

This is small python script i am using for coverting the hex to bin

Python:
from intelhex import IntelHex
import sys
import os

def convert_hex_to_bin(hex_path, bin_path):
    ih = IntelHex()
    ih.fromfile(hex_path, format='hex')
    start = ih.minaddr()
    end = ih.maxaddr()
    size = end - start + 1

    print(f"Converting: {hex_path}")
    print(f"Address Range: 0x{start:X} - 0x{end:X} ({size} bytes)")

    # Write to binary
    ih.tobinfile(bin_path, start=start, size=size)
    print(f"✅ Output written to: {bin_path}")

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("Usage: python convert_hex_to_bin.py firmware.hex firmware.bin")
        sys.exit(1)

    hex_file = sys.argv[1]
    bin_file = sys.argv[2]

    if not os.path.isfile(hex_file):
        print(f"❌ Error: File not found: {hex_file}")
        sys.exit(1)

    convert_hex_to_bin(hex_file, bin_file)
 
If it's dying in the disable cache function within the bootloader application then it can't be the linker side of how you are creating the binary image for your main application, it's not running that application at that point. It must be something to do with the bootloader code or the way that's running.
That should all be built as if it was a completely standard teensy application since it's the thing that needs to run on power up so no special linker requirements there. I'm not sure why the disable cache function is causing a crash/lockup for you, it's worked for me both on a teensy board and a custom pcb using the teensy bootloader.
 
Thank you for your response.
After properly handling the interrupts, the issue resolved now i can be able load the firmware from secondary location.
I have another question: is it possible to load the secondary firmware into a new region in flash without modifying bootdata.c and the linker script?
 
You can load the image anywhere you want in flash. Running if from that location is a different matter.

If the image doesn't start at the address specified in the linker then all the function addresses in flash will be incorrect. As soon as the code calls a function the processor will jump to the wrong location and best case it crashes immediately.
 
Back
Top