3

I have written a bootloader for a Kinetis K24 Cortex M4. The bootloader loads additional functionality over USB into ram at runtime. This ramcode exists as its own EWARM project generating a binary. The entry point to this binary must always be 0x20000000 and the vector table must always live at 0x20007000 in order to play nicely with my .NET tool. The IAR startup code handles clearing of the .bss and the .data copy but it also does some other things I dont want. I can't figure out how to force the IAR entry point to a specific address so I have created my own entry point like so

#pragma section=".bss"

#pragma location=".init"
__interwork int __low_level_init(void)
{
    char * from = __section_begin(".bss");
    char * to = __section_end(".bss");

    __DI(); // Disable interrupts

    memset(from, 0x00 , (to - from));

    memcpy(__vector_table, (unsigned char *)ROM_VECTOR_LOCATION, VECTOR_TABLE_SIZE);

    SCB_VTOR = (unsigned int) & __vector_table;

    main();

    SCB_VTOR = (uint32_t)ROM_VECTOR_LOCATION;
}

When I debug the code I can see that my global variables initialized to non-zero values take on random values. I believe this is because I am not copying the .data section from the LMA to the VMA.

My question is how do I duplicate this copy of the .data section from LMA to VMA?

I would also settle for using the IAR startup code if I could figure out how to break it up but the entry point can't be the reset vector. The entry point has to be 0x20000000 and the vector table has to live at 0x20007000

lusher00
  • 678
  • 1
  • 7
  • 18
  • You already do one `memcpy` of some data, why not add another? All the information you need should be available to you, yes? The start and end of the `.data` segment in FLASH, as well as some location for it in RAM? – Some programmer dude Jan 24 '18 at 15:22
  • [Some useful tips & tricks for how to roll out the "CRT" yourself on a generic MCU](https://stackoverflow.com/a/47940277/584518). Most notably your code seems to fail to setup the clock _before_ you init .data and .bss. That would be very bad. Though of course not all from that link applies to Cortex M4, ARM sets the SP through hardware etc. – Lundin Jan 24 '18 at 15:42
  • Yes, I assume this will be a simple copy but I do not know where this information is located. It's not in my linker script and nothing in the map file stands out. – lusher00 Jan 24 '18 at 15:42
  • If you want the variables to be initialized proberly calling main from __low_level_init is not a very good idea. __low_level_init is called before the initialization code. – Johan Jan 24 '18 at 15:44
  • Anyway, I don't know what IAR names the various segments, but obviously you need a `memcpy` from wherever `.data` initializers are stored in flash, to the RAM block named `.data`. Check your linker file/map file/symbol browser to find out the name used. I don't believe LMA/VMA is an issue. – Lundin Jan 24 '18 at 15:48
  • __low_level_init is my entry point by design. It is called after the bootloader has initialized everything. Watchdog is enabled, SP is setup, clocks configured, etc. The only thing left to do is clear the .bss and setup the .data section. – lusher00 Jan 24 '18 at 15:50
  • If I remember correctly, IAR calls the linker script for `*.lcf`. Something in the flash section of that file will be the initializers for `.data`. – Lundin Jan 24 '18 at 15:52
  • @lusher00 I realize that this is by design, I am just pointing out at least one reason why it is not a very good design. – Johan Jan 24 '18 at 15:54
  • @Johan Why not? Looks fine to me, except we can't tell what the magic `__DI();` function does. – Lundin Jan 24 '18 at 15:55
  • @johan what init code am I missing that can't be handled by the bootloader? The bootloader used the standard IAR init sequence. The only thing left to do is copy the data. I'm asking how to strip that portion out of the standard init sequence or write it my self. Either way, everything will be handled. – lusher00 Jan 24 '18 at 15:58
  • @lundin sorry, that disabled interrupts – lusher00 Jan 24 '18 at 15:58
  • @lusher00 You can of course handle the data initialization in the bootloader but the only thing that happens between __low_level_init and the call to main is that the data initialization is called. You should also note that when __low_level_init returns the program continues by calling data initialization and after that main. – Johan Jan 24 '18 at 16:05
  • @johan no it doesn't, it returns to the bootloader. I already have it working with a basic blinking light but the data isn't right with larger programs. In order for the IAR startup sequence to occur you have to start from the reset vector. That function should be renamed startup() but when I do that it doesn't get forced to 0x20000000. I have a question into IAR as to how to force it to 0x20000000 without calling it __low_level_init but I can assure only the code I have written is all that is executed because I have single step through it. – lusher00 Jan 24 '18 at 16:10
  • I believe it doesn't work when I call it startup() is because that function is never called (the bootloader jumps to address 0x20000000 where startup() is located) and its getting thrown out during optimization (even though I have optimization turned off) – lusher00 Jan 24 '18 at 16:19
  • @lusher00 You could try to tell the linker not to throw away the startup function even though it is not called. I don't remember how to do this but it should be described in the manual. There should also be a way to trigger variable initialization manually but for this I also have to refer you to the manual. – Johan Jan 24 '18 at 16:23
  • @johan, thank you. I'm going to focus on renaming this function for now to avoid any confusion. It's not really the __low_level_init() that gets called during a startup sequence. – lusher00 Jan 24 '18 at 16:30
  • So you are attempting to use C to bootstrap C am I understanding this correctly? – old_timer Jan 24 '18 at 21:08
  • You seem to be really over complicating this, if you are making a ram based program with data that is about as easy as it gets, a few lines of asm in flash copies the whole damn binary over in one pass, .text, .data, .bss, minimal/trivial linker script for the ram based code. – old_timer Jan 24 '18 at 21:11
  • You can also develop the project so that .bss doesnt need to be zeroed and there isnt any .data that needs to be initialized. – old_timer Jan 24 '18 at 21:53
  • @old_timer its not copied from flash, its loaded via USB at runtime. – lusher00 Jan 24 '18 at 22:16
  • no real difference there then, build right and the toolchain sets up .bss and .data for you, or just dont use .bss and .data – old_timer Jan 25 '18 at 00:24
  • @old_timer I would love to see the solution you propose posted as an answer below. – lusher00 Jan 25 '18 at 14:41
  • for clarification your issue is that you have a bootloader and that is not the problem the bootloader works? it allows you to download a program to SRAM and run the program with its data in that SRAM? And your issue is how to prep .bss and .data for that program in sram? – old_timer Jan 25 '18 at 15:34
  • @old_timer the bootloader works. the ramcode also works with the solution I posted below. It sounds like you are suggesting I tell the linker to add the .data and .bss section to the binary but that causes unnecessary bloat. I need the image to be very small. The standard IAR startup code handles the copy so what you are suggesting is non-standard. If you included those sections in the binary you would have to rewrite the startup code to remove the copy. – lusher00 Jan 25 '18 at 15:44

1 Answers1

1

The IAR function which handles the copy of the .data section is called __iar_data_init3(). I had considered calling this directly but couldn't believe it was that simple. IAR suggested this is the correct solution. I also used the keyword __root to prevent the compiler from removing my "unused" function. This allowed me to rename it something more appropriate like startup(). Calling it __low_level_init() was just a hack to prevent the compiler from removing it. __low_level_init() was not called as part of the startup sequence but rather the entry point I was loading the program counter with in the bootloader. This is my final solution

#pragma section=".bss"
#pragma location=".init"
__root void startup()
{
    char * from = __section_begin(".bss");
    char * to = __section_end(".bss");

    memset(from, 0x00 , (to - from));

    __iar_data_init3();

    memcpy(__vector_table, (unsigned char *)ROM_VECTOR_LOCATION, VECTOR_TABLE_SIZE);

    __DI(); // Disable interrupts

    SCB_VTOR = (unsigned int) & __vector_table;

    main();

    SCB_VTOR = (uint32_t)ROM_VECTOR_LOCATION;

}

Not there is also a function called __iar_zero_init3() which handles the zeroing of the .bss however on first try it caused my program to crash. I can't imagine it would take much work to get it working.

lusher00
  • 678
  • 1
  • 7
  • 18
  • The vector table includes the initial stack-pointer value. It seems in this solution you are using the bootloader's stack in `main()` which could conceivably overlap with the ram-resident code. – Clifford Jan 25 '18 at 10:28
  • @Clifford I am sharing the stack. I'm not sure how I feel about that but I'm going to let it go for now. It wouldn't be that much work to update the stack poiner before calling main and restoring it after. – lusher00 Jan 25 '18 at 14:44
  • The standard startup will also initialise the standard library; as such some library call may fail if they rely on static data. Heap management for example. – Clifford Jan 25 '18 at 14:50
  • @clifford the standard startup has been removed. – lusher00 Jan 25 '18 at 15:41
  • I am aware of that; my point is merely a caution that parts of the standard library may not then work. – Clifford Jan 25 '18 at 15:45
  • Are you sure that `__iar_data_init3` doesn't clear .bss as well? – D Krueger Jan 25 '18 at 23:52
  • @D Kruger. Yes __iar_data_init3 handles it – lusher00 Jan 27 '18 at 03:07