ryankurte / efm32-base

Base project for Silicon Labs EFM32 microcontrollers
89 stars 33 forks source link

The EFR32MG21 (series2 device) does not boot when I flash it. #28

Closed ewheelerinc closed 3 years ago

ewheelerinc commented 3 years ago

Hello Ryan,

I'm having trouble getting simple LED blink tests to work in the EFR32MG21. I have your test main.c blink working using portB, but only sometimes because I think the bootloader is jumping to somewhere in memory that happens to work if I get lucky, but it isn't consistent when I change a line of code so it hardfaults. Please correct me if I'm wrong on my assumptions below, this is based on what I've scraped together from PDFs online:

The series2 devices jump to flash at 0x00000000 to start the application and it looks like efm32-base starts with the vector table at 0x4000.

From here: https://www.yic-electronics.nz/datasheet/a0/EFR32MG12P132F512GM68-CR.pdf?page=6

On newer devices (EFR32xG12 and later), the bootloader resides in the bootloader area in the Information Block • Application @ 0x0 • First stage bootloader @ 0x0FE10000 • Main bootloader @ 0x0FE10800

Note that we are using the G21 not the G12, but I'm guessing the bootloader process is the same---or at least I've not yet found anything to the contrary.

Currently my _mainCRTStartup is at 0x4174, though t may change with global values and such. The vector table points the reset at 0x4ddd, shortly after which it jumps to 4174.

How can I write a preamble that starts at 0x0 to jump to 0x4174---or better yet, have the preamble read 0x4004 which is the reset PC location and jump there, which is 4ddd in this example?

Thank you for your help!

-Eric

ewheelerinc commented 3 years ago

Hmm, I just found this:

On EFR32MG1 and EFR32xG21 (Series 2) devices, the application is offset by 16 kB to accommodate the bootloader at thebottom of main flash. On other Series 1 EFR32 devices, the Gecko Bootloader resides in the Bootloader flash region outside of mainflash block.

[ https://www.silabs.com/documents/public/application-notes/an1084-gecko-bootloader-emberznet-silicon-labs-thread.pdf?page=13 ]

Do you know if this means that 0x4000 is the correct location for the vector table and thus it should jump to 4ddd as noted in the previous post?

I've not found any documentation on where the bootloader jumps, so any insight you might have would be greatly appreciated!

ryankurte commented 3 years ago

oh interesting, they ship a bootloader now? the compiled application starts with a program counter then a vector table offset, as you've inferred from the PC location at 0x4004 which the startup code uses to set everything up, this is how basically all cortex-m apps work. We default to linking at 0x0000_0000, but you can override this.

fingers crossed you can just add a set(FLASH_ORIGIN 0x4000) prior to including the toolchain files in your CMakeLists.txt to link the application at the correct flash location for the bootloader to jump to and everything should work... i can't remember if I've used this feature in this project before so, let me know how it goes?

ewheelerinc commented 3 years ago

We have played with FLASH_ORIGIN=0x4000, but it hasn't worked reliably. Maybe the vector table does need to start at 0000? Some documentation seems to indicate that the bootloader starts at 0000, in which case we don't need a bootloader to just run our little app as long as the "bootloader" is just a vector table with code. Either way, I've confirmed that we can write to 0x0000 and 0x4000 so nothing is write-locked.

When we build it shows up in the dump as 4000 no matter how we set FLASH_ORIGIN. Is this expected:

cd build cmake -DFLASH_ORIGIN=0x0000 .. && make clean && make -j24 from the .dmp: 00004000 <__Vectors>: 4000: 20000400 .word 0x20000400 4004: 00004ddd .word 0x00004ddd

cmake -DFLASH_ORIGIN=0x4000 .. && make clean && make -j24 from the .dmp: 00004000 <__Vectors>: 4000: 20000400 .word 0x20000400 4004: 00004ddd .word 0x00004ddd

Should the PC with FLASH_ORIGIN=0x0000 be 0ddd instead of 4ddd?

I think we want the vectors at 0000 but the code to be based at 4000 to jump to 4ddd as above---but I'm not certain of that.

Sometimes it "miraculously" works if I first flash at 0x0000 and then flash at 0x4000 as if one of those hits the right spot for the vectors. But then if I add a function or line of code it shifts the PC vector to a different value and subsequent flashes boot with a fault and sets the PC to 0xEFFFFFFE.

I'll need to do some more testing to better understand the 0x0000 vs 0x4000 flash behavior, but if this gives you any insight into what might be going on then let me know what we can try next!

-Eric

ewheelerinc commented 3 years ago

So I think this is the issue:

With a default FLASH_ORIGIN of 0x0000_0000 it flashes the following vector table at 0x0000_0000 in flash:

00004000 <__Vectors>:
    4000:       20000400        .word   0x20000400
    4004:       00004ddd        .word   0x00004ddd

Here's a memory dump at 0000:

J-Link>mem 0 10
00000000 = 00 04 00 20 **DD 4D 00 00**  09 4E 00 00 09 4E 00 00  ... .M...N...N..

You can see that 4ddd is in the resetPC at 0x0004 in flash but nothing is written to 4ddd after an erase so it doesn't boot:

J-Link>mem 4ddd 10
00004DDD = FF FF FF FF FF FF FF FF  FF FF FF FF FF FF FF FF  ................

If I reflash with FLASH_ORIGIN=0x4000 it works because the program is properly located at 0x4000 and the vector table is still at 0x0000 from earlier: cmake -DFLASH_ORIGIN=0x4000 .. && make clean && make -j24 f

And now you can see the populated reset handler, it boots, and the light blinks:

J-Link>mem 4ddd 10
00004DDD = 48 80 47 06 49 07 4A 07  4B 9A 42 BE BF 51 F8 04  H.G.I.J.K.B..Q..

Now if I make e and start over with an empty flash, then flash with FLASH_ORIGIN=0x4000, it doesn't work because the vector table needs to be at 0x0000 but the code needs to be at 0x4000, at least in this example.

Of course if I change a line of code in main.c and reflash then I have to first reflash at 0x0000 to reset the vectors and then flash 0x4000 to make the code run because resetPC (probably) differs between builds.

Do you already have a define in your code somewhere to specify a different location for the vector table vs. the code offset?

Alternatively, if the entire code started at 0x0000 instead of 0x4000 then that would work too (this resetPC would be 0ddd in this example)---but having separate vector table and code offset defines might make efm32-base more flexible in the future, especially when bootloaders get involved.

-Eric

ewheelerinc commented 3 years ago

So I've confirmed that this quick hack fixes it, but it will reset when I re-run cmake.

I can now modify my code ad-hoc and make f and it works correctly. Note that I duplicated the loadbin line for 0x0000 to get the vector table loaded. Probably more than I need, but its a proof of concept:

build]$ cat flash.jlink 
device 
h
loadbin space-ham.bin, 0x0000
loadbin space-ham.bin, 0x4000
verifybin space-ham.bin, 0x4000
r
g
qc

So, what is the "proper" fix?

ryankurte commented 3 years ago

hmm, interesting. I'm struggling to find any useful information on the bootloader operation? the user guide doesn't have a lot of detail.

it appears that FLASH_OFFSET was renamed to FLASH_ORIGIN and i had both forgotten about the renaming and forgotten to add this to the example CMakeLists.txt file in the root of the project to make it clear. With this set the .dmp file becomes:

efm32-test:     file format elf32-littlearm

Disassembly of section .text:
00008000 <__Vectors>:
    8000:   20004000    .word   0x20004000
    8004:   0000a675    .word   0x0000a675
    8008:   0000a6a1    .word   0x0000a6a1
    800c:   0000a6a1    .word   0x0000a6a1
    8010:   0000a6a1    .word   0x0000a6a1

which is as expected, hopefully this works for you?

Either way, I've confirmed that we can write to 0x0000 and 0x4000 so nothing is write-locked.

if you've written to 0x0000 you may have clobbered the first-stage bootloader? If you are loading applications later in flash the bootloader needs to set the vector table offset / PC / SP correctly, the bootloader will be doing something like

  /* Set new vector table pointer */
  SCB->VTOR = (uint32_t)APP_START;

  /* Read new SP and PC from vector table */
  sp = *((uint32_t *)APP_START);
  pc = *((uint32_t *)APP_START + 1);
  /* Jump to new application */
  ...

you may need to reprogram the first stage bootloader (or shim) per the user guide above to get things back to a reasonably working state (with applications located at the correct flash offset).

ewheelerinc commented 3 years ago

For now lets assume I'm not going to use a bootloader because it works fine if I flash the vector to 0x0000 and then put my code at 0x4000 with a 2nd flash.

Is there a way to make some options in your efm32-base code to put the vector at 0x0000 and keep the program at 0x4000?

Or, just move everything to an absolute base of 0x0000 would work too.

ewheelerinc commented 3 years ago

Another way of asking my question is this:

Do you have a cmake option to change the base address? Above you pasted in 0x8000 but mine is 0x4000. How do I set it to 0x0000?

Then this:

00004000 <__Vectors>:
    4000:       20000400        .word   0x20000400
    4004:       000051f1        .word   0x000051f1

It would then recompile and look like this (I hand subtracted 0x4000 from above for example sake):

00000000 <__Vectors>:
    0000:       20000400        .word   0x20000400
    0004:       000011f1        .word   0x000011f1
ryankurte commented 3 years ago

Do you have a cmake option to change the base address? Above you pasted in 0x8000 but mine is 0x4000. How do I set it to 0x0000?

yes, the option is FLASH_OFFSET as referred to above. you do need to completely remove your build directory before changing this.

it looks like when you build for the one y'all are using it's picking up a linker file for BLE support which locates the application at 0x4000, you can see this in the cmake output:

[smolfriend] ➜  build git:(master) ✗ cmake ..
Device: EFR32MG21A010F1024IM32
Processor: EFR32MG21A010F1024IM32
Family: EFR32MG21
Architecture: cortex-m33
Adding device library for: EFR32MG21
Found bluetooth specific linker script, using this instead:
 before: /home/ryan/projects/efm32-base/device/EFR32MG21/Source/GCC/efr32mg21.ld
 after: /home/ryan/projects/efm32-base/protocol/bluetooth/ble_stack/linker/GCC/efr32mg21a010f1024im32.ld
No board defined, skipping hardware
-- Configuring done
-- Generating done
-- Build files have been written to: /home/ryan/projects/efm32-base/build

if you remove the protocols include, or pull the latest version which adds a USE_PROTOCOLS flag, this works fine:

efm32-test:     file format elf32-littlearm
Disassembly of section .text:
00000000 <__Vectors>:
       0:   20018000    .word   0x20018000
       4:   00002871    .word   0x00002871
       8:   0000289d    .word   0x0000289d
       c:   0000289d    .word   0x0000289d
      10:   0000289d    .word   0x0000289d
      14:   0000289d    .word   0x0000289d
      18:   0000289d    .word   0x0000289d
      1c:   0000289d    .word   0x0000289d
      20:   0000289d    .word   0x0000289d

if you're trying to use BLE you'll need to update the overridden prototocol linker file to inherit the flash configuration as others do.

ewheelerinc commented 3 years ago

Looks good. Thanks for pointing out those linker files, that answered quite a few of my questions and explains why FLASH_ORIGIN wasn't doing what we wanted. Thanks for your help!

We will want to use Zigbee or Thread at some point, but no need for bluetooth on this chip. Any idea why BLE changes the vector to 0x4000 instead of respecting FLASH_ORIGIN? I suppose we can use a shim if needed.

(You said FLASH_OFFSET was renamed to FLASH_ORIGIN and in your previous post you mentioned FLASH_OFFSET. I assume you meant FLASH_ORIGIN, but correct me if I'm wrong.)

I'll test on 0x000 and see if it works without my double-flash hack and let you know.

ewheelerinc commented 3 years ago

It works! I was able to flash without a bootloader at 0x0000 and the program works great. Thank you for your help figuring this out!

ryankurte commented 3 years ago

We will want to use Zigbee or Thread at some point, but no need for bluetooth on this chip. Any idea why BLE changes the vector to 0x4000 instead of respecting FLASH_ORIGIN? I suppose we can use a shim if needed.

there is no technical impediment to any of this. the linker files are hand modified to work with FLASH_ORIGIN it just appears noone has done this yet for the bluetooth one on that device. i don't think we automatically pick up a linker file override in other situations (though it's possible we will need to do this for thread/zigbee), just have to try it and see what's required.

It works! I was able to flash without a bootloader at 0x0000 and the program works great. Thank you for your help figuring this out!

glad to hear it!