Skip to content

Commit 9c65ad9

Browse files
author
Jamie Smith
authored
Bugfix: LPC1768 IAP could not copy flash to flash (ARMmbed#156)
* Bugfix: LPC1768 IAP could not copy flash to flash * Add flash to flash test for flash API * Fix style * Style again * Augh again
1 parent d9d9f70 commit 9c65ad9

File tree

3 files changed

+83
-21
lines changed

3 files changed

+83
-21
lines changed

hal/include/hal/flash_api.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,14 @@ int32_t flash_read(flash_t *obj, uint32_t address, uint8_t *data, uint32_t size)
7979
/** Program pages starting at defined address
8080
*
8181
* The pages should not cross multiple sectors.
82-
* This function does not do any check for address alignments or if size is aligned to a page size.
82+
*
83+
* \note The upper level FlashIAP.cpp code guarantees:
84+
* <ul><li>\c data is 32-bit aligned</li>
85+
* <li>\c size is a multiple of the page size</li>
86+
* <li>\c address is inside a flash sector</li>
87+
* <li>\c address is aligned to the page size (but not the sector size)</li>
88+
* </ul>So, implementations of this function do not need to check these things.
89+
*
8390
* @param obj The flash object
8491
* @param address The sector starting address
8592
* @param data The data buffer to be programmed

hal/tests/TESTS/mbed_hal/flash/functional_tests/main.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,53 @@ void flash_program_page_test()
218218
delete[] data_flashed;
219219
}
220220

221+
// Tests that one flash page can be copied to another
222+
void flash_copy_flash_to_flash()
223+
{
224+
flash_t test_flash;
225+
int32_t ret = flash_init(&test_flash);
226+
TEST_ASSERT_EQUAL_INT32(0, ret);
227+
228+
uint32_t last_page_address = flash_get_start_address(&test_flash) + flash_get_size(&test_flash) - flash_get_page_size(&test_flash);
229+
uint32_t *last_page_pointer = reinterpret_cast<uint32_t *>(last_page_address);
230+
uint32_t second_to_last_page_address = last_page_address - flash_get_page_size(&test_flash);
231+
uint32_t *second_to_last_page_pointer = reinterpret_cast<uint32_t *>(second_to_last_page_address);
232+
233+
// Erase the sector(s) which contain the last two pages
234+
uint32_t last_page_sector = ALIGN_DOWN(last_page_address, flash_get_sector_size(&test_flash, last_page_address));
235+
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));
236+
237+
ret = flash_erase_sector(&test_flash, last_page_sector);
238+
TEST_ASSERT_EQUAL_INT32(0, ret);
239+
240+
if (last_page_sector != second_to_last_page_sector) {
241+
ret = flash_erase_sector(&test_flash, second_to_last_page_sector);
242+
TEST_ASSERT_EQUAL_INT32(0, ret);
243+
}
244+
245+
// Fill second to last page with test data
246+
size_t const numDataWords = flash_get_page_size(&test_flash) / sizeof(uint32_t);
247+
uint32_t *data = new uint32_t[numDataWords];
248+
for (size_t wordIdx = 0; wordIdx < numDataWords; ++wordIdx) {
249+
data[wordIdx] = wordIdx;
250+
}
251+
252+
ret = flash_program_page(&test_flash, second_to_last_page_address, reinterpret_cast<const uint8_t *>(data), flash_get_page_size(&test_flash));
253+
TEST_ASSERT_EQUAL_INT32(0, ret);
254+
255+
// Make sure data was written
256+
TEST_ASSERT_EQUAL_UINT32_ARRAY(data, second_to_last_page_pointer, numDataWords);
257+
258+
// Now, program last page from the second to last page
259+
ret = flash_program_page(&test_flash, last_page_address, reinterpret_cast<const uint8_t *>(second_to_last_page_pointer), flash_get_page_size(&test_flash));
260+
TEST_ASSERT_EQUAL_INT32(0, ret);
261+
262+
// Make sure data was written
263+
TEST_ASSERT_EQUAL_UINT32_ARRAY(data, last_page_pointer, numDataWords);
264+
265+
delete[] data;
266+
}
267+
221268
// check the execution speed at the start and end of the test to make sure
222269
// cache settings weren't changed
223270
void flash_clock_and_cache_test()
@@ -232,6 +279,7 @@ Case cases[] = {
232279
Case("Flash - mapping alignment", flash_mapping_alignment_test),
233280
Case("Flash - erase sector", flash_erase_sector_test),
234281
Case("Flash - program page", flash_program_page_test),
282+
Case("Flash - copy flash to flash", flash_copy_flash_to_flash),
235283
Case("Flash - clock and cache test", flash_clock_and_cache_test),
236284
};
237285

targets/TARGET_NXP/TARGET_LPC176X/device/flash_api.c

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -113,29 +113,36 @@ int32_t flash_program_page(flash_t *obj, uint32_t address,
113113
const uint8_t *data, uint32_t size)
114114
{
115115
unsigned long n;
116-
const uint32_t copySize = 1024; // should be 256|512|1024|4096
117-
uint8_t *alignedData, *source;
116+
const uint32_t pageSize = flash_get_page_size(obj);
117+
uint8_t *tempBuffer, *source;
118118

119-
alignedData = 0;
119+
tempBuffer = 0;
120120
source = (uint8_t *)data;
121+
122+
// On LPC1768, the first RAM bank starts at 0x1000000, so anywhere below that has to be flash.
123+
// The IAP firmware does not support flash to flash copies, so if the source data is in flash
124+
// it must be buffered in RAM.
125+
bool isFlashToFlashCopy = (ptrdiff_t)(data) < 0x10000000;
121126

122127
// check word boundary
123-
if (((uint32_t)data % 4) != 0) {
128+
if (isFlashToFlashCopy) {
124129
// always malloc outside critical section
125-
alignedData = malloc(copySize);
126-
if (alignedData == 0) {
127-
return (1);
130+
tempBuffer = malloc(pageSize);
131+
if (tempBuffer == 0) {
132+
return -1;
128133
}
129134
}
130135

131136
n = GetSecNum(address); // Get Sector Number
132137

133138
core_util_critical_section_enter();
134139

135-
while (size) {
136-
if (((uint32_t)data % 4) != 0) {
137-
memcpy(alignedData, source, copySize);
138-
source = alignedData;
140+
for(size_t pageIdx = 0; pageIdx < (size / pageSize); ++pageIdx)
141+
{
142+
uint8_t * pageSourceAddr = source + (pageIdx * pageSize);
143+
if (isFlashToFlashCopy) {
144+
memcpy(tempBuffer, pageSourceAddr, pageSize);
145+
pageSourceAddr = tempBuffer;
139146
}
140147

141148
/*
@@ -147,28 +154,28 @@ int32_t flash_program_page(flash_t *obj, uint32_t address,
147154
IAP.par[1] = n; // End Sector
148155
IAP_Call (&IAP.cmd, &IAP.stat); // Call IAP Command
149156
if (IAP.stat) {
150-
return (1); // Command Failed
157+
core_util_critical_section_exit();
158+
return -1; // Command Failed
151159
}
152160

153161
IAP.cmd = 51; // Copy RAM to Flash
154162
IAP.par[0] = address; // Destination Flash Address
155-
IAP.par[1] = (unsigned long)source; // Source RAM Address
156-
IAP.par[2] = copySize; // number of bytes to be written
163+
IAP.par[1] = (unsigned long)pageSourceAddr; // Source RAM Address
164+
IAP.par[2] = pageSize; // number of bytes to be written
157165
IAP.par[3] = CCLK; // CCLK in kHz
158166
IAP_Call (&IAP.cmd, &IAP.stat); // Call IAP Command
159167
if (IAP.stat) {
160-
return (1); // Command Failed
168+
core_util_critical_section_exit();
169+
return -1; // Command Failed
161170
}
162171

163-
source += copySize;
164-
size -= copySize;
165-
address += copySize;
172+
address += pageSize;
166173
}
167174

168175
core_util_critical_section_exit();
169176

170-
if(alignedData != 0) { // We allocated our own memory
171-
free(alignedData);
177+
if(tempBuffer != 0) { // We allocated our own memory
178+
free(tempBuffer);
172179
}
173180

174181
return (0); // Finished without Errors

0 commit comments

Comments
 (0)