From 0aeeece97d607d793f2a25c26e6549afbd790889 Mon Sep 17 00:00:00 2001 From: David Saada Date: Tue, 20 Feb 2018 14:51:53 +0200 Subject: [PATCH] FlashIAP driver modifications: - Support programming across sectors. - Support program size not aligned to page size. - Fix validations on sector erase. --- TESTS/mbed_drivers/flashiap/main.cpp | 57 ++++++++++++++++++++++--- drivers/FlashIAP.cpp | 62 +++++++++++++++++++++------- drivers/FlashIAP.h | 5 ++- 3 files changed, 101 insertions(+), 23 deletions(-) diff --git a/TESTS/mbed_drivers/flashiap/main.cpp b/TESTS/mbed_drivers/flashiap/main.cpp index 30a443e45d..48c207104e 100644 --- a/TESTS/mbed_drivers/flashiap/main.cpp +++ b/TESTS/mbed_drivers/flashiap/main.cpp @@ -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), }; diff --git a/drivers/FlashIAP.cpp b/drivers/FlashIAP.cpp index 9677e383d5..f36385a7e4 100644 --- a/drivers/FlashIAP.cpp +++ b/drivers/FlashIAP.cpp @@ -20,7 +20,9 @@ * SOFTWARE. */ +#include #include +#include #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; } diff --git a/drivers/FlashIAP.h b/drivers/FlashIAP.h index a6acc1bed9..a818410c55 100644 --- a/drivers/FlashIAP.h +++ b/drivers/FlashIAP.h @@ -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 _mutex; };