LPCOpen Platform  v1.03
LPCOpen Platform for NXP LPC Microcontrollers
 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"
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 {
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 */
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 = {
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. */
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 */
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 */
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 */
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. */
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(). */
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;
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 */
364  uint32_t secend)
365 {
366  command[0] = IAP_ERASE_SECS;
367  command[1] = secstart;
368  command[2] = secend;
370  command[4] = bank;
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 */
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 */
427  command[1] = secstart;
428  command[2] = secend;
429  command[3] = bank;
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 */
522  command[1] = start;
523  command[2] = (uint32_t) buff;
524  command[3] = PAGE_SIZE;
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;
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 
589 {
590  int idx;
591  const uint32_t bankAddress[2] = {0x1A000000, 0x1B000000};
592  uint32_t *iapTest32 = (uint32_t *) IAP_LOCATION;
593 
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;
606  if (result[0] == IAP_COMMAND_SUCCESS) {
607  /* Get part ID */
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