Bootloader Pre-Tutorial Source Code - ATmega644

Go To Last Post
64 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm in the midst of writing a Bootloader Tutorial for the Tutorials Forum and I've gotten a bootloader working in C for the ATmega644 that I wanted to post and let folks have at it before I get it committed to it an screw up some newbies who will try to learn about bootloaders by looking at it. I do know one problem, the USART code works, but uses some wrong constants that just happen to have the right value.

Anyway, I'd appreciate it if you guys would give 'er a look and critique'r before it gets set in concrete.

[edit]3/8/08 took mike and cliff's suggestions. Based on mikes suggestion, code size went from 2372 bytes to 1993 bytes so now it will fit in the 1024 word boot section! THANKS MIKE! Will investigate Cliff's suggestion about -nostdlib and nostartfiles later today. THANKS CLIFF![/edit]

[edit] 3/10/08 I've reduced the size for a minimum bootloader that still works with AVRProg and posted it on the second page of this thread, it is for the ATmega32 and is 912 bytes so it fits in the 512 word boot section.[/edit]

/*****************************************************
	RequiredText.c 3/7/08 Joe Pardue
******************************************************/

#include 
#include  
#include 
#include 

static void USARTInit();
static void sendByte( uint8_t );
static uint8_t receiveByte( void );
//static void sendBytePROGMEM(const uint8_t *);

static void AVR109CommandParser(void);

static void enterProgramMode(void);
static void autoIncrementAddress(void);
static void setAddress(void);
static void chipErase(void);
//static void leaveProgramMode(void);
static void selectDeviceType(void);
static void readSignatureBytes(void);
static void returnSupportedDeviceCodes(void);
static void returnSoftwareIdentifier(void);
static void returnSoftwareVersion(void);
static void returnProgrammerType(void);
static void setLED(void);
static void clearLED(void);
static void exitBootloader(void);
static void checkBlockSupport(void);
static void BlockLoad(uint16_t size, uint8_t type);
static void startBlockFlashLoad(uint16_t size);
static void startBlockEEPROMLoad(uint16_t size);
static void BlockRead(uint16_t size, uint8_t type);
static void startBlockFlashRead(uint16_t size);
static void startBlockEEPROMRead(uint16_t size);

//static void badAVR109Command(uint8_t);

//const uint8_t PM_badAVR109Command[] PROGMEM = "badAVR109Command\r";

#define	BOOT_STATE_PORT			PORTD
#define	BOOT_STATE_PIN			PIND
#define	BOOT_STATE_PIN_NUMBER	PD7  
 
//const uint8_t IS_BOOTLOADER[] PROGMEM = "IS the bootloader.\r";
//const uint8_t NOT_BOOTLOADER[] PROGMEM = "NOT the bootloader.\r";

uint16_t address;
uint8_t device;

#define SUPPORTED_DEVICE_CODE 	0x73

// Signature bytes for ATmega644
#define	SIGNATURE_BYTE_1		0x1E
#define	SIGNATURE_BYTE_2		0x96
#define	SIGNATURE_BYTE_3		0x09

// Function pointer to jump to the applicaion memory section
void (*funcptr)( void ) = 0x0000; 

#define BOOTSIZE 4096 // For test only

// SRAM Page buffer for flash page
uint8_t pageBuffer[SPM_PAGESIZE];

uint16_t address;

#define UART_RX_BUFFER_SIZE SPM_PAGESIZE
unsigned char gBuffer[UART_RX_BUFFER_SIZE];

int main(void)
{
	/* Function pointer to jump to the applicaion memory section */
    //void (*funcptr)( void ) = 0x0000;       

	//uint8_t b;

	USARTInit();

	// Use bootloader or application code?
    if( !(BOOT_STATE_PIN & (1<<BOOT_STATE_PIN_NUMBER)) ) /* If BOOT_STATE_PIN_NUMBER is low, use bootloader. */
    {
		while(1)
		{
			// wait for esc character (0x1B) and respond with AVRBOOT
			if(receiveByte() == 0x1B)
			{
				// wait for software identifier request
				while( receiveByte() != 'S');
				
				// answer the request
				returnSoftwareIdentifier();
				
				// begin servicing the commands
				AVR109CommandParser();
			}
			else sendByte('?');
		}

	}
	else
	{
		// If BOOT_STATE_PIN_NUMBER is high, don't use bootloader.
		// So... jump to the application 
		funcptr();
	}

	return 0;
}

void AVR109CommandParser()
{
	uint8_t cmd;
	uint16_t tempSize;

	while(1)
	{
		cmd = receiveByte();

		switch (cmd)
		{
			case 'P':
				enterProgramMode();
				break;
			case 'a':
				autoIncrementAddress();
				break;
			case 'A':
				setAddress();
				break;
			case 'e':
				chipErase();	
				break;
			case 'L':
				enterProgramMode();
				break;
			case 'T':
				selectDeviceType();
				break;					
			case 's':
				readSignatureBytes();	
				break;			
			case 't':
				returnSupportedDeviceCodes();	
				break;
			case 'S':
				returnSoftwareIdentifier();	
				break;			
			case 'V':
				returnSoftwareVersion();	
				break;			
			case 'x':
				setLED();
				break;
			case 'y':
				clearLED();
				break;
			case 'p':
				returnProgrammerType();	
				break;			
			case 'E':
				exitBootloader();	
				break;
			case 'b':
				checkBlockSupport();	
				break;
			case 'B':
				tempSize = (receiveByte() << 8) | receiveByte();
				BlockLoad( tempSize, receiveByte() );
				break;
			case 'g':
				tempSize = (receiveByte() << 8) | receiveByte();
				BlockRead( tempSize, receiveByte() );	// Perform the block read
				break;									
			default:
				if(cmd != 0x1B) sendByte('?');
		}
	}
}	


/*****************************************************
	AVR109 Self Programming Commands
******************************************************/

void enterProgramMode() // 'P'
{
	// what else is it going to do?
	sendByte('\r');
}

void autoIncrementAddress(void) // 'a'
{ 
	// Yes, this code autoincrements
	sendByte('Y');
}

void setAddress(void) // 'A'
{ 
	// Note that flash addresses are in words, not bytes               
    address = receiveByte();   
	address = (address<<8) | receiveByte();
	address = address << 1; // convert word address to byte address

 	sendByte('\r');  // respond okay
}

void chipErase(void) // 'e'
{ 
 	int i;
 	for(i = 0 ; i < (FLASHEND - (BOOTSIZE * 2)); i += SPM_PAGESIZE)
	{
    	boot_page_erase(i);	// Erase the page
    	boot_spm_busy_wait();		// Wait until finished.
	}
   
	sendByte('\r');  // respond okay
}

/*void leaveProgramMode() // 'L'
{
	// what else is it going to do?
	sendByte('\r');
}*/

void selectDeviceType() // 'T'
{
	//dummy read since we only have one device type
	uint8_t dummy;
	dummy = receiveByte();
	sendByte('\r');
}

void readSignatureBytes(void) // 'S'
{ 
    sendByte( SIGNATURE_BYTE_3 );
    sendByte( SIGNATURE_BYTE_2 );
    sendByte( SIGNATURE_BYTE_1 );
}

void returnSupportedDeviceCodes(void) // 't'
{
	sendByte(SUPPORTED_DEVICE_CODE); // Support only this device
	sendByte(0); // list terminator
}

void returnSoftwareIdentifier(void) // 'S'
{ 
    // Software identifier is 'AVRBOOT'
	sendByte('A');
    sendByte('V'); 
    sendByte('R');
    sendByte('B');
    sendByte('O');
    sendByte('O');
    sendByte('T');
}

void returnSoftwareVersion(void) // 'V'
{ 
	// Return software version.
    sendByte('0');
    sendByte('1');
}

void setLED(void)
{
	// ignore command
	receiveByte();
	sendByte('r');
}

void clearLED(void)
{
	// ignore command
	receiveByte();
	sendByte('r');
}

void returnProgrammerType(void) // 'p'
{      
	// Get programmer type - serial.
    sendByte('S');
}

void exitBootloader(void)
{ 
	sendByte('\r');
    
	funcptr(); // jump to application
}

void checkBlockSupport(void) // 'b'
{ 
    sendByte('Y'); // yes, block load is supported.
    sendByte((SPM_PAGESIZE>>8) & 0xFF); // send MSB first.
    sendByte(SPM_PAGESIZE & 0xFF); // send LSB second.
}

void BlockLoad(uint16_t size, uint8_t type)
{ 
	if(type == 'F')
	{
		startBlockFlashLoad(size);// load flash
	}
	else if(type == 'E')
	{
		startBlockEEPROMLoad(size);// load EEPROM
	}
	else sendByte('?');
}

void startBlockEEPROMLoad(uint16_t size)
{ 
	if(size <= SPM_PAGESIZE)
	{
		uint16_t i;
		for( i = 0 ; i < size; i++)
		{
			pageBuffer[i] = receiveByte();
		}

		eeprom_write_block((const void*)pageBuffer,(void*)address,size);

		sendByte('\r');
	}
	else sendByte('?');
}

void startBlockFlashLoad(uint16_t size)
{ 
	uint16_t tempAddress = address;

	uint16_t i,cnt,tempWord;

	// store values to be programmed in temporary buffer
	for (cnt=0; cnt 0


}
void startBlockFlashRead(uint16_t size)
{ 
	uint8_t data;
	do {
		data = pgm_read_byte_near(address++);	// read_program_memory(address,0x00);
		sendByte(data);			   				// send byte
		size--;							// reduce number of bytes to read by one
	} while (size);						// loop through size
}

/*void badAVR109Command(uint8_t cmd)
{
	// return the bad command with an error message
	sendBytePROGMEM(PM_badAVR109Command);
	sendByte(cmd);
}*/


/*****************************************************
	Functions from UART_Test
******************************************************/

void USARTInit()
{
	/* Set baud rate hard coded to 19200 for 12MHz */
	UBRR0L = 38;
	/* Enable receiver and transmitter */
	UCSR0B = (1<<RXEN0)|(1<<TXEN0);
	/* Set frame format: n,8,1 */
	UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); // Use 8-bit character sizes 
}

void sendByte( uint8_t data )
{
	/* Wait for empty transmit buffer */
	while ( !( UCSR0A & (1<<UDRE0)) )
	;
	/* Put data into buffer, sends the data */
	UDR0 = data;
}

uint8_t receiveByte( void )
{
	// Wait for data to be received 
	while ( !(UCSR0A & (1<<RXC0)) )
	;
	// Get and return received data from buffer 
	return UDR0;
}

/*void sendBytePROGMEM(const uint8_t *FlashString)
{
	int i = 0;

	// The 'for' logic terminates if the byte is '\0' or if i = 60.
	// '\0' is 'null' and terminates C strings
	// The 80 prevents too much overrun if we get a bad pointer
	// and it limits the string size	
	for( i = 0 ; pgm_read_byte(&FlashString[i]) && i < 80; i++) 
	{
			sendByte(pgm_read_byte(&FlashString[i]));
	}
} */

[edit] This is curtvm's suggestion (below in the thread) for the makefile:

Quote:
Its simple to change in AVR Studio, (assuming mega164 here with 1024word bl) Project->Configuration Options->Memory Settings->Add->Memory Type: Flash, Name: .text, Address (Hex): 0x1C00 (that's a word address here).
This if for the ATmega32 for the ATmega644 you need to set the address to 0x7C00.

[/edit] changed UCSRC setting

Last Edited: Tue. Mar 2, 2010 - 11:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The code looks ok from a quick code scan - i.e. all the right pieces. I would put a BSD source code license at the top of the file and add a lot more comments to show what is going on especially as this is a tutorial.

You didn't supply a Makefile so I reused one I had and WinAVR 20071221. I got two warnings that should be eliminated:

boot.c:10: warning: function declaration isn't a prototype
boot.c:410: warning: function declaration isn't a prototype

One key aspect of bootloaders is the elimination of extra code such as the vector table and various other startup overheads. If you make all of the functions static or inline, it is harder to read the LSS file or debug but saves 300 bytes.

After making all the functions static I get the additional warning:

boot.c:223: warning: 'leaveProgramMode' defined but not used

The following line is repeated:

static void (*funcptr)( void ) = 0x0000;

Does your tutorial start off with code that can be run as a simple application and is then incrementally added too, moved to the bootloader section of flash, then tuned for code space? That is one way to develop a bootloader.

Are you planning to mention how other download protocols could be used instead of AVR109?

I think a baud rate friendly frequency is better than 12 MHz. You should also remove the hardcoded UBRR0L value and use the standard macro.

I have a 644 in my parts box but haven't had a chance to actually test your code.

--Mike

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

If the code can work without relying on the globals being zeroed in the preamble then try adding -nostdlib to the link options which will drop the vector table and the C preamble. But you will need to add just a little bit of asm near the start to make sure _zero_reg__ contains 0 and the SP is initialised. Alternatively look at the effect of -nostartfiles which is similar.

(I explained how I used these (and worked around what they don't deliver) in the "smallest bootloader" thread)

BTW, like Mike says, funcptr is defined twice. As the global copy is not used (and appears to be the only .data/.bss entry that might rely on being 0) then ditch that and use the automatic copy inside main()

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

mikeperks wrote:
The code looks ok from a quick code scan - i.e. all the right pieces. I would put a BSD source code license at the top of the file and add a lot more comments to show what is going on especially as this is a tutorial.
I will havea lot more comments, especially in the text of the tutorial which will elaborate each section of the code. I personally don't see the value of a license of any sort since this will be in the public domain and I really don't care how it is used. Why take up the space?

mikeperks wrote:
You didn't supply a Makefile so I reused one I had and WinAVR 20071221. I got two warnings that should be eliminated:
boot.c:10: warning: function declaration isn't a prototype
boot.c:410: warning: function declaration isn't a prototype

Added the makefile, more details on using it in the tutorial.

mikeperks wrote:

One key aspect of bootloaders is the elimination of extra code such as the vector table and various other startup overheads. If you make all of the functions static or inline, it is harder to read the LSS file or debug but saves 300 bytes.

THANKS, SEE BELOW!

mikeperks wrote:

After making all the functions static I get the additional warning:
boot.c:223: warning: 'leaveProgramMode' defined but not used

The following line is repeated:

static void (*funcptr)( void ) = 0x0000;

Got rid of them.

mikeperks wrote:

Does your tutorial start off with code that can be run as a simple application and is then incrementally added too, moved to the bootloader section of flash, then tuned for code space? That is one way to develop a bootloader.
The tutorial will start off with some very simple and progressively more complex discussion and code for flash and EEPROM.

mikeperks wrote:
Are you planning to mention how other download protocols could be used instead of AVR109?
Nope. I plan to slowly build up the knowledge for an AVR109 bootloader and assume that if anyone makes it through that, they should be able to move forward on their own.

mikeperks wrote:
I think a baud rate friendly frequency is better than 12 MHz.
I'm using my BBUSB thingee which provides the 12MHz clock and the power supply for the breadboard. And it works just fine at 19200 baud.

mikeperks wrote:
You should also remove the hardcoded UBRR0L value and use the standard macro.
It's on my TODO list.

mikeperks wrote:
I have a 644 in my parts box but haven't had a chance to actually test your code.
Hey, you saved me over 300 bytes, your contribution is major!

I made changes suggested by Mike and the code size went from 1186 words (requiring the 2046 word size boot section size) to 997 word meaning that I can now use the 1024 word boot section size thus saving 2048 bytes of memory space. I'm not in a smallest bootloader contest since I am opting for clarity over size in the tutorial, but a whoping 2048 byte saving is a great contribution - THANKS MIKE!

I'll look into Cliffs suggestion later today.

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

Quote:
I personally don't see the value of a license of any sort since this will be in the public domain and I really don't care how it is used. Why take up the space?

Public domain works just as well as a BSD license, it's just that right now there isn't anything in the comments indicating anything. In modern copyright law, simply by writing it you have a copyright. Unless you explicitly license it or put in a statement placing it in the public domain, nothing gives the rest of us a legal right to use it. Of course, you probably already know all this, but I'm borrowing your soap box inform those that might not.

Back on topic... I'm just starting to study the internals of bootloaders seriously for the first time, so this is very timely and I've already learned a lot. Should be a great tutorial. More comments when I give the code a thorough study.

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

Since I did this originally with a boot size of 4096, not knowing how large it would actually be, and Mike gave me the way to reduce it so that it will now fit in the 1024 section, the following procedure will convert the code and AVRStudio to the smaller bootsize:

Changes bootloader size from 4096 to 1024

1.In the AVR Programmer window fuses tab
     a.From: Boot Flash size= 4096
     b.To: BootFlash size=1024
2.In makefileboot change
     a.From: LDFLAGS += -Wl,-section-start=.text=0xE000
     b.To: LDFLAGS += -Wl,-section-start=.text=0xFC00
3.Change BOOTSIZE 
     a.From: #define BOOTSIZE 4086 // For test only
     b.To: #define BOOTSIZE 1024 // For test only

Smiley

Last Edited: Sat. Mar 8, 2008 - 06:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dbc wrote:
Quote:
I personally don't see the value of a license of any sort since this will be in the public domain and I really don't care how it is used. Why take up the space?

Public domain works just as well as a BSD license, it's just that right now there isn't anything in the comments indicating anything. In modern copyright law, simply by writing it you have a copyright. Unless you explicitly license it or put in a statement placing it in the public domain, nothing gives the rest of us a legal right to use it. Of course, you probably already know all this, but I'm borrowing your soap box inform those that might not.

Back on topic... I'm just starting to study the internals of bootloaders seriously for the first time, so this is very timely and I've already learned a lot. Should be a great tutorial. More comments when I give the code a thorough study.

Okay, I'll have to give this some thought since I didn't realize that by not having the license rigramrole that I was actually copyrighting the stuff. So let me add this to my plate and see what I can come up with.

Smiley

Last Edited: Sat. Mar 8, 2008 - 06:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is a BSD license template... Clause #3 is optional.

Quote:

Software License Agreement (BSD License)

Copyright (c) ,
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

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

smileymicros wrote:
Okay, I'll have to give this some thought since I didn't realize that by not having the license rigramrole that I was actually copyrighting the stuff. So let me add this to my plate and see what I can come up with.
Not much to add really. BSD (or more strictly modified BSD) is a very open license that allows anyone to do anything with your code providing they preserve your original license. As dbc says, a license is important because it tells the rest of us who wrote the code and what we can do with it. Larger corporations (i.e. those with IP Lawyers) will not touch open source code unless it has the author names and a license. To save you some time, here is the BSD license text you could use:
/*
 *  BSD License
 *  -----------
 *
 *  Copyright (c) 2008, Smiley Micros, All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without 
 *  modification, are permitted provided that the following conditions are met: 
 *
 *  - Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.  
 *	
 *  - Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.  
 *	
 *  - Neither the name of the Smiley Micros nor the names of its contributors 
 *    may be used to endorse or promote products derived from this software 
 *    without specific prior written permission.  
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 *  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 *  POSSIBILITY OF SUCH DAMAGE.
 */

It's the same as the one I use for Oak Micros except I replaced "Oak" by "Smiley". You may also want to put the author name in the header as well in the same place as the description of the source file.

--Mike

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

Is there a copyright on the use of copyright agreements?

It seems that some lawyer would have figured he/she could make a buck on copyrighting the copyright of a copyright, etc. by now.

It seems that copyrights get tossed around awfully freely.

You can avoid reality, for a while.  But you can't avoid the consequences of reality! - C.W. Livingston

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

This is not really a copyright, merely an agreement that the software is open source and available to everybody for commercial or personal use, with or without modifications. It also protects smiley from potential civil actions if the bootloader was defective in some way, and for example of it was installed in some medical equipment and caused injuries.

The BSD license is free to use I believe.

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

Copyright exists as soon as the work is created. A licence tells people what they may do with it, and makes it explicit. You (as copyright holder) can change the licence terms at any time; from completely forbidden for use to completely open, and you can have different terms for different people.

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

It should be able to run easily with a M324p right? Still waiting for some M644p to come in.

Oh, and of course it is to be used with USART0? (M644 has just 1 USART M644P has 2 AFAIK)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

js wrote:
It should be able to run easily with a M324p right? Still waiting for some M644p to come in.

Oh, and of course it is to be used with USART0? (M644 has just 1 USART M644P has 2 AFAIK)

I would hope so. I tried to port it to the ATmega32 and everything went nuts, so I ate a bacon sandwich and took a nap. Now I'm about to go for a walk and I'll think about attacking it again tomorrow.

Anyway, if it does work on the ATmega324p, I'd appreciate a note as to what you had to change and/or any other comments you'd like to make.

Thanks,
Smiley

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

hmmm nothing like a dummie's input :lol: If I can break it, I will...and I have.

Just tried with a M164p because that's what I have on the bench now with a Dragon. So I did change the chip number in the makefile (will need to change the signature bytes too eventually) and nothing more.

I can get it to compile with a Studio generated makefile, which is not good of course as it puts things at 0x0000. Using the original makefile (pretending it was a M644) I get an error in line 50 (line 53 in my modified makefile which is attached)

## Compile 
Required_Test.o: ../Required_Test.c 
   $(CC) $(INCLUDES) $(CFLAGS) -c  $< 

last line

and it does not compile.

In order to preserve life and limb that's all I can do for the day I think...

Attachment(s): 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Quote:
which is not good of course as it puts things at 0x0000
Its simple to change in AVR Studio, (assuming mega164 here with 1024word bl) Project->Configuration Options->Memory Settings->Add->Memory Type: Flash, Name: .text, Address (Hex): 0x1C00 (that's a word address here).

For mega16/32 types, you will need to change the usart defines (its macro time).

I have just compiled it (not tested) for mega164p, mega32, mega64, mega324p, mega168, and it compiles without error on all of them (a little macro work for the older usart types). I just let AVR Studio do the makefile work (as I always do- Cliff if still trying to convert me).

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

curtvm wrote:
Quote:
which is not good of course as it puts things at 0x0000
Its simple to change in AVR Studio, (assuming mega164 here with 1024word bl) Project->Configuration Options->Memory Settings->Add->Memory Type: Flash, Name: .text, Address (Hex): 0x1C00 (that's a word address here).

For mega16/32 types, you will need to change the usart defines (its macro time).

I have just compiled it (not tested) for mega164p, mega32, mega64, mega324p, mega168, and it compiles without error on all of them (a little macro work for the older usart types). I just let AVR Studio do the makefile work (as I always do- Cliff if still trying to convert me).

Thanks for the pointer. This does create a makefile with the code located in the bootsection. I've buggered my hardware setup so I can't test it till tomorrow, but it looks like the way to do it. I guess I need to look at the new stuff in AVRStudio since I entirely missed that memory configuration feature.

Smiley

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

Okay! The curtvm makefile suggestion works so I've added that to the OP.

Smiley

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

It's ALIVE!!!

After doing my favourite mods to the USART init (as I don't have the exact hardware) I can communicate with the bootloader. The USART init has the 2 Stop bits stuff still there like the other tutorial.

Added definition:

//define Baud rate
#define baud 19200

Modified USART init:

/***************************************************** 
   Functions from UART_Test 
******************************************************/ 

void USARTInit() 
{ 
   // Set baud rate

	UBRR0H = ((F_CPU / (16UL * baud)) - 1)>>8;
	UBRR0L = (F_CPU / (16UL * baud)) - 1;


   /* Enable receiver and transmitter */ 
   UCSR0B = (1<<RXEN0)|(1<<TXEN0); 
   
   /* Set frame format: 8data, 2stop bit */ //****************
  // UCSR0C = (1<<USBS0)|(3<<UCSZ00); 
} 

The above will automatically set up the correct baud rate given "baud" and the clock, which gets sucked out the makefile.

If the bootloader is intended to be used in various chips, this becomes messy:

#define SUPPORTED_DEVICE_CODE    0x73 

// Signature bytes for ATmega644 
#define   SIGNATURE_BYTE_1      0x1E 
#define   SIGNATURE_BYTE_2      0x96 
#define   SIGNATURE_BYTE_3      0x09 

as it would need conditional compile depending on the micro.

I thought it would be easy enough to just get it from the chips' header file but not so!! The assembler header files have that information in them as:

; ***** SPECIFY DEVICE ***************************************************
.device ATmega164P
#pragma AVRPART ADMIN PART_NAME ATmega164P
.equ	SIGNATURE_000	= 0x1e
.equ	SIGNATURE_001	= 0x94
.equ	SIGNATURE_002	= 0x0a

#pragma AVRPART CORE CORE_VERSION V2E
Quote:

None of the C compilers seems to have that info in them...not unexpected given the fledgling state of the C language...not as advanced as ASM.. :? it will improve.

I will start a thread to ask for that info to be added into the header files in the future. May never happen of course.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

I was thinking the same sing the other day (signatures), so I wrote a little script to parse the asm headers, and extract the signatures. I can do it again if its wanted. Maybe a nice table of signatures would be handy.

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

curtvm wrote:
I was thinking the same sing the other day (signatures), so I wrote a little script to parse the asm headers, and extract the signatures. I can do it again if its wanted. Maybe a nice table of signatures would be handy.

Yeah, you'd think that wouldn't you. How can you create a generic bootloader with #ifdefs to specific devices without that information? You'd think it would be available somewhere already.

Smiley

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

Quote:
You'd think it would be available somewhere already.
It is, in C:\Program Files\Atmel\AVR Tools\AvrAssembler2\Appnotes xxxx*.inc files for each chip :)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Not all seems to be well. Even though both AVRprog and AVROSPII seem to communicate with the bootloader, programming fails to verify (or work) with both Flash and EEPROM. The chip's "signature" is detected by AVROSP in automatic detection as the Mega644 (haven't changed that yet).

Must do some paid work for a while now.....

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Here's a summary- PDF, raw text, and header.

Header untested, though.

Attachment(s): 

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

One of things that I'd like to mention: We could always use more help with documentation in AVR-LibC. Instead of filling up the tutorials forum with fantastic work, it would be nice to include it in the AVR-LibC User Manual where it will get wider distribution.

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

curtvm wrote:
Here's a summary- PDF, raw text, and header.

Header untested, though.

As I mentioned to js in another thread, please submit a feature request at the avr-libc project, and not here in the forums.

I lean towards including the signature information in each individual IO header file.

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

Quote:
I lean towards including the signature information in each individual IO header file.
That would be the place to put it. I was just helping the '109 Bootloader Boys' on their quest. The header seemed like a good idea after I created the pdf, since I just needed to change a couple lines in my script to create it.

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

Quote:
I was just helping the '109 Bootloader Boys' on their quest.
Then we need a small change to make it compatible with Smiley's code. :)

edit ...and of course

#include "signatures.h"

and

// Signature bytes for ATmega644 
//#define   SIGNATURE_BYTE_1      0x1E 
//#define   SIGNATURE_BYTE_2      0x96 
//#define   SIGNATURE_BYTE_3      0x09 

What about the SUPPORTED_DEVICE_CODE? Does that change with every device like it used to with the old AVR910 firmware? If so then it could be added to the signature file for each device.

I think this will need to be the case for now untill the next release if the changes are added to the header files.

Now the programmer (AVROSPII) detects the correct "signature", still not programming though. :(

Attachment(s): 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

I've reduced the code to the minimum (I think) for AVRProg to work. It now is 912 bytes so it fits in the 512 word boot section. This version if for the ATmega32

[/edit] added watchdog timer reset to USART functions and deleted a duplicate definition for 'address'

/*****************************************************
	RequiredText.c 3/10/08 Joe Pardue
	Minimum AVR109 bootloader commands
	ATmega32 Verson
******************************************************/

// I HATE LICENSES LIKE THIS BUT I've been told that without
// the license then the work is automatically copyrighted in my name
// since my purpose is to educate, I want the code to used by whoever
// wants to use it to learn something. If you like it, then visit
// my website www.smileymicros.com and buy something.

/*
 *  BSD License
 *  -----------
 *
 *  Copyright (c) 2008, Smiley Micros, All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 *   
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 *   
 *  - Neither the name of the Smiley Micros nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission. 
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

// And to further cover my ass, let me add that if you use this software
// it will destroy whatever machine you use it on and kill anyone in a one 
// kilometer radius. So don't even consider using it for any reason whatsoever!


#include 
#include  
#include 
#include 
#include  // watchdog timer 

// USART declarations
static void USARTInit();
static void sendByte( uint8_t );
static uint8_t receiveByte( void );

// AVR109 Command Parser declaration
static void AVR109CommandParser(void);

// AVR109 Command declarations
static void enterProgramMode(void);
static void autoIncrementAddress(void);
static void setAddress(void);
static void chipErase(void);
static void selectDeviceType(void);
static void readSignatureBytes(void);
static void returnSupportedDeviceCodes(void);
static void returnSoftwareIdentifier(void);
static void returnProgrammerType(void);
static void checkBlockSupport(void);
static void blockFlashLoad(uint16_t size);
static void blockFlashRead(uint16_t size);

// Pin definitions for boot or application reset state
#define	BOOT_STATE_PORT			PORTD
#define	BOOT_STATE_PIN			PIND
#define	BOOT_STATE_PIN_NUMBER	PD7  

#define SUPPORTED_DEVICE_CODE 	0x73

// Signature bytes for ATmega32
#define	SIGNATURE_BYTE_1		0x1E
#define	SIGNATURE_BYTE_2		0x95
#define	SIGNATURE_BYTE_3		0x02

// Function pointer to jump to the applicaion memory section
void (*funcptr)( void ) = 0x0000; 

// Used by chipErase function
#define BOOTSIZE 512

// SRAM Page buffer for flash page pre-load
uint8_t pageBuffer[SPM_PAGESIZE];
#define UART_RX_BUFFER_SIZE SPM_PAGESIZE

// AVR109 uses a global address
uint16_t address;

// From Peter Fluery AVRFreaks Aug 10 2005 - to remove interrupt Vector table
// put -nostartfiles in LDFlags, add the following function
// saves wasted space
void __jumpMain     (void) __attribute__ ((naked)) __attribute__ ((section (".init9")));

void __jumpMain(void)
{   
    asm volatile ( ".set __stack, %0" :: "i" (RAMEND) );
    asm volatile ( "clr __zero_reg__" );        // r1 set to 0
    asm volatile ( "rjmp main");                   // jump to main()
} 

int main(void)
{
	USARTInit();

	// Use bootloader or application code?
    if( !(BOOT_STATE_PIN & (1<<BOOT_STATE_PIN_NUMBER)) ) /* If BOOT_STATE_PIN_NUMBER is low, use bootloader. */
    {
		while(1)
		{
			// wait for esc character (0x1B) and respond with AVRBOOT
			if(receiveByte() == 0x1B)
			{
				// wait for software identifier request
				while( receiveByte() != 'S');
				
				// answer the request
				returnSoftwareIdentifier();
				
				// begin servicing the commands
				AVR109CommandParser();
			}
			else sendByte('?');
		}
	}
	else
	{
		// If BOOT_STATE_PIN_NUMBER is high, don't use bootloader.
		// So... jump to the application 
		funcptr();
	}

	return 0;
}

void AVR109CommandParser()
{
	uint8_t cmd,tempRec;
	uint16_t tempSize;

	while(1)
	{
		cmd = receiveByte();

		switch (cmd)
		{
			case 'P':
				enterProgramMode();
				break;
			case 'a':
				autoIncrementAddress();
				break;
			case 'A':
				setAddress();
				break;
			case 'e':
				chipErase();	
				break;
			case 'L':
				enterProgramMode();
				break;
			case 'T':
				selectDeviceType();
				break;					
			case 's':
				readSignatureBytes();	
				break;			
			case 't':
				returnSupportedDeviceCodes();	
				break;
			case 'S':
				returnSoftwareIdentifier();	
				break;			
			case 'p':
				returnProgrammerType();	
				break;			
			case 'b':
				checkBlockSupport();	
				break;
			case 'B':
			case 'g':
				tempSize = (receiveByte() << 8) | receiveByte();
				// dummy read for type - we only do flash NOT EEPROM
				tempRec = receiveByte(); 
				if(cmd == 'B') blockFlashLoad( tempSize );
				else blockFlashRead( tempSize  );
				break;									
			default:
				if(cmd != 0x1B) sendByte('?');
		}
	}
}	

/*****************************************************
	AVR109 Self Programming Commands
******************************************************/

void enterProgramMode() // 'P'
{
	// what else is it going to do?
	sendByte('\r');
}

void autoIncrementAddress(void) // 'a'
{ 
	// Yes, this code autoincrements
	sendByte('Y');
}

void setAddress(void) // 'A'
{ 
	// Note that flash addresses are in words, not bytes               
    address = receiveByte();   
	address = (address<<8) | receiveByte();
	address = address << 1; // convert word address to byte address

 	sendByte('\r');  // respond okay
}

void chipErase(void) // 'e'
{ 
 	int i;
 	for(i = 0 ; i < (FLASHEND - (BOOTSIZE * 2)); i += SPM_PAGESIZE)
	{
    	boot_page_erase_safe(i);	// Erase the page
    	boot_spm_busy_wait();		// Wait until finished.
	}
   
	sendByte('\r');  // respond okay
}

void selectDeviceType() // 'T'
{
	//dummy read since we only have one device type
	uint8_t dummy;
	dummy = receiveByte();
	sendByte('\r');
}

void readSignatureBytes(void) // 'S'
{ 
    sendByte( SIGNATURE_BYTE_3 );
    sendByte( SIGNATURE_BYTE_2 );
    sendByte( SIGNATURE_BYTE_1 );
}

void returnSupportedDeviceCodes(void) // 't'
{
	sendByte(SUPPORTED_DEVICE_CODE); // Support only this device
	sendByte(0); // list terminator
}

void returnSoftwareIdentifier(void) // 'S'
{ 
    // Software identifier is 'AVRBOOT'
	sendByte('A');
    sendByte('V'); 
    sendByte('R');
    sendByte('B');
    sendByte('O');
    sendByte('O');
    sendByte('T');
}

void returnProgrammerType(void) // 'p'
{      
	// Get programmer type - serial.
    sendByte('S');
}

void checkBlockSupport(void) // 'b'
{ 
    sendByte('Y'); // yes, block load is supported.
    sendByte((SPM_PAGESIZE>>8) & 0xFF); // send MSB first.
    sendByte(SPM_PAGESIZE & 0xFF); // send LSB second.
}

void blockFlashLoad(uint16_t size)
{ 
	uint16_t tempAddress = address;

	uint16_t i,tempWord;

	// store values to be programmed in temporary buffer
	for (i=0; i

To use it for the ATmega644 just change the signature bytes and the device type to match the OP.

The real trick is setting up the AVRStudio configuration files and the AVR Programmer. I'll discuss all that in the soon to arrive tutorial.

Smiley

Last Edited: Mon. Mar 10, 2008 - 08:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

EW wrote:
One of things that I'd like to mention: We could always use more help with documentation in AVR-LibC. Instead of filling up the tutorials forum with fantastic work, it would be nice to include it in the AVR-LibC User Manual where it will get wider distribution.

Eric,

I'm happy to oblige, but to keep the trolls at bay I'm going to PM you with my concerns.

Smiley

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

Smiley... will this compile with the latest winavr AND with the one in use at the time of your book? Might save a newbie a compiler configuration management headache

Imagecraft compiler user

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

smileymicros wrote:
I've reduced the code to the minimum (I think) for AVRProg to work. It now is 912 bytes so it fits in the 512 word boot section. This version if for the ATmega32
There are several items you need to still implement to make this a completely robust bootloader:
  • Support for RAMPZ and 32-bit addresses e.g.
    #if defined(RAMPZ)
    typedef uint32_t address_t;
    #else
    typedef uint16_t address_t;
    #endif
    
       /* calculate flash address from page number and page size */
       /* use 16bit address variable for ATmegas with <= 64K flash */
    	/* set the RAMPZ register for ATmegas > 64K flash */
    	#if defined(RAMPZ)
    		uint32_t address = ((uint32_t)Page) * SPM_PAGESIZE;
    		RAMPZ = ((Page >> 8) != 0);
    	#else
    		uint16_t address = Page * SPM_PAGESIZE;
    	#endif
    
       /* erase page and wait for completion */
    	boot_page_erase(address); 
    	boot_spm_busy_wait();
    	....

  • Turn off the watchdog, if supported by the device e.g.
    	/* reset the watchdog timer */
       wdt_reset();
    	WDTCR |= _BV(WDCE) | _BV(WDE);
    	WDTCR = 0;

  • Generalize the access to registers using standard names and map to device specific registers for each chip e.g.
    /* defines for the various USART registers */
    #if defined(__AVR_ATmega32__)
    
    	/* ATMega with one USART */
    	#define UART_BAUD_RATE_LOW	     UBRRL
    	#define UART_STATUS_REG	        UCSRA
    	#define UART_CONTROL_REG	     UCSRB
    	#define UART_ENABLE_TRANSMITTER TXEN
    	#define UART_ENABLE_RECEIVER	  RXEN
    	#define UART_READY_TO_TRANSMIT  UDRE
    	#define UART_TRANSMIT_COMPLETE  TXC
    	#define UART_RECEIVE_COMPLETE	  RXC
    	#define UART_DATA_REG	        UDR
    
    #elif defined(__AVR_ATmega128__) || defined(__AVR_ATmega1281__) || defined(__AVR_ATmega644__)
    
    	/* ATMega with two USARTs */
    	#define UART_BAUD_RATE_LOW	     UBRR0L
    	#define UART_STATUS_REG	        UCSR0A
    	#define UART_CONTROL_REG	     UCSR0B
    	#define UART_READY_TO_TRANSMIT  UDRE0
    	#define UART_ENABLE_TRANSMITTER TXEN0
    	#define UART_ENABLE_RECEIVER	  RXEN0
    	#define UART_TRANSMIT_COMPLETE  TXC0
    	#define UART_RECEIVE_COMPLETE	  RXC0
    	#define UART_DATA_REG	        UDR0
    
    #else
    	#error "no UART definition for MCU"
    #endif
    
    /* defines for EEPROM write bits */
    #if defined(__AVR_ATmega32__) || defined(__AVR_ATmega128__)
    
    	#define EEPROM_MASTER_WRITE  EEMWE
    	#define EEPROM_WRITE         EEWE
    
    #elif  defined(__AVR_ATmega644__) || defined(__AVR_ATmega1281__)
    
    	#define EEPROM_MASTER_WRITE  EEMPE
    	#define EEPROM_WRITE         EEPE
    
    #else
    	#error "no EEPROM definition for MCU"
    #endif

--Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
// Function pointer to jump to the applicaion memory section
void (*funcptr)( void ) = 0x0000; 

You can make that static, then there will be no ram required for that variable (and will help later if you go to -nodefaultlibs).

It looks like you are using -nostartfiles only, so why not kill 2 birds with 1 stone-

int main (void) __attribute__ ((naked,section (".init9")));
int main(void){
    asm volatile ( "clr __zero_reg__" );
    SP=RAMEND;
...

which puts main at the start, and gets rid of the useless pushing/popping at main start/end (naked).

You can remove one of these(declared twice)

// AVR109 sets the address globally
uint16_t address;

I'm sure we are getting carried away with your bootloader, but one thing I will add that was just mentioned by mikeperks- the watchdog. A simple thing to do would be to put a 'wdr' inside the receive and transmit loop (I think those two places would take care of it). That way it does not matter if the avr has the watchdog fused on, was reset by the watchdog, or whatever- you just keep clearing it in those loops.

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

bobgardner wrote:
Smiley... will this compile with the latest winavr AND with the one in use at the time of your book? Might save a newbie a compiler configuration management headache

No, I've moved up to the latest and greatest. The old stuff is just for the book and I expect those guys to follow the instruction that once finished with the book, then get the latest WinAVR. I consider this bootloader stuff post-book material.

mikeperks wrote:
smileymicros wrote:
I've reduced the code to the minimum (I think) for AVRProg to work. It now is 912 bytes so it fits in the 512 word boot section. This version if for the ATmega32
There are several items you need to still implement to make this a completely robust bootloader:
  • Support for RAMPZ and 32-bit addresses e.g.
    #if defined(RAMPZ)<br />
    
</p> <p>I'm assuming that the rampz stuff is for devices with > 64K memory? So if I restrict my code to 40 pin DIP devices and smaller, then it won't be needed.<br /> Also I'm going for clarity over 'robust' since this is instructional code. It should work okay for the systems described, but once understood the reader should then move on to a better bootloader depending on that individuals definition of better.</p> <p>
mikeperks wrote:
<br />
  • Turn off the watchdog, if supported by the device e.g.[code] /* reset the watchdog timer */<br /> wdt_reset();<br /> WDTCR |= _BV(WDCE) | _BV(WDE);<br /> WDTCR = 0;

  • I'll give this a try, along with curtvm's version below.

    mikeperks wrote:
  • Generalize the access to registers using standard names and map to device specific registers for each chip e.g.
    /* defines for the various USART registers */
  • <br /> These are good suggestions. I'm concerned with adding too much code that isn't bootloader specific since that topic alone is too much for most folks and any distraction might prove fatal to understanding. I guess what I should do is provide a well discussed minimal bootloader, then append a listing for a more robust and generic bootloader based on the one discussed. I'll look into this as I have time.</p> <p>
    curtvm wrote:
    <br /> [code]<br /> // Function pointer to jump to the applicaion memory section<br /> void (*funcptr)( void ) = 0x0000;<br />
    You can make that static, then there will be no ram required for that variable (and will help later if you go to -nodefaultlibs).

    It looks like you are using -nostartfiles only, so why not kill 2 birds with 1 stone-

    <br />
    int main (void) __attribute__ ((naked,section (".init9")));<br />
    int main(void){<br />
        asm volatile ( "clr __zero_reg__" );<br />
        SP=RAMEND;<br />
    ...<br />
    

    which puts main at the start, and gets rid of the useless pushing/popping at main start/end (naked).

    You can remove one of these(declared twice)

    // AVR109 sets the address globally<br />
    uint16_t address;

    I'm sure we are getting carried away with your bootloader, but one thing I will add that was just mentioned by mikeperks- the watchdog. A simple thing to do would be to put a 'wdr' inside the receive and transmit loop (I think those two places would take care of it). That way it does not matter if the avr has the watchdog fused on, was reset by the watchdog, or whatever- you just keep clearing it in those loops.

    Since I've gotten it below 512 words and am unlikely to get it below 256 words, I'm going to leave off any further optimizations that aren't directly relevant to understanding a basic bootloader. I will include a watchdog fix as you and mike have suggested. Thanks for catching the duplicated definition.

    Thanks for all the great input guys,
    Smiley

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

    While adding this-

    void sendByte( uint8_t data )
    {
       // Wait for empty transmit buffer
       do{
            wdt_reset();
       }
       while ( !( UCSRA & (1<<UDRE)) );
       // Put data into buffer, sends the data
       UDR = data;
    }

    I noticed the size decreased quite a bit (relatively speaking). It seems for some reason the latest winavr/gcc is 'inlining' all those sendByte's (take a look at returnSoftwareIdentifier for example). After adding the above wdr, it no longer felt the need to inline it everytime. For double checking, I just simply added a 'nop' instead of the wdr/do/while code, and the same thing happens. Apparently the compiler seems to think inlining is the thing to do, until adding one more instruction tips it over the edge, or something (only gcc 4.x.x doing this, gcc 3.4.6 already makes it a 'called function' everytime). Not sure what's going on there.

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

    I made that suggested change and it made no difference in my compiled code size. Added it to the receiveByte code and it incresed the codesize by 2 bytes. Wonder what's going on?

    Program 756
    data 132

    Smiley

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

    It could be my setup. With the original sendByte, mine wants to inline all those. When anything is added to the function, it decides to not inline it anymore.

    I'm pretty close to your 756 now (726 with main naked, 760 without, and 688 with -mshort-calls).

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

    **WARNING** **WARNING** **WARNING**
    MAKE SURE that the bootsize fuses are set correctly to fit the bootloader code as SPM does NOT work outside the bootloader area. :( :(

    This bit should be on top of every page...ok I'll stand in the corner facing the wall for the next hour :(

    It's all working now.

    However there is one thing I'm interested in finding out.

    The SUPPORTED_DEVICE_CODE 0x73 is for the boot area of the Mega 32, it still works with the Mega 164p and AVRProg is thinking that is talking to the M32 boot.

    Since AVRPROG is no longer updtated,ie no device codes are available for new chips for the past 5 years or so, how is it that it will still program newer chips? Does the device code really matter as the bootloader is doing the real programming? I'm confused.

    I'll try the new, smaller code later on.

    John Samperi

    Ampertronics Pty. Ltd.

    www.ampertronics.com.au

    * Electronic Design * Custom Products * Contract Assembly

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

    I have done a bit more playing with this (I see the tutorial is out) and managed to get the firmware to read the signature bytes directly out of the chip as the M164 family supports this.

    It works with M164 and M324 but I can't get the M644 to work because no matter which chip is selected it always seems to compile at 0x3800. So it will work with the Dragon attached in debug mode but not in standaldone mode with the M644 as the code is not at the right location.

    What are the incantation words to use so it will compile at the correct locations for all 3 chips? (ie the equ of .org :( something to do with the .init9 ?? )

    I have also added an internal pull up to the bootloader select pin as the board I'm using has just a link to ground.

    edit ok I see that the original makefile has the correct .text address but the one generated by Studio stays at:

    LDFLAGS += -Wl,-section-start=.text=0x3800

    regardless of the chip selected, M164, M324 or M644.

    A BUG???

    John Samperi

    Ampertronics Pty. Ltd.

    www.ampertronics.com.au

    * Electronic Design * Custom Products * Contract Assembly

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

    You have to change the AVR Studio memory settings-
    https://www.avrfreaks.net/index.p...

    It will not change by itself. No incantation words will help (I'm assuming words already tried did not help).

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

    I no longer mess with the makefile. This will be discussed in the soon to be released tutorial, but for now you need to set the boot section in two places. First in AVRStudio Project menu Configurations Options, click the Memory Settings Icon and then in the Memory Segments section click the Add button and add Memory Type 'Flash' Name '.text' Address 'Ox7e00'. Then in the AVR Programmer under the Fuses tab select the bootsize for 512 words and check the bootsz checkbox. Okay, that was a mouth full, but in the new tutorial I show pictures so it will be clearer. Anyway no need to mess with the makefile. Yeah!

    Smiley

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

    Quote:
    click the Memory Settings Icon and then in the Memory Segments section click the Add button and add Memory Type 'Flash' Name '.text' Address 'Ox7e00'
    Thanks! I just discovered that and was about to post a " do not worry" message :oops: I still have the M164 settings there, self made bug. :oops:

    I have to go for the larger bootsize for now (using 0x7c00) as the signature file reading bit is pushing the code just over the egde. I think I can trim it down.

    This is just playing around as it is not really useful for older chips without that facility.

    Is really needed? I don't see any actual interrupt service routines. Will this possibly remove the dead code for the interrupt handlers? A lot of wasted flash there.

    f800 <__vectors>:
        f800:	0c 94 3e 7c 	jmp	0xf87c	; 0xf87c <__ctors_end>
        f804:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f808:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f80c:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f810:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f814:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f818:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f81c:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f820:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f824:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f828:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f82c:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f830:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f834:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f838:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f83c:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f840:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f844:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f848:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f84c:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f850:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f854:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f858:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f85c:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f860:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f864:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f868:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f86c:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f870:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f874:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>
        f878:	0c 94 5d 7c 	jmp	0xf8ba	; 0xf8ba <__bad_interrupt>

    John Samperi

    Ampertronics Pty. Ltd.

    www.ampertronics.com.au

    * Electronic Design * Custom Products * Contract Assembly

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

    I don't think removing the interrupt header will do anything, but there is something that can be done to keep from wasting all that space putting the interrupt handler vectors in the boot section, problem is that I don't remember what. I'll make a not to make sure to cover this in the final tutorial which I'll work on tomorrow.

    Smiley

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

    linker option
    -nostartfiles

    but then you need to take care of a few details yourself.
    https://www.avrfreaks.net/index.p...

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

    Quote:
    I don't think removing the interrupt header will do anything,
    True, just tried it.

    Apart from that it's all happy now for all 3 chips once I get the .text thingy adjusted for each type of chip.

    For anyone that may want to try the reading of the signature bytes here are the changes. Pretty ugly code as I don't really understand how to use register and bit names with inline asm. Also 12 bytes could be saved if the push and pop are not needed (not sure about that).

    It pushes the size of the code to 1042 unfortunately...now if we can get rid of the dead int pointers... :) and of course it only works for the M164 family and M640 AFAIK and I guess other new chips

    1st change:
    // Signature bytes for ATmega32 
    //#define   SIGNATURE_BYTE_1      0x1E 
    //#define   SIGNATURE_BYTE_2      0x95 
    //#define   SIGNATURE_BYTE_3      0x02 
    
    volatile uint8_t SIGNATURE_BYTE_1, SIGNATURE_BYTE_2, SIGNATURE_BYTE_3;
    ...
    ...
    
    the code itself
    
    void readSignatureBytes(void) // 's' 
    { 
    asm volatile(
    	"push	r24\n\t"
    	"push	r30\n\t"
    	"push	r31\n\t"
    	"clr	r30\n\t"		//point z to 0x0000 1st sig byte
    	"clr	r31\n\t"
    	"rcall	read_sig\n\t"
    	"sts	SIGNATURE_BYTE_1,r24\n\t"
    
    	"adiw	r30,2\n\t"		//point z to 0x0002 2nd sig byte
    	"rcall	read_sig\n\t"
    	"sts	SIGNATURE_BYTE_2,r24\n\t"
    
    	"adiw	r30,2\n\t"		//point z to 0x0004 3rd sig byte
    	"rcall	read_sig\n\t"
    	"sts	SIGNATURE_BYTE_3,r24\n\t"
    	"rjmp	read_sig_exit\n\t"
    
    	"read_sig:\n\t"
    	"ldi	r24,0x21\n\t"
    	"out	0x37,r24\n\t"
    	"lpm	r24,z\n\t"
    	"ret\n\t"
    
    	"read_sig_exit:\n\t"
    	"pop	r31\n\t"
    	"pop	r30\n\t"
    	"pop	r24\n\t"
    	::);
    
    	sendByte( SIGNATURE_BYTE_3 ); 
        sendByte( SIGNATURE_BYTE_2 ); 
        sendByte( SIGNATURE_BYTE_1 ); 
    } 

    This is frightening I even defined the 3 variables as volatile :o

    John Samperi

    Ampertronics Pty. Ltd.

    www.ampertronics.com.au

    * Electronic Design * Custom Products * Contract Assembly

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

    Quote:
    linker option
    -nostartfiles
    I can see that now in the source file. Any hints on how to do this with the Studio config options? :)

    It took out the origin address and the code starts at 0 with the vectors still there :(

    John Samperi

    Ampertronics Pty. Ltd.

    www.ampertronics.com.au

    * Electronic Design * Custom Products * Contract Assembly

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

    Quote:
    This is frightening
    what's frightening is all that assembly code that could be replaced with a few C lines :)

    Project->Configuration Options->Custom Options->
    select [Linker options],
    type in -nostartfiles,
    click add

    add this to your code-

    int main (void) __attribute__ ((naked,section (".init9")));
    void my_crt (void) __attribute__ ((naked,section (".init0")));
    void my_crt (void){
        asm volatile ( "clr __zero_reg__" );
        SREG=0; //newer crt1.S does this
        SP=RAMEND;
    }

    that will get r1 cleared (before its used to clear bss data), clear sreg (I assume to cli), and set the stack. The normal data/bss thing still happens right after that (you need -nodefaultlibs to get rid of that). Then main is positioned at .init9 so it is the first thing to run after the startup stuff.

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

    Quote:
    all that assembly code that could be replaced with a few C lines
    Go for it :)

    I'll wait for the new and improved source tomorrow???

    by the way since I put the -nostartfile the code has gone back to 0x0000 no matter what, even deleted all generated files including the makefile.

    Now I still have

    LDFLAGS += -Wl,-section-start=.txt=0xf800

    but it generates

    00000000 <__vectors>:
       0:	0c 94 3e 00 	jmp	0x7c	; 0x7c <__ctors_end>
       4:	0c 94 5d 00 	jmp	0xba	; 0xba <__bad_interrupt>
       8:	0c 94 5d 00 	jmp	0xba	; 0xba <__bad_interrupt>
    

    no matter what I do and yes I have deleted the -nostartfile from the options.

    John Samperi

    Ampertronics Pty. Ltd.

    www.ampertronics.com.au

    * Electronic Design * Custom Products * Contract Assembly

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

    curtvm wrote:
    Quote:
    This is frightening
    what's frightening is all that assembly code that could be replaced with a few C lines :)

    Project->Configuration Options->Custom Options->
    select [Linker options],
    type in -nostartfiles,
    click add

    add this to your code-

    int main (void) __attribute__ ((naked,section (".init9")));
    void my_crt (void) __attribute__ ((naked,section (".init0")));
    void my_crt (void){
        asm volatile ( "clr __zero_reg__" );
        SREG=0; //newer crt1.S does this
        SP=RAMEND;
    }

    that will get r1 cleared (before its used to clear bss data), clear sreg (I assume to cli), and set the stack. The normal data/bss thing still happens right after that (you need -nodefaultlibs to get rid of that). Then main is positioned at .init9 so it is the first thing to run after the startup stuff.

    I'm using the following from Peter Fluery:

    // From Peter Fluery AVRFreaks Aug 10 2005 - to remove interrupt Vector table
    void __jumpMain     (void) __attribute__ ((naked)) __attribute__ ((section (".init9")));
    
    void __jumpMain(void)
    {   
        asm volatile ( ".set __stack, %0" :: "i" (RAMEND) );
        asm volatile ( "clr __zero_reg__" );        // r1 set to 0
        asm volatile ( "rjmp main");                // jump to main()
    } 
    

    Frankly, I'm not sure what difference either makes. I'll do a bit more research on it.

    Smiley

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

    There's many ways to do the same thing, but in the Fluery example, r1 is not cleared until .init9, which comes AFTER the bss clearing which uses r1 (since you are still using the libgcc.a startup code). So his example will leave bss data in the state of whatever r1 happens to be. Maybe your code happens to not need any initialized bss data.

    In my example, .init0 runs first- which clears r1,sets stack, (and clears sreg as the newer crt1.S does), then it continues to .init4 which sets up the data and clears bss, then it continues to .init9 (before .text). Since 'main' was told to start at .init9, it will be the first to run (no other functions can sneak in, since other functions start at .text).

    .vectors (no longer used) (-nostartfiles)
    .init0 -new startup stuff
    .init4 -normal data/bss stuff
    .init9 -main starts here
    .text -the rest of the app

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

    Quote:
    I'll wait for the new and improved source tomorrow???
    simple, inefficient version-
    #if defined(SIGRD)
    void readSignatureBytes(void){ // 's'
        sendByte(boot_signature_byte_get(4));
        sendByte(boot_signature_byte_get(2));
        sendByte(boot_signature_byte_get(0));
    }
    #endif

    a little better

    #if defined(SIGRD)
    void readSignatureBytes(void){ // 's'
        uint8_t addr=4;
        while(addr!=254){
            sendByte(boot_signature_byte_get(addr));
            addr-=2;
        };
    }
    #endif

    non-library version

    #if defined(SIGRD)
    void readSignatureBytes(void){ // 's'
        register volatile uint16_t Z asm("r30");
        Z=4;
        while(1){
            SPMCSR=(1<<SIGRD)|(1<<SPMEN);
            asm volatile("lpm r24,z");
            asm volatile("call sendByte");
            if(!Z) break;
            Z-=2;
        };
    }
    #endif

    Pages