I was looking on 'how to use spm' and found only two tutorials. I am confused with the code
#define F_CPU 8000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/boot.h>
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <stdio.h>
int main ( void )
{
uint16_t i;
uint8_t A [ 300 ];
uint8_t sreg;
uint32_t page = 256;
unsigned char *buf = A;
//-------------------------------------------------------------------------------
DDRD |= 0x80;
PORTD |= 0x7F; //led on. bootloader ok.
_delay_ms ( 2000 );
PORTD |= 0x80; //turn off led now. We hope the application will turn on the LED again
_delay_ms ( 2000 );
//--------------------------------------------------------------------------------
// storing the bytes from ith location of flash memory to ith variable of array A//
for ( i = 0; i < 300; i ++ )
A [ i ] = pgm_read_byte ( i );
// storing the bytes from ith location of flash memory to ith variable of array A//
//================================= SPM ==========================================//
sreg = SREG; // store the current interrupt status in sreg
cli(); // clear interrupts
eeprom_busy_wait (); // wait till the eeprom is free.
boot_page_erase (page); // erase the page in the flash which we are about to write into
boot_spm_busy_wait (); // Wait until the memory is erased.
//---- fill the bytes of the page into temperory page buffer before wriying into flash ----//
for (i=0; i<SPM_PAGESIZE; i+=2)
{
//convert the bytes to little-endian word//
uint16_t w = *buf++;
w += (*buf++) << 8;
//convert the bytes to little-endian word//
boot_page_fill (page + i, w); // fill the temperory page buffer byte by byte
}
//---- fill the bytes of the page into temperory page buffer before wriying into flash ----//
//--------------------------------------------------------------------//
boot_page_write (page); // Store buffer in flash page.
//--------------------------------------------------------------------//
boot_spm_busy_wait(); // Wait until the memory is written.
boot_rww_enable (); // Reenable RWW-section again
SREG = sreg; // Re-enable interrupts
//================================= SPM ==========================================//
asm ( "jmp 0x0100" ); // jump to application programmed at 0x0100
}
this is the code from the tutorial found here. I cant understand the following
in the function pgm_read_byte() the address required is byte address (as per my understanding) but its not converted and also it is a 16 bit variable. I found out the function returns a byte of the word in one iteration but what about the second byte ?.
In the for loop that is used to write to the temporary page buffer, why is it converted to little endian,becuase when we read the bytes from the flash its stored in the correct sequence right ?. Also have the same confusion regarding the address conversion
In the code the address used is 256 if its the byte address then how it resembles 0x0100 of the flash address. Please someone should give me help and I know I am wrong somewhere but please correct me and give me good understanding of the above code
It's just combining two bytes because flash is organized as 16 bit data. The fact that it is little endian is just because the processor is little endian. The code is not "converting" big to little endian or vice versa. It's just converting bytes to 16 bit words. The authors are just reminding you what order they are doing it in.
To your other question, 256 and 0x0100 represent the same value. The first is expressed in base 10 and the second is expressed in base 16. They have the same binary representation in memory.
uint8_t A[300];
for (i = 0; i < 300; i ++)
A[i] = pgm_read_byte(i);
in the function pgm_read_byte() the address required is byte address (as per my understanding) but its not converted and also it is a 16 bit variable. I found out the function returns a byte of the word in one iteration but what about the second byte ?
The loop is copying the first 300 bytes of FLASH/PROGMEM into the byte array in RAM named 'A'. It is reading and writing bytes, not words.
m_k_akash:
In the for loop that is used to write to the temporary page buffer, why is it converted to little endian,because when we read the bytes from the flash its stored in the correct sequence right? Also have the same confusion regarding the address conversion.
The "Load from Program Memory" (LPM) instruction uses a byte address. If the low order bit of the address is 0 it reads the low byte of the memory word. If the low order bit of the address is 1 it reads the high byte of the memory word. The "Set Program Memory" (SPM) instruction writes a word and the low order bit of the address should be 0. To get the bytes to read out in order, the words are little-endian.
m_k_akash:
In the code the address used is 256 if its the byte address then how it resembles 0x0100 of the flash address.
256 decimal and 0x100 hexadecimal are the same number.
I know that 256 is 0X0100 but some where I read that to convert it to byte address it should be multiplied by 2(i.e 0x0200) for the low byte and (x2 +1) for high byte (0x0201) thats why I was confused, is this right ?. so which page does it represent ? (by my calculation I found it to be 4) and can a page be any 128 byte or does it have sequential order ?, Also is there a problem if I use a 8 bit variable instead of 16 for the address?
m_k_akash:
I know that 256 is 0X0100 but some where I read that to convert it to byte address it should be multiplied by 2(i.e 0x0200) for the low byte and (x2 +1) for high byte (0x0201) thats why I was confused, is this right ?. so which page does it represent ? (by my calculation I found it to be 4) and can a page be any 128 byte or does it have sequential order ?, Also is there a problem if I use a 8 bit variable instead of 16 for the address?
The address for SPM and LPM is in the 16-bit 'Z' register. The top 15 bits represents the word address. The bottom bit for LPM is 0 for the low byte and 1 for the high byte. The bottom bit for SPM should be 0. The upshot is that the 16 bits of 'Z' look just like a byte address.
You should read about programming FLASH in the datasheet. The section is named: "Boot Loader Support – Read-While-Write Self-Programming, ATmega88PA, ATmega168PA and ATmega328P"
m_k_akash:
so is this wrong ? :
0PPPPPPPPWWWWWWB - 16 bits
0 - Not used
P - Page address
W - word address
B - Low Byte, High byte
Looks fine to me. I think the hardware will just ignore the parts of the address that don't make sense in context. If you use the address 0x1234 and you are doing a Page operation, the hardware will treat it like 0x1200 and will operate on the 128 locations 0x1200 through 0x127F. I'm guessing if you are writing into the page buffer the hardware will use location 0x0034.
So what is meant by the word address to byte address conversion factor (x2) ? This made me crazy and made me ask in the forum. The not used bit that I mentioned, is it really used for something or not ? And for the page addressing, I think I should give a try with some random address and read the flash to get the result. Where is exactly the page buffer found ? I read it's not part of SRAM, and why are we addressing the page buffer with the same address where we have to write in the flash ? Because it has different physical address right
m_k_akash:
So what is meant by the word address to byte address conversion factor (x2) ? This made me crazy and made me ask in the forum.
You should ask the person who said it, since nobody here is giving you that explanation. But it obviously expresses the fact that each word is composed of two bytes. A word address points to every even byte. Was the data sheet helpful?
m_k_akash:
Where is exactly the page buffer found? I read it's not part of SRAM, and why are we addressing the page buffer with the same address where we have to write in the flash ? Because it has different physical address right
Have you read about programming FLASH in the ATmega328P datasheet? The SPM instruction has its own control register to put the SMP instructions into one of several modes: Erase Program Memory Page, Load Page Buffer, and Write Page Buffer to Program Memory.
In "Erase Program Memory Page" mode the bottom 7 bits of register Z are ignored and the the top 9 bits are used to select which page of Program Memory is erased.
In "Load Page Buffer" mode the top 9 bits of register Z are ignored and the bottom bit 'should' be zero. Registers R0 (low byte) and R1 (high byte) are written into the buffer word addressed by register Z bits 1 through 6.
In 'Write Page Buffer to Program Memory' mode the bottom 7 bits of register Z are ignored and the the top 9 bits are used to select which page of Program Memory the buffer gets written to.
m_k_akash:
so is this wrong ? :
0PPPPPPPPWWWWWWB - 16 bits
0 - Not used
P - addressing the pages
W - addressing the words
B - Low Byte, High byte
According to this, the last address of the flash should be 0x7FFF right ? but it is 0x3FFF
I think its because 0x3FFF multiplied by 2 and add 1 gives 0x7FFF. So the address 256 which is 0x0100, shouldnt it be multiplied by 2 ? and use 0x0200 in the functions ?
m_k_akash:
According to this, the last address of the flash should be 0x7FFF right ? but it is 0x3FFF
I think its because 0x3FFF multiplied by 2 and add 1 gives 0x7FFF. So the address 256 which is 0x0100, shouldnt it be multiplied by 2 ? and use 0x0200 in the functions ?
Note that the tutorial you were following was for an ATmega16 processor. Different AVR processors have different amounts of PROGMEM and different page sizes. See the datasheet for the processor you want to program.
I know it was for ATmega16, I simply compiled it for ATmega328p and its working, checked the datasheet still couldnt understand why it works exactly as mentioned for ATmega16, and the main problem there isnt any other tutorial, Its been days working on it, trying to figure out it works without converting the word address to byte address, but function is supposed to work on byte address. Reading the 300 bytes is ok but writing section is confusing.
look at the parts(excluding the unwanted bytes) of the flash memory that I have read. The bootloader successfully copied the bytes from the address 0x0000 to 0x0100 cant figure out how it works. how do you guys read from the flash in c ?
if I burn the bootloader alone, the code written in the bootloader gets continuously executed like a loop untill any thing is programmed to the address 0x0000. if any thing is programmed to address 0x0000 the code in the BLS gets executed only once, why this happens ?
m_k_akash:
if I burn the bootloader alone, the code written in the bootloader gets continuously executed like a loop untill any thing is programmed to the address 0x0000. if any thing is programmed to address 0x0000 the code in the BLS gets executed only once, why this happens ?
My guess is that in the unprogrammed state "0xFFFF" the contents of address 0 (the place the bootloader jumps to when it doesn't get a command from the serial port) is an invalid opcode. I would expect that to cause a reset and, since the BOOTRST fuse is set, that takes control back to the first address in the bootloader.
My guess is that in the unprogrammed state "0xFFFF" the contents of address 0 (the place the bootloader jumps to when it doesn't get a command from the serial port) is an invalid opcode. I would expect that to cause a reset
Nope. 0xFFFF disassembles as "NOP" (IIRC, it actually some instruction that doesn't do anything useful.) Typical behavior if you start running a chip containing a bootloader at high memory and 0xFFFF everywhere else is that it will execute those ~15000 NOP instructions linearly, until it gets to the meaningful bootloader, at which point it will start dong the useful bootloader things.
Also, illegal instructions on AVR don't cause a reset. They're just ... undefined, and do perhaps-undefined things...
There's an added complication with "word addressing" vs "byte addressing", in that the Atmel/Microchip documentation consistently addresses instructions as words - to write "instruction word 100" in flash, you have to write bytes 200 and 201. This usually results in confusion WRT setting the bootloader start addresses, because the documentation will say "with fuses xx, the boot section is 256 words long and starts at address 0x3F00", when the switch you have to give avr-gcc will be "--section-start=.text=0x7E00"
westfw:
There's an added complication with "word addressing" vs "byte addressing", in that the Atmel/Microchip documentation consistently addresses instructions as words - to write "instruction word 100" in flash, you have to write bytes 200 and 201. This usually results in confusion WRT setting the bootloader start addresses, because the documentation will say "with fuses xx, the boot section is 256 words long and starts at address 0x3F00", when the switch you have to give avr-gcc will be "--section-start=.text=0x7E00"
Yes, I had a hard time finding the problems related to word address and byte address, and for building it I used atmel studio 7. in which the start address should be mentioned in word address (ie .text=0x3F00). I was puzzled with the tutorial as the guy didnt mention whether it was a byte address or word address but when I saw this diagram
As I saw the end address as 0x1FFF which is word address, I thought the guy was talking about word address in the whole tutorial. And was confused why the guy didnt convert it to byte address. Now I believe the diagram in the tutorial is wrong.
The following list file shows that the numerical value for the "nop" instruction of ATmega328P is "0000".
Yep. Thus my careful wording "disassembles as NOP". Here's some AS7 disassembly output:
0000003B 1d.9a SBI 0x03,5 Set bit in I/O register
ldi rcounter, counter ; Reset Counter to 25
0000003C 19.e1 LDI R17,0x19 Load immediate
rjmp Loop ; Loop
0000003D fb.cf RJMP PC-0x0004 Relative jump
--- No source file -------------------------------------------------------------
0000003E ff.ff NOP Undefined
0000003F ff.ff NOP Undefined
The closest to 0xFFFF seems to be "SBRS r31, 7", which assembles to 0xFFF7 - one bit missing.
That is "Skip if Bit 7 of R31 is Set." It would either skip the next instruction or not, depending on contents of R31 (assuming that that's how it's decoded. I suspect it's actually a real NOP, and always will be, even if it's not the "preferred nop." (I used to use a computer that had a BUNCH of different NOPs, and good assembly programmers were expected to know which were the fastest. ("Test Right No Bits and do nothing" on the most recent CPU, and "don't Jump and Flag CLear nothing" on the slightly older version. but also "jump never", "skip never", etc. Older ARMs use "mov r8, r8")