| LPCOpen Platform v1.03 LPCOpen Platform for NXP LPC Microcontrollers

- Main Page
- Modules
- Data Structures
- Files
- Related Pages

- File List
- Globals

All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dfuutil_programming_iflash_algorithm.c
Go to the documentation of this file.
1 /*
2 * @brief DFU Utility program for internal FLASH
3 * This programming algortihm allows reading or writing
4 * to the internal FLASH on 18xx/43xx devices.
5 *
6 * @note
7 * Copyright(C) NXP Semiconductors, 2012
8 * All rights reserved.
9 *
10 * @par
11 * Software that is described herein is for illustrative purposes only
12 * which provides customers with programming information regarding the
13 * LPC products. This software is supplied "AS IS" without any warranties of
14 * any kind, and NXP Semiconductors and its licensor disclaim any and
15 * all warranties, express or implied, including all implied warranties of
16 * merchantability, fitness for a particular purpose and non-infringement of
17 * intellectual property rights. NXP Semiconductors assumes no responsibility
18 * or liability for the use of the software, conveys no license or rights under any
19 * patent, copyright, mask work right, or any other intellectual property rights in
20 * or to any products. NXP Semiconductors reserves the right to make changes
21 * in the software without notification. NXP Semiconductors also makes no
22 * representation or warranty that such application will be suitable for the
23 * specified use without further testing or modification.
24 *
25 * @par
26 * Permission to use, copy, modify, and distribute this software and its
27 * documentation is hereby granted, under NXP Semiconductors' and its
28 * licensor's relevant copyrights in the software, without fee, provided that it
29 * is used in conjunction with NXP Semiconductors microcontrollers. This
30 * copyright, permission, and disclaimer notice must appear in all copies of
31 * this code.
32 */
33
34 #include "board.h"
35 #include "dfuutil_programming_api.h"
36 #include "stdio.h"
37 #include "string.h"
38
65 /*****************************************************************************
66 * Private types/enumerations/variables
67 ****************************************************************************/
68
69 /* LPC18xx/43xx IAP command defines */
70 #define IAP_INIT 49
71 #define IAP_PREP_SECS 50
72 #define IAP_RAM_TO_FLASH 51
73 #define IAP_ERASE_SECS 52
74 #define IAP_BLANK_CHECK_SECS 53
75 #define IAP_READ_PART_ID 54
76 #define IAP_READ_BOOT_CODE_VER 55
77 #define IAP_READ_DEV_SERIAL_NUM 58
78 #define IAP_COMPARE 56
79 #define IAP_REINVOKE_ISP 57
80 #define IAP_ERASE_PAGE 59
81 #define IAP_SET_ACTIVE_FLASH_BANK 60
82
83 /* LPC18xx/43xx IAP command status defines (partial) */
84 #define IAP_COMMAND_SUCCESS 0x00
85 #define IAP_INVALID_COMMAND 0x01
86 #define IAP_SRC_ADDR_ERROR 0x02
87 #define IAP_DST_ADDR_ERROR 0x03
88 #define IAP_SRC_ADDR_NOT_MAPPED 0x04
89 #define IAP_DST_ADDR_NOT_MAPPED 0x05
90 #define IAP_COUNT_ERROR 0x06
91 #define IAP_INVALID_SECTOR 0x - 7
92 #define IAP_SECTOR_NOT_BLANK 0x08
93 #define IAP_SECTOR_NOT_PREPARED 0x09
94 #define IAP_COMPARE_ERROR 0x0A
95 #define IAP_BUSY 0x0B
96 #define IAP_PARAM_ERROR 0x0C
97 #define IAP_ADDR_ERROR 0x0D
98 #define IAP_ADDR_NOT_MAPPED 0x0E
99 #define IAP_CMD_LOCKED 0x0F
100 #define IAP_INVALID_CODE 0x10
101 #define IAP_INVALID_BAUD_RATE 0x11
102 #define IAP_INVALID_STOP_BIT 0x12
103 #define IAP_CRP_ENABLED 0x13
104 #define IAP_INVALID_FLASH_UNIT 0x14
105 #define IAP_USER_CODE_CHECKSUM 0x15
106 #define IAP_ERROR_SETTING_ACTIVE_PART 0x16
107
108 /* Maximum size of FLASH */
109 #define FLASHMAXSIZE (512 * 1024)
110
111 /* Some IAP functions need to know the clock speed. If you don't
112 know it, pick a -alow- speed. */
113 #define CPUCLOCKFRQINKHZ 90000
114
115 /* 18xx and 43xx sector information */
116 typedef struct {
117 uint32_t sector_offset;
118 uint32_t sector_size;
119 } SECTOR_INFO_T;
120 static const SECTOR_INFO_T sectorinfo[] = {
121 {0x00000000, 0x00002000}, /* Offset 0x00002000, 8K size */
122 {0x00002000, 0x00002000}, /* Offset 0x00004000, 8K size */
123 {0x00004000, 0x00002000}, /* Offset 0x00006000, 8K size */
124 {0x00006000, 0x00002000}, /* Offset 0x00008000, 8K size */
125 {0x00008000, 0x00002000}, /* Offset 0x0000A000, 8K size */
126 {0x0000A000, 0x00002000}, /* Offset 0x0000C000, 8K size */
127 {0x0000C000, 0x00002000}, /* Offset 0x0000E000, 8K size */
128 {0x0000E000, 0x00002000}, /* Offset 0x00010000, 8K size */
129 {0x00010000, 0x00010000}, /* Offset 0x00020000, 64K size */
130 {0x00020000, 0x00010000}, /* Offset 0x00030000, 64K size */
131 {0x00030000, 0x00010000}, /* Offset 0x00040000, 64K size */
132 {0x00040000, 0x00010000}, /* Offset 0x00050000, 64K size */
133 {0x00050000, 0x00010000}, /* Offset 0x00060000, 64K size */
134 {0x00060000, 0x00010000}, /* Offset 0x00070000, 64K size */
135 {0x00070000, 0x00010000}, /* Offset 0x00000000, 64K size */
136 {0xFFFFFFFF, 0xFFFFFFFF} /* End of list */
137 };
138
139 /* FLASH page size */
140 #define PAGE_SIZE 512
141
142 /* IAP function and support structures */
143 typedef void (*IAP)(uint32_t *, uint32_t *);
144 #define IAP_LOCATION *((uint32_t *) 0x10400100);
145 static IAP iap_entry;
146 static uint32_t command[6], result[5];
147
148 /* Forward references */
149 int32_t progalgo_iflash_erase(uint32_t start, uint32_t size);
150
151 int32_t progalgo_iflash_erase_all(void);
152
153 int32_t progalgo_iflash_write(void *buff, uint32_t start, uint32_t size);
154
155 int32_t progalgo_iflash_read(void *buff, uint32_t start, uint32_t size);
156
157 /* DFU buffer size */
158 #define DFU_BUFF_PROG_SIZE PAGE_SIZE
159
160 /* Function table for exposed API functions */
161 static const PROGALGOS_T palgos = {
162 progalgo_iflash_erase,
163 progalgo_iflash_erase_all,
164 progalgo_iflash_write,
165 progalgo_iflash_read
166 };
167
168 /* Number of program regions */
169 #define PROGRAM_REGIONS 2
170
171 /* Regions and sizes for this device. The regions will be filled in after
172 IAP init. */
173 static DFUPROG_REGZONE_T pregions[PROGRAM_REGIONS] = {
174 {0x00000000, 0x00000000},
175 {0x00000000, 0x00000000}
176 };
177
178 /* DFU programming region/API structure
179 This structure puts them all together and is used by the DFU streamer */
180 static DFUPROG_REGION_T dfuregion = {
181 0, /* Up to 2 regions per device, set later */
182 DFU_BUFF_PROG_SIZE, /* Size of buffer provided to DFU streamer */
183 &palgos, /* Pointer to programming algorithm function table */
184 pregions, /* Array of region addresses and sizes */
185 DFUPROG_VALIDVAL,
186 "Internal FLASH"
187 };
188
189 /* Temporary work string for formatting */
190 static char tempSTR[128];
191
192 /*****************************************************************************
193 * Public types/enumerations/variables
194 ****************************************************************************/
195
196 /*****************************************************************************
197 * Private functions
198 ****************************************************************************/
199
200 /* Find bank for an address */
201 static int progalgo_iflash_findbank(uint32_t addr)
202 {
203 int bank;
204
205 /* Bank 0 check first, then bank 1 */
206 for (bank = 0; bank < dfuregion.num_regions; bank++) {
207 if ((addr >= pregions[bank].region_addr) &&
208 (addr < (pregions[bank].region_addr + pregions[bank].region_size))) {
209 return bank;
210 }
211 }
212
213 /* No matching bank, note this function returns -1 for an error, while
214 most return 0 for error. */
215 return -1;
216 }
217
218 /* Verify a program address range is valid. It should not cross banks and
219 should not excced the end of the banks address range. */
220 static int progalgo_iflash_progaddrvalid(uint32_t addr, uint32_t size)
221 {
222 int bank, sg;
223
224 /* Determine bank first */
225 bank = progalgo_iflash_findbank(addr);
226 if (bank < 0) {
227 usbDebug("FLASH: Address does not map to bank\n");
228 return 0;
229 }
230
231 /* The address must always be 512 byte aligned */
232 if ((addr & (PAGE_SIZE - 1)) != 0) {
233 usbDebug("FLASH: Address is not 512 byte aligned\n");
234 return 0;
235 }
236
237 /* The size must be 512 byte aligned */
238 sg = size / PAGE_SIZE;
239 if (size != (PAGE_SIZE * sg)) {
240 usbDebug("FLASH: Size must be a multiple of 512 bytes\n");
241 return 0;
242 }
243
244 /* The range check can be done here, but both erase and programming
245 functions need to know the starting and ending sector for the address
246 range, so we'll let the sector find function handle it. */
247 return (int) size;
248 }
249
250 /* For erase and write prepare operations, the starting and ending sector
251 need to be known for an address range. This determines that sector
252 range. Before calling this function, the address and size should have
253 been validated with a call to progalgo_iflash_progaddrvalid(). */
254 static int progalgo_iflash_find_sectorrange(uint32_t addr, uint32_t size,
255 int *bank, uint32_t *secstart, uint32_t *secend, int *aligned)
256 {
257 int idx;
258 uint32_t addrend, addrbase, regstart, regend;
259
260 /* Bank number for address range */
261 *bank = progalgo_iflash_findbank(addr);
262
263 /* Base address for bank */
264 addrbase = pregions[*bank].region_addr;
265 addrend = addr + size - 1;
266
267 /* The aligned flag is returned if the address range exactly maps to a
268 range of sectors boundaries. For erase operations, the aligned flag
269 must be set. */
270 *aligned = 0;
271
272 /* Find starting sector */
273 idx = 0;
274 *secstart = 0xFFFFFFFF;
275 while ((*secstart == 0xFFFFFFFF) && (idx != -1)) {
276 if (sectorinfo[idx].sector_offset == 0xFFFFFFFF) {
277 idx = -1;
278 }
279 else {
280 regstart = addrbase + sectorinfo[idx].sector_offset;
281 regend = regstart + sectorinfo[idx].sector_size - 1;
282 if ((addr >= regstart) && (addr <= regend)) {
283 /* Falls in sector range */
284 *secstart = (uint32_t) idx;
285
286 /* Is starting address aligned? */
287 if (addr == regstart) {
288 *aligned = 1;
289 }
290 else {
291 *aligned = 0;
292 }
293 }
294 else {
295 /* Check next range */
296 idx++;
297 }
298 }
299 }
300
301 /* A starting sector was found? */
302 if (idx == -1) {
303 return 0;
304 }
305
306 /* Find the last sector for the address range */
307 *secend = 0xFFFFFFFF;
308 while ((*secend == 0xFFFFFFFF) && (idx != -1)) {
309 if (sectorinfo[idx].sector_offset == 0xFFFFFFFF) {
310 idx = -1;
311 }
312 else {
313 regstart = addrbase + sectorinfo[idx].sector_offset;
314 regend = regstart + sectorinfo[idx].sector_size - 1;
315 if ((addrend >= regstart) && (addrend <= regend)) {
316 /* Falls in sector range */
317 *secend = (uint32_t) idx;
318
319 /* Is ending address aligned? */
320 if (addrend == regend) {
321 *aligned = 1;
322 }
323 else {
324 *aligned = 0;
325 }
326 }
327 else {
328 /* Check next range */
329 idx++;
330 }
331 }
332 }
333
334 /* A ending sector was found? */
335 if (idx == -1) {
336 return 0;
337 }
338
339 return size;
340 }
341
342 /* Prepare a range of sectors for write/erase, returns a IAP status
343 value of IAP_* */
344 static int progalgo_iflash_prepwrite(uint32_t bank, uint32_t secstart,
345 uint32_t secend)
346 {
347 command[0] = IAP_PREP_SECS;
348 command[1] = secstart;
349 command[2] = secend;
350 command[3] = bank;
351 iap_entry(command, result);
352
353 if (result[0] != IAP_COMMAND_SUCCESS) {
354 sprintf(tempSTR, "ERASE: Error preparing sectors %d-%d (bank %d)\n",
355 secstart, secend, bank);
356 usbDebug(tempSTR);
357 }
358
359 return result[0];
360 }
361
362 /* Erase a range of sectors */
363 static int progalgo_iflash_erasesectors(uint32_t bank, uint32_t secstart,
364 uint32_t secend)
365 {
366 command[0] = IAP_ERASE_SECS;
367 command[1] = secstart;
368 command[2] = secend;
369 command[3] = CPUCLOCKFRQINKHZ;
370 command[4] = bank;
371 iap_entry(command, result);
372
373 if (result[0] != IAP_COMMAND_SUCCESS) {
374 sprintf(tempSTR, "ERASE: Error erasing sectors %d-%d (bank %d)\n",
375 secstart, secend, bank);
376 usbDebug(tempSTR);
377 }
378
379 return result[0];
380 }
381
382 /* Erase a region of FLASH memory */
383 static int32_t progalgo_iflash_erase(uint32_t start, uint32_t size)
384 {
385 int bank, aligned;
386 uint32_t secstart, secend;
387
388 sprintf(tempSTR, "ERASE: %p with size %p\n",
389 (void *) start, (void *) size);
390 usbDebug(tempSTR);
391
392 /* Basic verification first of input parameters */
393 if (progalgo_iflash_progaddrvalid(start, size) == 0) {
394 usbDebug("ERASE: address/size validation failure\r\n");
395 return 0;
396 }
397
398 /* Get sector and bank info for the operation */
399 if (progalgo_iflash_find_sectorrange(start, size, &bank,
400 &secstart, &secend, &aligned) == 0) {
401 usbDebug("ERASE: sector range lookup failure\r\n");
402 return 0;
403 }
404
405 /* Must be aligned to a sector range! */
406 if (aligned == 0) {
407 usbDebug("ERASE: Address range must be sector aligned\r\n");
408 return 0;
409 }
410
411 sprintf(tempSTR, "ERASE: Bank %d, Start sec %d, End sec %d\n",
412 bank, secstart, secend);
413 usbDebug(tempSTR);
414
415 /* Prepare for write */
416 if (progalgo_iflash_prepwrite(bank, secstart, secend) != IAP_COMMAND_SUCCESS) {
417 return 0;
418 }
419
420 /* Erase sectors */
421 if (progalgo_iflash_erasesectors(bank, secstart, secend) != IAP_COMMAND_SUCCESS) {
422 return 0;
423 }
424
425 /* Verify they are erased */
426 command[0] = IAP_BLANK_CHECK_SECS;
427 command[1] = secstart;
428 command[2] = secend;
429 command[3] = bank;
430 iap_entry(command, result);
431 if (result[0] != IAP_COMMAND_SUCCESS) {
432 usbDebug("ERASE: Error erasing sectors\r\n");
433 return 0;
434 }
435
436 return size;
437 }
438
439 /* Erase the entire device */
440 static int32_t progalgo_iflash_erase_all(void)
441 {
442 uint32_t bank, sz;
443
444 /* Stop if there are no banks to erase */
445 if (dfuregion.num_regions == 0) {
446 return 0;
447 }
448
449 /* Erase 1 or 2 banks */
450 for (bank = 0; bank < dfuregion.num_regions; bank++) {
451 if (progalgo_iflash_erase(pregions[bank].region_addr,
452 pregions[bank].region_size) == 0) {
453 return 0;
454 }
455 }
456
457 sz = pregions[0].region_size;
458 if (dfuregion.num_regions == 2) {
459 sz += pregions[1].region_size;
460 }
461
462 return sz;
463 }
464
465 /* Write buffer to FLASH */
466 static int32_t progalgo_iflash_write(void *buff, uint32_t start, uint32_t size)
467 {
468 int bank, aligned;
469 uint32_t secstart, secend, offset, wsize = size;
470 uint8_t *fbuff = (uint8_t *) buff;
471
472 sprintf(tempSTR, "WRITE: %p with size %p\n", (void *) start, (void *) size);
473 usbDebug(tempSTR);
474
475 /* Verify size doesn't exceed buffer length. This check is not
476 really needed as the DFU streamer keeps the transfer at or
477 below the buffer size, but it's here for debug. */
478 if (size > PAGE_SIZE) {
479 usbDebug("WRITE: Program buffer too big\r\n");
480 return 0;
481 }
482
483 /* If the passed size is less than the buffer size, then this is
484 the last fragment of code to program into FLASH. Since images to
485 program may not be on 512 byte boundaries, we'll fill up the
486 unused space with 0xFF to allow programming. */
487 if (wsize < PAGE_SIZE) {
488 for (; wsize < PAGE_SIZE; wsize++) {
489 fbuff[wsize] = 0xFF;
490 }
491
492 sprintf(tempSTR, "WRITE: Last sector too small, padded %d bytes\n",
493 (wsize - size));
494 usbDebug(tempSTR);
495 }
496
497 /* Verify basic parameters */
498 if (progalgo_iflash_progaddrvalid(start, wsize) == 0) {
499 usbDebug("WRITE: Input address/size is not valid\r\n");
500 return 0;
501 }
502
503 /* Get sector and bank info for the operation */
504 if (progalgo_iflash_find_sectorrange(start, wsize, &bank,
505 &secstart, &secend, &aligned) == 0) {
506 usbDebug("WRITE: sector range lookup failure\r\n");
507 return 0;
508 }
509
510 offset = start - (pregions[bank].region_addr + sectorinfo[secstart].sector_offset);
511 sprintf(tempSTR, "WRITE: programming: Bank %d, Start sec %d, End sec %d offset %d size %d\n",
512 bank, secstart, secend, offset, wsize);
513 usbDebug(tempSTR);
514
515 /* Prepare for write */
516 if (progalgo_iflash_prepwrite(bank, secstart, secend) != IAP_COMMAND_SUCCESS) {
517 return 0;
518 }
519
520 /* Program the data */
521 command[0] = IAP_RAM_TO_FLASH;
522 command[1] = start;
523 command[2] = (uint32_t) buff;
524 command[3] = PAGE_SIZE;
525 command[4] = CPUCLOCKFRQINKHZ;
526 iap_entry(command, result);
527 if (result[0] != IAP_COMMAND_SUCCESS) {
528 usbDebug("WRITE: Error programming address range\r\n");
529 return 0;
530 }
531
532 /* Verify */
533 command[0] = IAP_COMPARE;
534 command[1] = start;
535 command[2] = (uint32_t) buff;
536 command[3] = PAGE_SIZE;
537 iap_entry(command, result);
538 if (result[0] != IAP_COMMAND_SUCCESS) {
539 usbDebug("WRITE: Verify error on program\r\n");
540 return 0;
541 }
542
543 return size;
544 }
545
546 /* Read data from the device. Returns 0 if the region cannot
547 be read. */
548 static int32_t progalgo_iflash_read(void *buff, uint32_t start, uint32_t size)
549 {
550 sprintf(tempSTR, "READ @ 0x%p, %p bytes\n", (void *) start, (void *) size);
551 usbDebug(tempSTR);
552
553 /* SPI FLASH is memory mapped, so just copy the data to the buffer */
554 memmove(buff, (void *) start, size);
555
556 return size;
557 }
558
559 /* Attempt to compute FLASH size from ID */
560 static uint32_t progalgo_iflash_getsize(int bank, uint32_t part_id2)
561 {
562 uint32_t flash_size;
563 const int bank_shift[2] = {0, 4};
564 const int bank_mask[2] = {0x0F, 0xF0};
565
566 flash_size = ((part_id2 & bank_mask[bank]) >> bank_shift[bank]) * 0x10000;
567 if (FLASHMAXSIZE <= flash_size) {
568 flash_size = 0;
569 }
570 else {
571 flash_size = FLASHMAXSIZE - flash_size;
572 }
573
574 return flash_size;
575 }
576
577 /*****************************************************************************
578 * Public functions
579 ****************************************************************************/
580
588 DFUPROG_REGION_T *algo_flash_init(void)
589 {
590 int idx;
591 const uint32_t bankAddress[2] = {0x1A000000, 0x1B000000};
592 uint32_t *iapTest32 = (uint32_t *) IAP_LOCATION;
593
594 iap_entry = (IAP) IAP_LOCATION;
595 dfuregion.num_regions = 0;
596
597 /* Is IAP supported? */
598 if (*iapTest32 == 0) {
599 usbDebug("FLASH not supported on this device");
600 return &dfuregion;
601 }
602
603 /* Initialize IAP */
604 command[0] = IAP_INIT;
605 iap_entry(command, result);
606 if (result[0] == IAP_COMMAND_SUCCESS) {
607 /* Get part ID */
608 command[0] = IAP_READ_PART_ID;
609 iap_entry(command, result);
610
611 /* Setup regions and sizes based on part ID */
612 for (idx = 0; idx < 2; idx++) {
613 /* Up to 2 possible regions at 0x1A000000 and 0x1B000000 with varying sizes */
614 pregions[idx].region_size = progalgo_iflash_getsize(idx, result[2]);
615 if (pregions[idx].region_size > 0) {
616 pregions[idx].region_addr = bankAddress[idx];
617 dfuregion.num_regions++;
618 }
619 else {
620 pregions[idx].region_addr = 0;
621 }
622 }
623 }
624
625 sprintf(tempSTR, "FLASH: banks = %d(0x%08x/0x%08x)\n", dfuregion.num_regions,
626 result[1], result[2]);
627 usbDebug(tempSTR);
628 for (idx = 0; idx < dfuregion.num_regions; idx++) {
629 sprintf(tempSTR, "FLASH bank %d: Start: %p, Size %p\n", idx,
630 (void *) pregions[idx].region_addr, (void *) pregions[idx].region_size);
631 usbDebug(tempSTR);
632 }
633
634 /* On FLASHless parts the number of regions will be 0 */
635 return &dfuregion;
636 }
637