Всё, написал свой Flash Loader для SPI. Суть такая: один процессор с большим объёмом памяти принимает по UART с компа прошивку и отправляет её по SPI в LPC1110, делая небольшие паузы каждые 256 байт (пока последний пишет во флеш).
Вот файлы кода, может кому-то нужно. Здесь нужно обратить внимание вот на что:
1) из-за малого количества ОЗУ, пришлось урезать табличку прерываний с 0xC0 байт до 0x20, причём делать все 8 ячеек по 4 байта, т.к. в 8-ю линковщик кладёт контрольную сумму, которую потом сверяет ISP;
2) из-за такой урезанной таблички не работают никакие прерывания, кроме reset handler'а, да и не нужны они здесь. Плюс ко всему: "Before making any IAP call, either disable the interrupts or ensure that the user interrupt vectors are active in RAM and that the interrupt handlers reside in RAM. The IAP code does not use or disable interrupts.", так что прерывания я сразу отключаю.
3) "Flash programming commands use the top 32 bytes of on-chip RAM. The maximum stack usage in the user allocated stack space is 128 bytes and it grows downwards.", то есть нельзя трогать последние 32 байта ОЗУ - их использует IAP, также IAP может использовать до 128 байт стека, поэтому стек делаю размером 128 + 32 = 160 (0xA0) байт.
SPI надо настроить как 16 data bits, POL = 0, PHASE = 0. При приёме каждого следующего слова оно отправляется обратно, чтобы Master мог проверить корректность приёма
Оптимизация максимальная по размеру.
Вот файлы для моего SPI Flash Loader'а:
LPC1110FD20_SRAM.icfКод
/*###ICF### Section handled by ICF editor, don't touch! ****/
/*-Editor annotation file-*/
/* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
/*-Specials-*/
define symbol __ICFEDIT_intvec_start__ = 0x10000000;
/*-Memory Regions-*/
define symbol __ICFEDIT_region_ROM_start__ = 0x10000020;
define symbol __ICFEDIT_region_ROM_end__ = 0x1000020F;
define symbol __ICFEDIT_region_RAM_start__ = 0x10000210;
define symbol __ICFEDIT_region_RAM_end__ = 0x100003DF;
/*-Sizes-*/
define symbol __ICFEDIT_size_cstack__ = 0xA0;
define symbol __ICFEDIT_size_heap__ = 0x00;
/**** End of ICF editor section. ###ICF###*/
define symbol __CRP_start__ = 0x000002FC;
define symbol __CRP_end__ = 0x000002FF;
define memory mem with size = 4G;
define region ROM_region = mem:[from __ICFEDIT_region_ROM_start__ to __ICFEDIT_region_ROM_end__] - mem:[from 0x10000050 to 0x10000060];
define region RAM_region = mem:[from __ICFEDIT_region_RAM_start__ to __ICFEDIT_region_RAM_end__];
define region CRP_region = mem:[from __CRP_start__ to __CRP_end__];
define block CSTACK with alignment = 8, size = __ICFEDIT_size_cstack__ { };
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
initialize by copy { readwrite };
do not initialize { section .noinit };
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
place in ROM_region { readonly };
place in RAM_region { readwrite,
block CSTACK, block HEAP };
place in CRP_region { section .crp };
cstartup.sКод
MODULE ?cstartup
;; Forward declaration of sections.
SECTION CSTACK:DATA:NOROOT(3)
SECTION .intvec:CODE:NOROOT(2)
EXTERN __iar_program_start
PUBLIC __vector_table
DATA
__vector_table
DCD sfe(CSTACK) ; Top of Stack
DCD __iar_program_start ; Reset Handler
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0 ; NXP vector table CRC position
END
main.cppКод
//------------------------------------------------------------------------------
#include <intrinsics.h>
#include <nxp/iolpc1110fd20.h>
//------------------------------------------------------------------------------
#define PAGE_WORDS (256 / 2)
#define PAGES_COUNT (16)
#define IAP_LOCATION (0x1fff1ff1)
#define IRC_IN_KHZ (12000)
#define C_PREPARE_SECTOR (50)
#define C_COPY_RAM_TO_FLASH (51)
#define C_ERASE_SECTOR (52)
#define C_BLANK_CHECK_SECTOR (53)
#define C_READ_PART_ID (54)
#define C_READ_BOOT_CODE_REV (55)
#define C_COMPARE (56)
#define C_REINVOKE_ISP (57)
#define C_READ_UID (58)
#define S_CMD_SUCCESS (0)
#define S_INVALID_COMMAND (1)
#define S_SRC_ADDR_ERROR (2)
#define S_DST_ADDR_ERROR (3)
#define S_SRC_ADDR_NOT_MAPPED (4)
#define S_DST_ADDR_NOT_MAPPED (5)
#define S_COUNT_ERROR (6)
#define S_INVALID_SECTOR (7)
#define S_SECTOR_NOT_BLANK (8)
#define S_SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION (9)
#define S_COMPARE_ERROR (10)
#define S_BUSY (11)
#define S_INVALD_PARAM (12)
//------------------------------------------------------------------------------
typedef unsigned int uint32;
typedef unsigned short int uint16;
typedef unsigned char uint8;
typedef void (*IAP)(uint32 *, uint32 *);
//------------------------------------------------------------------------------
__no_init uint16 buf[PAGE_WORDS];
__no_init uint32 command_param[5];
__no_init uint32 status_result[4];
IAP iap_entry = (IAP)IAP_LOCATION;
uint16 word = 0;
uint16 page = 0;
//------------------------------------------------------------------------------
void main(void)
{
__disable_interrupt();
SYSMEMREMAP = 0x01; // SRAM remap
// SYS, ROM, RAM, FLASHREG, FLASHARRAY, GPIO, SSP0, IOCON
SYSAHBCLKCTRL |= 0x1085F;
IOCON_PIO0_2 = 1; // SSEL0
IOCON_SCK_LOC = 2; // Selects SCK0 function in pin location PIO0_6/SCK0
IOCON_PIO0_6 = 2; // SCK0
IOCON_PIO0_8 = 1; // MISO0
IOCON_PIO0_9 = 1; // MOSI0
PRESETCTRL = 0x01; // deassert SSP0 reset
SSP0CLKDIV = 1; // div = 1
SSP0CPSR = 2; // div = 2
SSP0CR0 = 0xF; // data bits = 16, pol=0, phase=0
SSP0CR1 = (1 << 1) | (1 << 2); // enable in slave mode;
command_param[0] = C_PREPARE_SECTOR;
command_param[1] = 0;
command_param[2] = 0;
command_param[3] = 0;
command_param[4] = 0;
iap_entry(command_param, status_result);
command_param[0] = C_ERASE_SECTOR;
command_param[1] = 0;
command_param[2] = 0;
command_param[3] = IRC_IN_KHZ;
command_param[4] = 0;
iap_entry(command_param, status_result);
while(true)
{
if(SSP0SR & (1 << 2))
{
buf[word] = SSP0DR;
SSP0DR = buf[word];
word++;
if(word == PAGE_WORDS)
{
command_param[0] = C_PREPARE_SECTOR;
command_param[1] = 0;
command_param[2] = 0;
command_param[3] = 0;
command_param[4] = 0;
iap_entry(command_param, status_result);
command_param[0] = C_COPY_RAM_TO_FLASH;
command_param[1] = page * PAGE_WORDS * 2;
command_param[2] = (uint32)buf;
command_param[3] = PAGE_WORDS * 2;
command_param[4] = IRC_IN_KHZ;
iap_entry(command_param, status_result);
page++;
if(page == PAGES_COUNT) while(true); // end of flash
word = 0;
}
}
}
}
//------------------------------------------------------------------------------
Для отладки делаю так:
1) компилирую в IAR'е конечную прошивку для LPC1110 (жму F7);
2) загружаю и запускаю отладку в SRAM программы FlashLoader'а в другом окне IAR'а;
3) потом отправляю с компа прошивку по UART в другой проц, который после приёма кидает её в LPC1110 по SPI;
4) останавливаю отладку flash loadera и в первом окне IAR'а с конечной программой жму "Debug without downloading".
5) отлаживаю нужную программу.
Весь процесс до начала отладки занимает секунд 10, не идеально, но всего в пару кликов и с возможностью отладки.