shumatech/BOSSA

ATSAM4S Flash Sector Boundary Write Error

haata opened this issue · 1 comments

haata commented

tl;dr I figured out what the problem was will writing up this report, ATSAM4S doesn't support auto erase as it's been written into BOSSA beyond the 16 kB boundary (this is a limitation of ATSAM4S). Two possible solutions: a) Add an error message describing the issue and how to work around it for ATSAM4S b) Add simulated auto-erase by erasing pages manually when writing (and auto erase is enabled).

I'm not entirely certain what's going on here, but I'm documenting this here because this problem has baffled me for well over 2 years now (and now I have a reliable work around!).

Problem:
When writing over 16384 bytes (32 pages) to any ATSAM4S chip (I've worked with 2A up to 8C and many in between) using bossac or bossash, flashing will fail 100% of the time (Linux) on the 33rd page.

sudo bin/bossac -u -w bootloader.bin -v
Unlock all regions
Write 17448 bytes to flash (35 pages)
[============================  ] 94% (33/35 pages)
Flash command failed

Attempting at a higher offset, fails at the same place.

sudo bin/bossac -u -w bootloader.bin -v -o 8192
[sudo] password for hyatt:
Unlock all regions
Write 17448 bytes to flash (35 pages)
[==============                ] 48% (17/35 pages)
Flash command failed

When flashing with the GUI bossa program, it works 100% of the time. And I've been perplexed for a long time on why that's the case...

ATSAM4S flash is laid out into 3 different sector sizes.

  • 8 Kbyte small sectors (2x)
  • 48 Kbyte medium sectors (1x)
  • 64 Kbyte large sectors (1..15x depending on the chip)

The 8 Kbyte sectors have special considerations for erase commands as compared to the 48 and 64 Kbyte sectors (I've had to deal with these inside my own bootloader code).

See Figure 8-3 (Page 42) for a visual: http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11100-32-bit%20Cortex-M4-Microcontroller-SAM4S_Datasheet.pdf

It took me years on and off looking at this problem (my bootloader grew again to over 16 kB recently so I've had to solve it this time), but I believe I understand why bossa works and bossac doesn't. bossa always does an erase all before write (there isn't even an erase button in the bossa gui app).
With bossac I have been excluding the --erase option to speed up manufacturing (it's ever so slightly faster when working with 16 kB or less; my own bootloader handles flashing the rest).

sudo bin/bossac -u -e -w bootloader.bin -v
Unlock all regions
Erase flash

Done in 1.085 seconds
Write 17448 bytes to flash (35 pages)
[==============================] 100% (35/35 pages)
Done in 0.230 seconds
Verify 17448 bytes of flash
[==============================] 100% (35/35 pages)
Verify successful
Done in 0.185 seconds

I'm not exactly sure what's going on with SAM-BA and BOSSA, though I'm pretty certain that the issue is with the erase in some way.
Another interesting note, is that it's not sufficient to erase in a previous command, you must do it in one shot.

sudo bin/bossac -e
Erase flash

Done in 1.089 seconds
sudo bin/bossac -w bootloader.bin
Write 17448 bytes to flash (35 pages)
[============================  ] 94% (33/35 pages)
Flash command failed

...
...
...

Aaaaand, while writing this up I discovered that disabling _eraseAuto fixes the erase, then write problem.

diff --git a/src/EefcFlash.cpp b/src/EefcFlash.cpp
index 4f61401..e535d1d 100644
--- a/src/EefcFlash.cpp
+++ b/src/EefcFlash.cpp
@@ -73,7 +73,7 @@ EefcFlash::EefcFlash(Samba& samba,
                      uint32_t regs,
                      bool canBrownout)
     : Flash(samba, name, addr, pages, size, planes, lockRegions, user, stack),
-      _regs(regs), _canBrownout(canBrownout), _eraseAuto(true)
+      _regs(regs), _canBrownout(canBrownout), _eraseAuto(false)
 {
     assert(planes == 1 || planes == 2);
     assert(pages <= 4096);

This isn't a fix, because it will prevent subsequent flashes from working (verify will fail because you have to erase first).

And, this solves the mystery of why adding --erase makes the flashing work over 16 kB.

Flasher.cpp

void
Flasher::erase(uint32_t foffset)
{
    _observer.onStatus("Erase flash\n");
    _flash->eraseAll(foffset);
    _flash->eraseAuto(false);
}

So, looking at EefcFlash.cpp it seems that autoErase corresponds to EEFC_FCMD_EWP.

void
EefcFlash::writePage(uint32_t page)
{
    if (page >= _pages)
        throw FlashPageError();

    _wordCopy.setDstAddr(_addr + page * _size);
    _wordCopy.setSrcAddr(_onBufferA ? _pageBufferA : _pageBufferB);
    _onBufferA = !_onBufferA;
    waitFSR();
    _wordCopy.runv();
    if (_planes == 2 && page >= _pages / 2)
        writeFCR1(_eraseAuto ? EEFC_FCMD_EWP : EEFC_FCMD_WP, page - _pages / 2);
    else
        writeFCR0(_eraseAuto ? EEFC_FCMD_EWP : EEFC_FCMD_WP, page);
}

This is bad for ATSAM4S as it is not supported out side of the first 2 8 kB sectors.

Here's what the datasheet says (on page 42, http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11100-32-bit%20Cortex-M4-Microcontroller-SAM4S_Datasheet.pdf).

The following erase commands can be used depending on the sector size:

  • 8 Kbyte small sector
    • Erase and write page (EWP)
    • Erase and write page and lock (EWPL)
    • Erase sector (ES) with FARG set to a page number in the sector to erase
    • Erase pages (EPA) with FARG [1:0] = 0 to erase four pages or FARG [1:0] = 1 to erase eight pages. FARG [1:0] = 2 and FARG [1:0] = 3 must not be used.
  • 48 Kbyte and 64 Kbyte sectors
    • One block of 8 pages inside any sector, with the command Erase pages (EPA) with FARG[1:0] = 1
    • One block of 16 pages inside any sector, with the command Erase pages (EPA) and FARG[1:0] = 2
    • One block of 32 pages inside any sector, with the command Erase pages (EPA) and FARG[1:0] = 3
    • One sector with the command Erase sector (ES) and FARG set to a page number in the sector to
      erase
  • Entire memory plane
    • The entire Flash, with the command Erase all (EA)

I think the fix here would be adding an error if writing any flash over address 16 kB on an ATSAM4S chip. I'm not sure if other EefcFlash chips are configured the same way, my quick look at ATSAM3 that this is not an issue.

If I have some time, I'll try to put together a PR. Should be pretty straight-forward (keep existing behaviour the same, then just add an additional field for the EWP boundary).

haata commented

Hypothetically it's also possible to build in an auto erase that manually does an erase of the needed pages.
But yeah, it's not something I need and I'm satisfied with just erasing everything tbh.