diff --git a/hal/include/hal/flash_api.h b/hal/include/hal/flash_api.h index e98c806..cfcbf79 100644 --- a/hal/include/hal/flash_api.h +++ b/hal/include/hal/flash_api.h @@ -79,7 +79,14 @@ /** Program pages starting at defined address * * The pages should not cross multiple sectors. - * This function does not do any check for address alignments or if size is aligned to a page size. + * + * \note The upper level FlashIAP.cpp code guarantees: + * So, implementations of this function do not need to check these things. + * * @param obj The flash object * @param address The sector starting address * @param data The data buffer to be programmed diff --git a/hal/tests/TESTS/mbed_hal/flash/functional_tests/main.cpp b/hal/tests/TESTS/mbed_hal/flash/functional_tests/main.cpp index 46a9a7b..8f0a049 100644 --- a/hal/tests/TESTS/mbed_hal/flash/functional_tests/main.cpp +++ b/hal/tests/TESTS/mbed_hal/flash/functional_tests/main.cpp @@ -218,6 +218,53 @@ delete[] data_flashed; } +// Tests that one flash page can be copied to another +void flash_copy_flash_to_flash() +{ + flash_t test_flash; + int32_t ret = flash_init(&test_flash); + TEST_ASSERT_EQUAL_INT32(0, ret); + + uint32_t last_page_address = flash_get_start_address(&test_flash) + flash_get_size(&test_flash) - flash_get_page_size(&test_flash); + uint32_t *last_page_pointer = reinterpret_cast(last_page_address); + uint32_t second_to_last_page_address = last_page_address - flash_get_page_size(&test_flash); + uint32_t *second_to_last_page_pointer = reinterpret_cast(second_to_last_page_address); + + // Erase the sector(s) which contain the last two pages + uint32_t last_page_sector = ALIGN_DOWN(last_page_address, flash_get_sector_size(&test_flash, last_page_address)); + uint32_t second_to_last_page_sector = ALIGN_DOWN(second_to_last_page_address, flash_get_sector_size(&test_flash, second_to_last_page_address)); + + ret = flash_erase_sector(&test_flash, last_page_sector); + TEST_ASSERT_EQUAL_INT32(0, ret); + + if (last_page_sector != second_to_last_page_sector) { + ret = flash_erase_sector(&test_flash, second_to_last_page_sector); + TEST_ASSERT_EQUAL_INT32(0, ret); + } + + // Fill second to last page with test data + size_t const numDataWords = flash_get_page_size(&test_flash) / sizeof(uint32_t); + uint32_t *data = new uint32_t[numDataWords]; + for (size_t wordIdx = 0; wordIdx < numDataWords; ++wordIdx) { + data[wordIdx] = wordIdx; + } + + ret = flash_program_page(&test_flash, second_to_last_page_address, reinterpret_cast(data), flash_get_page_size(&test_flash)); + TEST_ASSERT_EQUAL_INT32(0, ret); + + // Make sure data was written + TEST_ASSERT_EQUAL_UINT32_ARRAY(data, second_to_last_page_pointer, numDataWords); + + // Now, program last page from the second to last page + ret = flash_program_page(&test_flash, last_page_address, reinterpret_cast(second_to_last_page_pointer), flash_get_page_size(&test_flash)); + TEST_ASSERT_EQUAL_INT32(0, ret); + + // Make sure data was written + TEST_ASSERT_EQUAL_UINT32_ARRAY(data, last_page_pointer, numDataWords); + + delete[] data; +} + // check the execution speed at the start and end of the test to make sure // cache settings weren't changed void flash_clock_and_cache_test() @@ -232,6 +279,7 @@ Case("Flash - mapping alignment", flash_mapping_alignment_test), Case("Flash - erase sector", flash_erase_sector_test), Case("Flash - program page", flash_program_page_test), + Case("Flash - copy flash to flash", flash_copy_flash_to_flash), Case("Flash - clock and cache test", flash_clock_and_cache_test), }; diff --git a/targets/TARGET_NXP/TARGET_LPC176X/device/flash_api.c b/targets/TARGET_NXP/TARGET_LPC176X/device/flash_api.c index 7a3dfad..259d420 100644 --- a/targets/TARGET_NXP/TARGET_LPC176X/device/flash_api.c +++ b/targets/TARGET_NXP/TARGET_LPC176X/device/flash_api.c @@ -113,18 +113,23 @@ const uint8_t *data, uint32_t size) { unsigned long n; - const uint32_t copySize = 1024; // should be 256|512|1024|4096 - uint8_t *alignedData, *source; + const uint32_t pageSize = flash_get_page_size(obj); + uint8_t *tempBuffer, *source; - alignedData = 0; + tempBuffer = 0; source = (uint8_t *)data; + + // On LPC1768, the first RAM bank starts at 0x1000000, so anywhere below that has to be flash. + // The IAP firmware does not support flash to flash copies, so if the source data is in flash + // it must be buffered in RAM. + bool isFlashToFlashCopy = (ptrdiff_t)(data) < 0x10000000; // check word boundary - if (((uint32_t)data % 4) != 0) { + if (isFlashToFlashCopy) { // always malloc outside critical section - alignedData = malloc(copySize); - if (alignedData == 0) { - return (1); + tempBuffer = malloc(pageSize); + if (tempBuffer == 0) { + return -1; } } @@ -132,10 +137,12 @@ core_util_critical_section_enter(); - while (size) { - if (((uint32_t)data % 4) != 0) { - memcpy(alignedData, source, copySize); - source = alignedData; + for(size_t pageIdx = 0; pageIdx < (size / pageSize); ++pageIdx) + { + uint8_t * pageSourceAddr = source + (pageIdx * pageSize); + if (isFlashToFlashCopy) { + memcpy(tempBuffer, pageSourceAddr, pageSize); + pageSourceAddr = tempBuffer; } /* @@ -147,28 +154,28 @@ IAP.par[1] = n; // End Sector IAP_Call (&IAP.cmd, &IAP.stat); // Call IAP Command if (IAP.stat) { - return (1); // Command Failed + core_util_critical_section_exit(); + return -1; // Command Failed } IAP.cmd = 51; // Copy RAM to Flash IAP.par[0] = address; // Destination Flash Address - IAP.par[1] = (unsigned long)source; // Source RAM Address - IAP.par[2] = copySize; // number of bytes to be written + IAP.par[1] = (unsigned long)pageSourceAddr; // Source RAM Address + IAP.par[2] = pageSize; // number of bytes to be written IAP.par[3] = CCLK; // CCLK in kHz IAP_Call (&IAP.cmd, &IAP.stat); // Call IAP Command if (IAP.stat) { - return (1); // Command Failed + core_util_critical_section_exit(); + return -1; // Command Failed } - source += copySize; - size -= copySize; - address += copySize; + address += pageSize; } core_util_critical_section_exit(); - if(alignedData != 0) { // We allocated our own memory - free(alignedData); + if(tempBuffer != 0) { // We allocated our own memory + free(tempBuffer); } return (0); // Finished without Errors