Problem with simple bootloader

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

Hello experts

 

I'm playing a little with the start of a bootloader. Not sure I'm ever getting to use it for anything - just want to understand the basic principles.

 

Let me start by telling you what I have got so far. I have made two simple programs:

1) simulating the "main" program, with the functionality of "++PORTB;". This prorgam is programmed from address 0.

2) simulating the bootloader. This program blinks a few times with a led, then jumps to address 0. This program is programmed from address 0x3e00.

 

When I program both hex files to my target (a mega32), it first executes the "bootloader", then skips to the "main program".

 

My next attempt is the to make the bootloader write the main program. To do this as simple as possible, I have converted the hex file from the previously mentioned "main program" into a byte (or actually word) array, and hardcoded this array into the bootloader project. When I program my target, I then want to erase the entire chip, and make the bootloader write the "main program" into the flash at address 0, and then run it.

 

But I have run into a problem, and have now been stuck for quite some time - actually I think I have (at least) two problems. 

 

Here's my source:

// Tell the linker to put this code in the bootloader sector
asm("  .section .text"); 

#include <avr/boot.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

// The "main program"
const uint16_t program[] = {
0x0C94,0x2A00,0x0C94,0x3400,0x0C94,0x3400,0x0C94,0x3400,
0x0C94,0x3400,0x0C94,0x3400,0x0C94,0x3400,0x0C94,0x3400,
0x0C94,0x3400,0x0C94,0x3400,0x0C94,0x3400,0x0C94,0x3400,
0x0C94,0x3400,0x0C94,0x3400,0x0C94,0x3400,0x0C94,0x3400,
0x0C94,0x3400,0x0C94,0x3400,0x0C94,0x3400,0x0C94,0x3400,
0x0C94,0x3400,0x1124,0x1FBE,0xCFE5,0xD8E0,0xDEBF,0xCDBF,
0x0E94,0x3600,0x0C94,0x4500,0x0C94,0x0000,0x8FEF,0x87BB,
0x88B3,0x8F5F,0x88BB,0x2FEF,0x84E3,0x9CE0,0x2150,0x8040,
0x9040,0xE1F7,0x00C0,0x0000,0xF3CF,0xF894,0xFFCF
};

int main(void)
{
	DDRB = 0xff;
	
	// Program something...
	cli();
	uint16_t sizeInBytes = sizeof(program);
	uint8_t sizeInPages = (sizeInBytes/128)+1;
	
	// Erase flash
	for(uint8_t i=0; i<sizeInPages; ++i)
		boot_page_erase_safe(i);
		
	// Write the program
	uint16_t b=0;
	uint16_t tp;
	for(uint8_t p=0; p<sizeInPages; ++p) {
		// Copy the program to the page buffer
		for(uint8_t i=0; i<32 || b<sizeInBytes; ++i, ++b)  {
			// TODO: Should data be converted to little-endian
			tp = ((program[b]&0xff)<<8) + (program[b]>>8);
			boot_page_fill_safe(b, tp);
		}
		// Store buffer in flash
		boot_page_write_safe(p);
	}
	
	// Enable program section again
	boot_rww_enable_safe();
	
	// Jump to the start of the program the bootloader has supposedly programmed
	asm("jmp 0000;"); 
}

 

The first problem is the my program is not written to the flash. I have tried to write something else to the flash, and it seems that my "bootloader" is capable of erasing the flash where it's supposed to write the program. But after that, nothing more happens. The best result I have had so far, is that I had the bootloader writing 8 zeroes... not really what I expected... Any ideas what I'm missing?

 

When I get over the hurdle, I'm very sure that I have not understood the concept of the hex file compleatly. If I want to write the remote side of the bootloader, I will probably also have to understand that. How does my hardcoded representation of the "main program" look? Heres the original hex file, of the program I tried to turn into the byte array.

 

:100000000C942A000C9434000C9434000C943400AA
:100010000C9434000C9434000C9434000C94340090
:100020000C9434000C9434000C9434000C94340080
:100030000C9434000C9434000C9434000C94340070
:100040000C9434000C9434000C9434000C94340060
:100050000C94340011241FBECFE5D8E0DEBFCDBF25
:100060000E9436000C9445000C9400008FEF87BB73
:1000700088B38F5F88BB2FEF84E39CE021508040E2
:0E0080009040E1F700C00000F3CFF894FFCFEE
:00000001FF

I have read quite a bit on the topic, including other bootloader. Most of what others a doing are in asm (which I assume they have a very good reason for doing), but I would like to keep this really simple and in plain c for now. 

 

The sketch for my bootloader comes from the source documentation on boot.h (http://ugweb.cs.ualberta.ca/~c27...)

 

Any help is highly appreciated. I also gladly receive comment on my code and my own comments in the likely case that I have misunderstood something.

This topic has a solution.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
for(uint8_t i=0; i<sizeInPages; ++i)
    boot_page_erase_safe(i);
                   boot_page_fill_safe(b, tp);
		}
		// Store buffer in flash
		boot_page_write_safe(p);

According to the documentation, the various boot_page_* calls require an ADDRESS as an argument, not a page number, nor an offset within a page.

 

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

That's a good point. How ever, I still can not get it to work. I moved the boot_page_write_safe() into the for-i loop, and changed the argument to 'b' in stead of 'p'. With that I would the expect to see something written to the flash, but there is still nothing (0xff) on address 0 and forward.

 

Another detail then. If you look at the code example in the boot.h documentation (http://www.atmel.com/webdoc/AVRL...) they also use a page number for exactly those to macros.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
If you look at the code example in the boot.h documentation (http://www.atmel.com/webdoc/AVRLibcReferenceManual/group__avr__boot.html)
they also use a page number for exactly those to macros.

They used a variable named "page", but it sure looks like it is an address to me, since 1) It's a uint32_t and 2) they ADD the offset (i) to it when filling individual bytes:


void boot_program_page (uint32_t page, uint8_t *buf)
{
    uint16_t i;
//   :
    boot_page_erase (page);
    boot_spm_busy_wait (); // Wait until the memory is erased.

    for (i=0; i<SPM_PAGESIZE; i+=2) { // Set up little-endian word.
	uint16_t w = *buf++; w += (*buf++) << 8;
	boot_page_fill (page + i, w);
    }

 

You'll forgive me if I don't try to guess what your current code looks like?

You can look at Arduino's st500v2 bootloader; it seems to be one of the few bootloaders that actually uses the avr-libc boot_* functions :-(  here's the core write loop:

if ( msgBuffer[0] == CMD_PROGRAM_FLASH_ISP )
{
    // erase only main section (bootloader protection)
    if (eraseAddress < APP_END )
    {
	boot_page_erase(eraseAddress);	// Perform page erase
	boot_spm_busy_wait();		// Wait until the memory is erased.
	eraseAddress += SPM_PAGESIZE;	// point to next page to be erase
    }

    /* Write FLASH */
    do {
	lowByte		=	*p++;
	highByte 	=	*p++;

	data		=	(highByte << 8) | lowByte;
	boot_page_fill(address,data);

	address	=	address + 2;	// Select next word in memory
	size	-=	2;				// Reduce number of bytes to write by two
    } while (size);					// Loop until all bytes written

    boot_page_write(tempaddress);
    boot_spm_busy_wait();
    boot_rww_enable();	
    

 

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

Hi westfw - it works now :) Thanks a lot.

 

I didn't really do a lot, except from basically copying the lines from the arduino loader you send, but for the sake of good measure I better post my solution to the problem. Another small change is the "program" is changed into a byte array (not that that is really important for my original problem):

 

uint8_t program[] = {
	0x0C,0x94,0x2A,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,
	0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,
	0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,
	0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,
	0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,0x0C,0x94,0x34,0x00,
	0x0C,0x94,0x34,0x00,0x11,0x24,0x1F,0xBE,0xCF,0xE5,0xD8,0xE0,0xDE,0xBF,0xCD,0xBF,
	0x0E,0x94,0x36,0x00,0x0C,0x94,0x45,0x00,0x0C,0x94,0x00,0x00,0x8F,0xEF,0x87,0xBB,
	0x88,0xB3,0x8F,0x5F,0x88,0xBB,0x2F,0xEF,0x84,0xE3,0x9C,0xE0,0x21,0x50,0x80,0x40,
	0x90,0x40,0xE1,0xF7,0x00,0xC0,0x00,0x00,0xF3,0xCF,0xF8,0x94,0xFF,0xCF
};

int main(void)
{
	cli();
	
	// Erase flash
	for(uint16_t w=0; w<sizeof(program); w+=2) {
		boot_page_erase_safe(w);
	}
		
	// Write the program
	for(uint16_t w=0; w<sizeof(program); w+=2) {
		uint16_t data = program[w]; 
		data += program[w+1]<<8;
		
		boot_page_fill_safe(w, data);
		boot_page_write_safe(w);
	}

	// Enable program section again
	boot_rww_enable_safe();
	// Jump to the start of the program the bootloader has supposedly programmed
	asm("jmp 0000;"); 
}

Thank again.

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

They used a variable named "page", but it sure looks like it is an address to me, since 1) It's a uint32_t and 2) they ADD the offset (i) to it when filling individual bytes

I've been bitten by this too. avr/boot.h has an example boot_program_page() at the top (which is actually the easiest way to do all this - just copy that routine!) but the first parameter to that function is "uint32_t page". I thought that because it was called "page" they wanted a page number but really it just wants a byte address (that is the boundary of a page).