Preserving information from bootloader after boot

Technical questions regarding the XTC tools and programming with XMOS.
magnus
Member
Posts: 13
Joined: Tue Aug 06, 2019 8:40 pm

Preserving information from bootloader after boot

Post by magnus »

Hi,

I'm working with the bootloader and I have a problem with conveying any information from the bootloader to the actual program when it has been booted. I'm using the following device: XU232-1024-FB374.

During boot I look for user input (button press) and depending on the length of the press we do different things. (where the final mode is booting to factory image).
All this works great but I have trouble preserving any information from the bootloader.

What I've looked into:

* Storing data in flash. Don't know how to do this as we only seem to have the ability to read from the flash from the bootloader.

* Creating a variable at a certain location in memory and then reading it once booted. I've messed a bit with linker scripts but didn't get anything to work. Also I'm having trouble finding out the memory layout used (any information about this would be welcome).

* Drive a 1-bit port high/low (I only need one bit of information so this would be enough) and then when the application has booted read the state of the port using peek(). I thought this would work but it seems the ports are reset between my bootloader callbacks my application has booted.

Any ideas or pointers on how to do this?


User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am

Post by mon2 »

Hi. Yes it is possible. Review AN00188.


AN00188_-Using-QuadSPI-flash-memory-for-persistent-storage-with-xCORE-200_1.0.2rc1.pdf
You do not have the required permissions to view the files attached to this post.
magnus
Member
Posts: 13
Joined: Tue Aug 06, 2019 8:40 pm

Post by magnus »

Not sure if I missed anything but I didn't see anything about a bootloader in that document.

I already do make use of several upgrade images, factory image and a data partition. But this doesn't help me (possibly unless I can write to the flash directly from the bootloader) to send information (in this case a single bit) from the bootloader to the application.
User avatar
mon2
XCore Legend
Posts: 1913
Joined: Thu Jun 10, 2010 11:43 am

Post by mon2 »

Hi. Study AN00109. It should be close to your requirements. Please post your update.
You do not have the required permissions to view the files attached to this post.
magnus
Member
Posts: 13
Joined: Tue Aug 06, 2019 8:40 pm

Post by magnus »

Thanks, that is the document that I have based a lot of my bootloader from.
But it does not pass any information from the bootloader to the application. Instead it chooses what image to boot depending on user input.

TLDR: Basically all I want to do is pass the value of an integer from the bootloader callbacks to the application I tell the code to boot (it does not need to be preserved over power cuts, only between jump from boot loader to application).

For anyone who cares:
To expand on my use-case. If a button is pressed during boot I monitor how long it is held down:
(the instant the button is released I choose a boot image and boot immediately)
  • a) If the button is hold down for less than X seconds we boot normally, as if no button had ever been pressed.
  • b) If the button is hold down for more than X seconds a LED starts blinking. I want to know whether this has happened once the application boots. If the user releases the button here I boot the same image as in a) but I want to know that b) has occurred.
  • c) If the button is hold down for X+Y more seconds we go directly to the factory image as a sort of emergency recovery.
So, if the button is released at time t where: X < t < (X+Y) I would like to inform the application to do some something.
This all works as expected with the exception that I can not find a way to detect when the case of b) has occurred.

As I note in the original post I would have imagined that I could detect whether a port is driven high/low or tell the linker to reserve an address for me to fetch a status digit. Or write this to flash. This must be done from the bootloader callbacks.

But so far I haven't succeeded in either.

I could of course create two upgrade images with the sole reason to distinguish that state (again, one bit). But that would be extremely wasteful and cumbersome and I don't know if we can spare the memory for it either.
User avatar
akp
XCore Expert
Posts: 579
Joined: Thu Nov 26, 2015 11:47 pm

Post by akp »

I would look at writing one of the last few words of RAM in your bootloader, and then reading that memory element in your application. You can do it with assembly

Code: Select all

unsigned flag = <flag val>;
unsigned addr = <address>; --> you'll have to figure this one out, I am not sure if you can use the last word or not
asm volatile("stw %0, %1[0]" :: "r"(flag),"r"(addr) : "memory");
and in your application you could read it

Code: Select all

asm volatile("ldw %0, %1[0]" : "=r"(flag) : "r"(addr));
I think this should work. But I am not 100% sure. And I might have made a typo.

Or you could do it in C just by doing (unsigned *)addr = flag; of course
magnus
Member
Posts: 13
Joined: Tue Aug 06, 2019 8:40 pm

Post by magnus »

Thanks, yes.
I really wanted to know something about the memory layout before attempting that. Because otherwise even if I could get it to work I would have no way to know if future versions would break it. Which is why I tried to get the linker involved.

Anyway, I've tried it and all addresses I tried to read results in a:
xrun: Program received signal ET_LOAD_STORE, Memory access exception.

I thought I'd write out a bunch of flags in the loader and then iterate through them in my application and see which would stick. But seeing as I can't get passed the exception I don't know how to proceed with that either.

And again, without having a clue about the memory layout this feels reckless.

Update:
Wrote out the address of a stack variable and a heap variable the first thing on a tile0 function.
stack_address: 0x7f50c, heap address: 0x75d90

Now these didn't belong in the address range I guessed. Reading them is legal but any values I set in the loader are not preserved. Also very hacky.
User avatar
akp
XCore Expert
Posts: 579
Joined: Thu Nov 26, 2015 11:47 pm

Post by akp »

I tested this idea on the MC-AUDIO board and it works. I used address 0x7FFFC and wrote it in init() in loader.xc. I set different values depending on which input pin(s) are pulled down at power up. I could read it in main.xc on a tile[0] core.

I chose 0x7FFFC because it is the last word address in tile SRAM for 256kB tiles (e.g. XE216, XE232) (assuming you haven't changed the image-base to something other than the default 0x40000, from your previous posts it appears you haven't).

Here's the code I used to write the value in loader.xc init():

Code: Select all

  unsigned val;
  unsigned addr = 0x7FFFC; // last address in SRAM
  val = <whatever>;
  asm volatile("stw %0, %1[0]" :: "r"(val), "r"(addr));
And here's the code I used to read the value in my application main.xc:

Code: Select all

    on tile[0]:
    {
      unsigned boot_val, addr = 0x7FFFC;
      asm volatile("ldw %0, %1[0]" : "=r" (boot_val) : "r" (addr));
      do_something_with(boot_val);
    }
Note: I just read it in the factory image, not an upgrade image, but it should work the same in both. I would suggest you use some special code for your flag so it's unlikely to be set when you run under jtag. You might want to actually clear the value after you load it in main, or set it to some other special code (so then if you run under jtag this memory location won't have your special boot flag).
User avatar
fabriceo
XCore Addict
Posts: 184
Joined: Mon Jan 08, 2018 4:14 pm

Post by fabriceo »

Hi Guys,
akp is poining in the right direction. This technic is used in the "module_dfu" with the following piece of info in dfu.xc:

Code: Select all

#define FLAG_VALUE 0x11042011
#if (XUD_SERIES_SUPPORT==4)
/* xCORE-200 */
/* Note range 0x7FFC8 - 0x7FFFF guarenteed to be untouched by tools */
#define FLAG_ADDRESS 0x7ffcc
#else
/* Note range 0x1FFC8 - 0x1FFFF guarenteed to be untouched by tools */
#define FLAG_ADDRESS 0x1ffcc
#endif

/* Store Flag to fixed address */
 void SetDFUFlag(unsigned x)
{
    asm volatile("stw %0, %1[0]" :: "r"(x), "r"(FLAG_ADDRESS));
}

/* Load flag from fixed address */
 unsigned GetDFUFlag()
{
    unsigned x;
    asm volatile("ldw %0, %1[0]" : "=r"(x) : "r"(FLAG_ADDRESS));
    return x;
}
the FLAG_VALUE is checked at address FLAG_ADDRESS at boot time and if so the xmos enters in dfu mode