More self-programming strangeness - attiny814

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

Been scouring documents and post and so far have some code running, which I will post at the end. I am having a strange problem though.

Based on the docs, the flash pages are 256 bytes, addressed as words. So I am making one assumption off the bat (that seems to be correct so far): if I have 8k of flash, since it's word-addressed, the address range is 0x8000 to 0x9000 (instead of 0xA000).

In my project, I want to store 8 separate 128-byte chunks of data into flash (some settings that a user can store). So I would assume, since you have to erase a full page at a time (and assuming I want to keep the 128-byte chunks of data contiguous in memory), I would do something along the lines of reading the full page (256 bytes) into the page buffer (or reading half of it into the page buffer) and then update the other half, either the first 128 bytes or second, depending on which chunk (0-7) I was updating.

On to the actual weirdness (for me at least):When I tried the above I was only getting 0xffff back from memory for each address. After playing around with it a bunch, I ran into the only way I could get it to work. I treated the memory as if it were only 64 bytes per page. What I mean by this is: if I tried to write 128 bytes into memory, I would get the first 64 bytes back as 0xff and the next 64 bytes with the first 64 byte of the actual data, as if the address I was writing to was shifted up by 64 bytes (32 addresses). When I write only 64 bytes at a time, this does not happen. What I write is what I get, for that chunk of 64 bytes. The next unexpected thing: I figured on my first try that, since I should be erasing 256-byte pages, if I wrote 64 bytes, did a Page_Erace_Write command, and then wrote another 64 bytes (32 address up), the first 64 bytes would be all set to 0xff. That is not what happened though. I got all 128 bytes of data back as written. So it the page size (for erasing and writing) wrong? Or am I doing something strange with the addresses that just happens to work? Or some other thing I can't think of? (I was also playing with that PAGESIZE up at top. no change)

 

P.S. Fuzes: BOOTEND=0x01, APPEND=0x04

#define PAGESIZE 256
#include <avr/io.h>
#define FLAST_START 0x8000 //Flash mem starting address
#define SLOT_START (FLAST_START + 0x1100) //Starting address for slots in APPDATA


volatile uint8_t *data_ptr = (uint8_t *)SLOT_START;
volatile uint8_t data;
uint8_t board[128];

void read_slot(uint8_t slot, uint8_t * destBoard)
{
    uint16_t * address = SLOT_START + (slot * 128);

    for (uint8_t i = 0; i < 64; i++)
    {
        destBoard[(i<<1)] = address[i];
        destBoard[(i<<1) + 1] = address[i]>>8;
    }
}

void write_slot(uint8_t slot, uint8_t * sourceBoard)
{

    uint16_t temp;
    //Slots are 128 bytes long
    uint16_t *pageStart = SLOT_START + (slot * 128);
    uint16_t *slotStart = pageStart;
    uint8_t i;
    
    _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEBUFCLR_gc);
    while((NVMCTRL.STATUS & (NVMCTRL_EEBUSY_bm |  NVMCTRL_FBUSY_bm)))

    

    
    
    for (i = 0; i < 32; i++)
    {

        //Update the page buff with the source board data
        temp = sourceBoard[(i<<1)] | sourceBoard[(i<<1) + 1] << 8;
        //using temp for debugging
        slotStart[i] = temp;

    }
    
    _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc);
    while((NVMCTRL.STATUS & (NVMCTRL_EEBUSY_bm |  NVMCTRL_FBUSY_bm)))
    
    for (i = 32; i < 64; i++)
    {

        //Update the page buff with the source board data
        temp = sourceBoard[(i<<1)] | sourceBoard[(i<<1) + 1] << 8;
        //using temp for debugging
        slotStart[i] = temp;

    }
    
    _PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc);
    while((NVMCTRL.STATUS & (NVMCTRL_EEBUSY_bm |  NVMCTRL_FBUSY_bm)))    

}

int main(void)
{
    PORTB.DIRSET = 0x03;

    read_slot(0, board);
    PORTB.OUTSET = 0x03 & board[1];

    for (uint8_t i = 0; i < 128; i++)
    {
        board[i] = i;
    }

    write_slot(0, board);

    
    for (uint8_t i = 0; i < 128; i++)
    {
        board[i] = 127 - i;
    }
    
    write_slot(1, board);
    
    read_slot(0, board); 
    
    read_slot(1, board);

   while(1);
}

 

 

This topic has a solution.
Last Edited: Wed. Jul 21, 2021 - 05:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

if I have 8k of flash, since it's word-addressed,

//Update the page buff with the source board data
        temp = sourceBoard[(i<<1)] | sourceBoard[(i<<1) + 1] << 8;
        //using temp for debugging
        slotStart[i] = temp;

It's NOT "word-addressed", if you're writing with ST instructions.  Your whole "put things together into 16bit quantities" is unnecessary.

(I don't think it should actually cause problems, because the compiler will do 8bit stores anyway.  But it makes things unnecessarily complex.)

See https://github.com/Optiboot/opti...

 

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

Hexum064 wrote:
So I am making one assumption off the bat
A dangerous strategy. As Bill says the addressing is byte not word.

 

While I don't think AVR-LibC's boot.h has grown support for the Xmega derivative chips there may still be something to be learned simply by reading the "manual":

 

https://www.nongnu.org/avr-libc/...

 

Specifically the "API usage example" there. Now clearly that is code for tiny/mega AVR not Xmega like the "tiny"814 but I think the concept of "buffer stuffing" shown in this code will still apply -  note how the for() loop is doing i+2 each time each time two bytes are stuffed into the page buffer.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

So it the page size (for erasing and writing) wrong? 

It is listed in the datasheet, and also the mcu header, and has nothing to do with the division if the sections, which are always in 256 byte blocks.

 

  #define MAPPED_PROGMEM_PAGE_SIZE (64U)

 

 

Fuzes: BOOTEND=0x01, APPEND=0x04

boot section = 0-255 bytes, 1 block

appcode section = 256-1023 bytes, 3 blocks

appdata section = 1024-end

 

You can only write flash from boot section to any app section, and from appcode to appdata. If not using the boot section for bootloader purposes, then you can skip the appcode section and just have the boot section and appdata section.

 

BOOTEND = 0x04, APPEND = 0x04

boot section = 0-1023 bytes

appdata section = 1024-end

Last Edited: Wed. Jul 21, 2021 - 11:10 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I want to store 8 separate 128-byte chunks of data into flash (some settings that a user can store)

If you don't need so much, you can use the EEPROM---it is great for storing adjustments, such as user options & cal values.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

curtvm wrote:

It is listed in the datasheet, and also the mcu header, and has nothing to do with the division if the sections, which are always in 256 byte blocks.

 

  #define MAPPED_PROGMEM_PAGE_SIZE (64U)

 

I just found  MAPPED_PROGMEM_PAGE_SIZE  in the header but I can't find it in the datasheet for the MCU (2020 version) or the AVR106 or AVR109 docs.

What datasheet did you find it in?

Last Edited: Wed. Jul 21, 2021 - 04:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Bill and clawson. Maybe it was just getting late and I wasn't thinking straight but when I had tried to write individual bytes to the memory it seemed like it was only writing every other byte. I didn't want to do the 16 bit writes but that seemed to work at the time.

I added single byte version of my read and write methods and it seems to be good so I will stick with that strategy.


void read_slot_8(uint8_t slot, uint8_t * destBoard)
{
	uint8_t * address = SLOT_START + (slot * 128);

	for (uint8_t i = 0; i < 64; i++)
	{
		destBoard[i] = address[i];
	}
	
}

void write_slot_8(uint8_t slot, uint8_t * sourceBoard)
{

	uint8_t temp;
	//Slots are 128 bytes long. Extra pointer originally for longer pages.
	uint8_t *pageStart = SLOT_START + (slot * 128);
	uint8_t *slotStart = pageStart;
	uint8_t i;
	
	_PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEBUFCLR_gc);
	while((NVMCTRL.STATUS & (NVMCTRL_EEBUSY_bm |  NVMCTRL_FBUSY_bm)));

	for (i = 0; i < 64; i++)
	{

		//Update the page buff with the source board data
		temp = sourceBoard[i];
		//using temp for debugging
		slotStart[i] = temp;

	}
	
	_PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc);
	while((NVMCTRL.STATUS & (NVMCTRL_EEBUSY_bm |  NVMCTRL_FBUSY_bm)));
	
	for (i = 64; i < 128; i++)
	{

		//Update the page buff with the source board data
		temp = sourceBoard[i];
		//using temp for debugging
		slotStart[i] = temp;

	}
	
	_PROTECTED_WRITE_SPM(NVMCTRL.CTRLA, NVMCTRL_CMD_PAGEERASEWRITE_gc);
	while((NVMCTRL.STATUS & (NVMCTRL_EEBUSY_bm |  NVMCTRL_FBUSY_bm)));

}

 

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

clawson wrote:
Now clearly that is code for tiny/mega AVR not Xmega like the "tiny"814

The atting814 is considered an xmega-like core instead of a tiny-like core?

 

Thanks for this link!

 

 

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

What datasheet did you find it in?

Its in the Memories section of your tiny814 datasheet.

 

 

 

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

curtvm wrote:

What datasheet did you find it in?

Its in the Memories section of your tiny814 datasheet.

 

 

 

 

Aah. I see that now. I was reading almost exclusively from the NVM Controller section.

How did you know about  MAPPED_PROGMEM_PAGE_SIZE? Just looking through the header?

 

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


Hexum064 wrote:
The atting814 is considered an xmega-like core instead of a tiny-like core?
err.. yes.

 

For whatever reason the Xmega range from Atmel never really seemed to "take off" in the way traditional Tiny/Mega did so when Microchip took over they started making chips like AVR-0, AVR-1, AVR-Dx and so on which basically reused the more complex peripheral sets from Xmega but for reasons no one earth (except for their marketing manager?) may ever understand they chose to call they "tiny" and "mega" again to confuse the living daylights out of everyone who comes across them. 

 

The clues is in the datasheet:

 

in days gone by some people (I was one) called these things "tinyX" to empahsise that they were chips full of Xmega not tiny/mega peripherals. 

 

If you know tiny/mega it does not really help with these as it's the same steep leanring curve as moving from tiny/mega to Xmega.

 

The NVM controller is just one of those added bits of complexity. In the older chips you could do all the SPM preparation stuff with the SPM opcode alone (and Z saying what kind of operation it really was).



Sadly this is where things like < avr/boot.h > get "left behind". It does SPM support the tiny/mega way but is not a whole lot of help for an Xmega derivative (except that (the point of my first post) the page buffering is a kind of similar approach).

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

Ug. This has been the source of some confusion for me lately.

Thanks again, clawson. You're a huge source of knowledge and help as usual!