Strings for my boot loader

Go To Last Post
21 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

I've spent some time both "googling", searching this forum, and playing with this problem. I'm looking to place some strings in the boot loader I've written for the ATmega2560. Currently, I use:

void sendVersion()
{
	sendchar( '0' );
	sendchar( '.' );
	sendchar( '4' );
}

Pretty much BF&I (Brute Force and Ignorance). I was hoping to place a string in the flash up in the boot area and then use the pgm_read_*_far functions to spit the strings out (and, along the way, make it easy for the app to query the boot for it's version).

Now I know that the PROGMEM stuff is useless for this. There doesn't seem to be any other way to tell WinAVR gcc to place a string in flash.

I also tried another approach that seemd to have promise. I used a "function" to store the string and then pass the function pointer around:

#define BOOTUTIL_SECTION   __attribute__ ((section (".bootutil")))
void BootOK( void ) BOOTUTIL_SECTION __attribute((naked))__;

void BootVersion( void )
{
    asm volatile ( ".asciz \"0.4\"     \n\t"
                   ".balign 2          \n\t"
                 );
    return;
}

but when I tried to use the function as a pointer:

sendstring_P( (uint32_t) BootVersion );

I got bogus values loaded for the argument.

So, any clues? I seem to be stuck with multiple problems with any approach I take. I'm currently stuck with the original approach.

For the version, I guess I could set up a main->boot function call to return the version as has been talked about recently. Still, I was hoping for something a little more elegant than 4 bytes of flash for every character sent.

Thanks!

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Now I know that the PROGMEM stuff is useless for this.

it is? why? I didn't know that, but then again I have very little experience programming bootloaders, I have only worked on one and it was just a premade one I modded a little.

I may be understanding you wrong, but what about something like:

void send_string(const char *s )
{
    while (*s) 
      sendchar(*s++);
}

and then just use PSTR or what ever to store the string into flash, like:

void send_v(void)
{
  send_string(PSTR("version 1.0"));
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm with Brent on this - what's the problem with using PROGMEM in a bootloader?

When you build a bootloader all you are really doing is relocating .text and as you can see in the link scripts:

  *(.progmem*)

is just an early component of .text

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
I'm with Brent on this - what's the problem with using PROGMEM in a bootloader?
I haven't been building it as two separate pieces and then gluing them together. If you don't really want the interrupts active in your boot (and I don't), then you don't need to build the boot as a separate app. I suppose I could build the boot as a separate app -- that means a pretty big rewrite of the loader I already have.

*sigh* If that's the answer then I'll come up with something.

I may also use a variation of the technique in this thread:

https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=48330

Thanks anyway.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would NEVER contemplate writing the boot and the app together. Apart from anything else what happens when you want to issue V2 of the app code and send it out to all those users who have your boot in place. You now need to go through hoops to chop a second copy of the bootloader off the end of it. They are two entirely separate programs - develop them as such.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
then you don't need to build the boot as a separate app.

I may be wrong here again, but now doesn't that just defeat the whole purpose of a bootloader?

if you want just a section of code that starts before main, you could always use one of the inits I suppose ( http://www.nongnu.org/avr-libc/u... ) I guess Im just having a hard time understanding why you would want the bootloader and app in the same spot, though im sure you have your reasons.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff, ph0rkeh,

No, NOT insane! :) I send the entire .hex (app and boot) out all the time. The boot cannot overwrite itself, so who cares? It's a few extra bytes, but since the app is now well over 120K bytes (and may top out at over 200K bytes), an extra 2K bytes just don't matter. It's not a security concern in this case, although I could see it in other cases.

PLUS, if I decide to change the boot my customer can install it direct from that version's .hex file (assuming he has the proper JTAG accoutrements, which he does). My manufacturing folks like it because they only need one file, the latest firmware.

Now, understand that my application has all of ~45 units in the field, and if it is wildly successful MAY exceed 500. TOTAL. All sites have dedicated customer service folks who know how to load the device through JTAG, if needed (it usually isn't). I understand that having > 10K units might change your perspective, but I don't have that problem.

As for V2 of the app, it has grown from a 30K byte pipsqueak to the monster it is now. I have re-arranged the app numerous times. I now support 6 different hardware configurations, 2 dynamically (i.e., I figure out at wakeup what configuration I'm in). So what. The boot has four purposes in life: 1, check the flash for sanity. 2: check for a user-requested drop into the monitor. 3: download firmware into flash (requested either through the monitor or from the app). 4: wake up the app. That's it. If I get real excited I may add some minor block read functions someday. In fact, it's real purpose in life is to serve as the downloader and as a backup in case a download goes fubar.

At any rate, don't always assume the worst case. In some cases it may make sense to build the boot with the app.

Oh, and by the way ph0rkeh, the boot is relocated to the upper 4K of the Flash by using linker sections, so the boot and app do NOT live in the same spot.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Oh, and by the way ph0rkeh, the boot is relocated to the upper 4K of the Flash by using linker sections, so the boot and app do NOT live in the same spot.

im sorry, I must have came across wrong, what I meant was you have two *separate* apps in one source. I just thought thats odd, since the bootloader can be used as a completely different source. but like I said, to all his own.

ok so for my own curiosity, like I said I have little experience with bootloaders, I suppose I should start playing with them more.

so basically you have a hex file that allows the upload of both bootloader and app sections from one hex? my question is does this bootloader still have support for writing to flash? or would the whole AVR have to be programmed over for a new version?

EDIT: oh wait I see, you said it can in your post above, I skimmed that part :P

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stu

But if the boot code is just 2K would it really be that difficult to separate it from the rest of the app to split the thing in two?

As for when you want to install the first hit of boot+app before delivery - you just use srecord to glue the two .hex files together and program that as one entity as you are now.

But one comment you made there got me wondering. If all sites have a JTAG and a trained operator then why the need to waste 4K of the chip's space on a bootloader at all ? ;)

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
But one comment you made there got me wondering. If all sites have a JTAG and a trained operator then why the need to waste 4K of the chip's space on a bootloader at all ? ;)
Good question! It's a matter of the whole system architecture.

Without getting into details, the complete system is a manufacturing test system for testing optical media. Our device is an important but "embedded" part of the complete system, and the JTAG port is not normally accessible. Communication from the system to the device is through a serial port.

Under normal conditions when the system wants to update the device's firmware it needs to talk to it through the serial port. Ergo, the need for a "boot" loader that can take serial data and store it in flash. That kind of firmware update is simple enough that a trained service person is not required.

Yes, the customer service folks have (or actually will have, as we're just now buying them) JTAG programmers, but we don't want that to be the normal mode of firmware download.

I guess I really didn't explain all of this up front.

One other problem I have is that the list of fixes and enhancements is quite large, and my time is finite. If I have a choice between a new function or pulling the boot loader out of my compile, guess which one I'll choose? :D

Finally, when I went through the Atmel app notes and such on boot loaders when I started more than a year ago, there wasn't a nice "how to" tutorial on boot loaders. There really isn't one now. Your suggestions, Cliff, are good and sensible for most applications. Perhaps a Tutorial on Boot Loaders 101 would be a good idea. I'd do it but I'm demonstrably not the right choice. Perhaps I can start something and send it off to you for additions...

At any rate, thanks for the feedback. No hard feelings, I hope! :)

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

stu_stan:
When writing bootloaders for >64k devices, take care where you point the RAMPZ register.
With RAMPZ 0, send_string(PSTR("version 1.0")); will fetch from somewhere in the lower 64k. With RAMPZ=3; send_string(PSTR("version 1.0")); it will fetch from the 64k page where the bootloader resides.

What do you do to keep the boot and app from sharing, say, the 32 bit multiply subroutines (pulled in from the library by the linker, it has to pull them in twice)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

KKP wrote:
When writing bootloaders for >64k devices, take care where you point the RAMPZ register.
With RAMPZ 0, send_string(PSTR("version 1.0")); will fetch from somewhere in the lower 64k. With RAMPZ=3; send_string(PSTR("version 1.0")); it will fetch from the 64k page where the bootloader resides.
Thank you! I was aware of the RAMPZ register and do the proper settings from app to boot and vice-versa. I was already using the pgm_read_byte_far function which should accomplish the same thing.

The issue was not accessing the string, it was locating it in the boot area. The PSTR("version 1.0") code you give will place the string in the main .progmem.data area, not the boot. You are again implying that which Cliff and ph0rkeh has stated: compile the boot and then use the srecord utilities to relocate the code up to high memory. I've already given my reasons for not doing this (although I admit they may be completely bogus and stupid).

KKP wrote:
What do you do to keep the boot and app from sharing, say, the 32 bit multiply subroutines (pulled in from the library by the linker, it has to pull them in twice)
Good Grief! :shock: Why would I want a 32-bit multiply in a boot? In answer to your question (yes, there are other functions in the library I might want), I consider the boot to be a very simple entity: no fancy string manipulation, no serious math, no interrupts (yes, I know they are available), no access to library functions. Perhaps if I downloaded firmware from an SD card, as some folks seem to want to do, I would need that. Thank heaven I don't need to do that!

I obviously have a much more simplified view of what a "bootloader" does than everyone else. Perhaps the common view is that the boot is akin to the PC's BIOS. I just don't see it that way. I would list the purposes of a boot as:

    - Program the flash from the processor itself when needed - Check the processor for basic problems (mis-programmed flash) before starting the app
    - Provide back-up functionality in case the app cannot operate
    - Provide wake-up services for the device that cannot be placed in the application. For example, deciding what hardware configuration we're in
I'm very much in the Keep It Simple Stupid camp here. I avoided the avr-libc functions like the plague because I assumed that I would have the linking problems you mentioned.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The question is not why, the question is how do we put strings in the boot section? But if you must know why, its for debugging my application in Studio 4 with no boot code loaded. And yes its about version numbers.

The way I have found is to use the libc-boot-example to write a function that write the strings from a ordinary “const char” to an unused page (128 byte) in the boot section.

const char ver_str[] = "Version String";

Code in the application section can not write to the boot section so the function must it self must be in the boot section.

void HW_ver(void) __attribute__((section(".bootloader")));

With linker settings:

-Wl,-section-start=.bootloader=0xe000

The compiler ignores any PROGMEM constants in this function with this declaration and the "Version String" ends up as expected both in application and ram section. For me its just for debugging purposes so the waste of flash and ram is no big deal but it sticks in my eyes. Is there really no other way to put strings in boot section when working with the application?

Mats

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One way to do this might be with a custom linker script. Note that I have not tested this at all.

Put your strings in a named section, different than the bootloader, say .bootstrings. Customise the default linker script (in your case avr6.x). At the bottom, but still within the SECTIONS group, place two new output sections like so,


  .bootutil 
: { *(.bootutil*) } .bootstrings
: { *(.bootstrings*) }

Replace the

in both of those groups with the address of where you want them to reside. You can place the strings in a separate fixed location (but still within the bootloader area).

Then, of course, you have to change your makefile to not relocate the sections on the command line, and to tell it to use your custom linker script (just place it in the same directory as your makefile).

Like I said, I don't know if this will work, but it might work.

As to a tutorial, or any kind of how-to documentation, for bootloaders, it would be great if we can include that in the avr-libc user manual.

Part of the problem with bootloaders, is that there are so many different ways to approach it. Yes, there are also reasons to include an application and bootloader together, i.e build it as a single app without the post-build srecord utility stitching. What if you want to be able to update your application and your bootloader out in the field? It can be done by limiting your bootloader to the first half of the bootloader memory. Then when invoking your bootloader, it copies itself to the upper half of the bootloader memory, then it calls itself in the upper memory. The relocated bootloader will then pull in the new application and new bootloader (again that is limited to the lower half of the bootloader memory), resets. There is now a new application and new bootloader. Yes, you can call subroutines from the relocated bootloader, through indirect function calls with a fixed offset from the function address. However, don't do that in AVR GCC 4.x: there's a regression bug that make it fail. One would have to wait until it gets fixed. But I know that the method works in 3.4.x.

HTH
Eric Weddington

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for all the hints. I'll give them a shot.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stu,

Did you ever solve this problem? I'm trying to do the exact same thing.

-- Mike

-- Mike

Michael A. Fallavollita
Vice President of Research and Development
mikef@valentpower.com

Valent Power, Inc.
5617 Scotts Valley Dr. Suite 100
Scotts Valley, CA  95066
   

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Urmmmm... No. I got sidetracked onto something else and never came back to it. Sorry.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is easy enough to have separate projects for the main app and the bootloader and then combine them using the makefile. I use srec_cat to calculate a checksum for the main code and insert it at the end of the main code space. srec_cat then appends the hex code for the bootloader. This is part of the makefile for the main code:

# create final output files (.hex, .eep) from ELF output file, then create checksummed hex file
# also create hex file with bootloader built in
%.hex: %.elf
	$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@
	srec_cat $(TARGET).hex -intel \
             -fill 0xFF 0x0000 0x3BFC \
             -Little_Endian_Checksum_Positive 0x3BFC \
             -unfill 0xFF 3 \
             -o $(TARGET).hex -intel

	srec_cat $(TARGET).hex -intel ../BootLoader/main.hex -intel -o $(TARGET).hex -intel

Arthur

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

adebeun2 wrote:
It is easy enough to have separate projects for the main app and the bootloader and then combine them using the makefile.
Yes, I know. That has been suggested at least twice in this thread. I gave my reasons for not wanting to do that.

In my copious spare time I will probably try it. What the hell, it is only code! :wink:

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay, figured it out.

First, this uses the latest and greatest tool set (WinAVR 20070525) - with any other version, caveat emptor!

The problem really breaks down to two parts: first, how to locate the strings at a particular location, and second how to print them.

The first part is done as follows. In the C, use:

/* definition of special Boot string section */

#define BOOTSTRING_SECTION   __attribute__ ((section (".bootstring")))

const char boot_Version   [] BOOTSTRING_SECTION = "0.5";
const char boot_Monitor   [] BOOTSTRING_SECTION = "DPHIMON ";
. . .

and in your makefile (this is a modified version of an Mfile-generated makefile):

#---------------- Linker Options ----------------
#  -Wl,...:     tell GCC to pass this to linker.
#    -Map:      create map file
#    --cref:    add cross reference to  map file
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref
.
.
.
LDFLAGS += -Wl,--section-start=.bootstring=0x3FA00
. . .

Note that the address for the bootstring section must not overlap any other linker section. I placed my strings above the boot loader, which in my ATmega2560 starts at byte address 0x3e000.

Now, for printing the strings, use:

#include 

void sendVersion()
{
	sendstring_P( (uint32_t) boot_Version );
}

void sendstring_P(uint32_t str_P)
{
	char c;

	do
	{
		c = pgm_read_byte_far( str_P );
		if ( c )
		{
			sendchar( c );
			str_P++;
		}
	} while ( c );
}

Note that the address is actually handled correctly by the compiler, although you do need to cast the address to a 32-bit value for the mega2560. I think you'll need to do this for any processor with more than 64K bytes of flash (m128, m1280, m1281, m2560, m2561, others?). For processors with less than 64K of flash, you can probably use 16-bit addresses and pgm_read_byte instead of pgm_read_byte_far.

This code works on my system. Hopefully it will work for others.

Thanks to everyone for the help!!

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Great job! This is perfect!

Mats