FlashIAP driver modifications:

- Support programming across sectors.
- Support program size not aligned to page size.
- Fix validations on sector erase.
pull/6140/head
David Saada 2018-02-20 14:51:53 +02:00
parent 24a3acd647
commit 0aeeece97d
3 changed files with 101 additions and 23 deletions

View File

@ -81,6 +81,56 @@ void flashiap_program_test()
TEST_ASSERT_EQUAL_INT32(0, ret);
}
void flashiap_cross_sector_program_test()
{
FlashIAP flash_device;
uint32_t ret = flash_device.init();
TEST_ASSERT_EQUAL_INT32(0, ret);
uint32_t page_size = flash_device.get_page_size();
// Erase last two sectors
uint32_t address = flash_device.get_flash_start() + flash_device.get_flash_size();
uint32_t sector_size, agg_size = 0;
for (uint32_t i = 0; i < 2; i++) {
sector_size = flash_device.get_sector_size(address - 1UL);
TEST_ASSERT_NOT_EQUAL(0, sector_size);
TEST_ASSERT_TRUE(sector_size % page_size == 0);
agg_size += sector_size;
address -= sector_size;
}
ret = flash_device.erase(address, agg_size);
TEST_ASSERT_EQUAL_INT32(0, ret);
address += sector_size - page_size;
uint32_t aligned_prog_size = 2 * page_size;
uint32_t prog_size = aligned_prog_size;
if (page_size > 1) {
prog_size--;
}
uint8_t *data = new uint8_t[aligned_prog_size];
for (uint32_t i = 0; i < prog_size; i++) {
data[i] = rand() % 256;
}
for (uint32_t i = prog_size; i < aligned_prog_size; i++) {
data[i] = 0xFF;
}
ret = flash_device.program(data, address, prog_size);
TEST_ASSERT_EQUAL_INT32(0, ret);
uint8_t *data_flashed = new uint8_t[aligned_prog_size];
ret = flash_device.read(data_flashed, address, aligned_prog_size);
TEST_ASSERT_EQUAL_INT32(0, ret);
TEST_ASSERT_EQUAL_UINT8_ARRAY(data, data_flashed, aligned_prog_size);
delete[] data;
delete[] data_flashed;
ret = flash_device.deinit();
TEST_ASSERT_EQUAL_INT32(0, ret);
}
void flashiap_program_error_test()
{
FlashIAP flash_device;
@ -111,12 +161,6 @@ void flashiap_program_error_test()
TEST_ASSERT_EQUAL_INT32(-1, ret);
}
if (flash_device.get_page_size() > 1) {
// unaligned page size
ret = flash_device.program(data, address, page_size + 1);
TEST_ASSERT_EQUAL_INT32(-1, ret);
}
delete[] data;
ret = flash_device.deinit();
@ -126,6 +170,7 @@ void flashiap_program_error_test()
Case cases[] = {
Case("FlashIAP - init", flashiap_init_test),
Case("FlashIAP - program", flashiap_program_test),
Case("FlashIAP - program across sectors", flashiap_cross_sector_program_test),
Case("FlashIAP - program errors", flashiap_program_error_test),
};

View File

@ -20,7 +20,9 @@
* SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include "FlashIAP.h"
#include "mbed_assert.h"
@ -57,6 +59,9 @@ int FlashIAP::init()
if (flash_init(&_flash)) {
ret = -1;
}
uint32_t page_size = get_page_size();
_page_buf = new uint8_t[page_size];
_mutex->unlock();
return ret;
}
@ -68,6 +73,7 @@ int FlashIAP::deinit()
if (flash_free(&_flash)) {
ret = -1;
}
delete[] _page_buf;
_mutex->unlock();
return ret;
}
@ -85,22 +91,43 @@ int FlashIAP::read(void *buffer, uint32_t addr, uint32_t size)
int FlashIAP::program(const void *buffer, uint32_t addr, uint32_t size)
{
uint32_t page_size = get_page_size();
uint32_t current_sector_size = flash_get_sector_size(&_flash, addr);
// addr and size should be aligned to page size, and multiple of page size
// page program should not cross sector boundaries
if (!is_aligned(addr, page_size) ||
!is_aligned(size, page_size) ||
(size < page_size) ||
(((addr % current_sector_size) + size) > current_sector_size)) {
uint32_t flash_size = flash_get_size(&_flash);
uint32_t flash_start_addr = flash_get_start_address(&_flash);
uint32_t chunk, prog_size;
const uint8_t *buf = (uint8_t *) buffer;
const uint8_t *prog_buf;
// addr should be aligned to page size
if (!is_aligned(addr, page_size) || (!buffer) ||
((addr + size) > (flash_start_addr + flash_size))) {
return -1;
}
int ret = 0;
_mutex->lock();
if (flash_program_page(&_flash, addr, (const uint8_t *)buffer, size)) {
ret = -1;
while (size) {
uint32_t current_sector_size = flash_get_sector_size(&_flash, addr);
chunk = std::min(current_sector_size - (addr % current_sector_size), size);
if (chunk < page_size) {
memcpy(_page_buf, buf, chunk);
memset(_page_buf + chunk, 0xFF, page_size - chunk);
prog_buf = _page_buf;
prog_size = page_size;
} else {
chunk = chunk / page_size * page_size;
prog_buf = buf;
prog_size = chunk;
}
if (flash_program_page(&_flash, addr, prog_buf, prog_size)) {
ret = -1;
break;
}
size -= chunk;
addr += chunk;
buf += chunk;
}
_mutex->unlock();
return ret;
}
@ -117,10 +144,19 @@ bool FlashIAP::is_aligned_to_sector(uint32_t addr, uint32_t size)
int FlashIAP::erase(uint32_t addr, uint32_t size)
{
uint32_t current_sector_size = 0UL;
uint32_t current_sector_size;
uint32_t flash_size = flash_get_size(&_flash);
uint32_t flash_start_addr = flash_get_start_address(&_flash);
uint32_t flash_end_addr = flash_start_addr + flash_size;
uint32_t erase_end_addr = addr + size;
if (!is_aligned_to_sector(addr, size)) {
if (erase_end_addr > flash_end_addr) {
return -1;
} else if (erase_end_addr < flash_end_addr){
uint32_t following_sector_size = flash_get_sector_size(&_flash, erase_end_addr);
if (!is_aligned(erase_end_addr, following_sector_size)) {
return -1;
}
}
int32_t ret = 0;
@ -132,10 +168,6 @@ int FlashIAP::erase(uint32_t addr, uint32_t size)
break;
}
current_sector_size = flash_get_sector_size(&_flash, addr);
if (!is_aligned_to_sector(addr, size)) {
ret = -1;
break;
}
size -= current_sector_size;
addr += current_sector_size;
}

View File

@ -72,8 +72,8 @@ public:
* The sectors must have been erased prior to being programmed
*
* @param buffer Buffer of data to be written
* @param addr Address of a page to begin writing to, must be a multiple of program and sector sizes
* @param size Size to write in bytes, must be a multiple of program and sector sizes
* @param addr Address of a page to begin writing to
* @param size Size to write in bytes, must be a multiple of program size
* @return 0 on success, negative error code on failure
*/
int program(const void *buffer, uint32_t addr, uint32_t size);
@ -128,6 +128,7 @@ private:
bool is_aligned_to_sector(uint32_t addr, uint32_t size);
flash_t _flash;
uint8_t *_page_buf;
static SingletonPtr<PlatformMutex> _mutex;
};