diff --git a/flashloaders/stm32l4.s b/flashloaders/stm32l4.s new file mode 100644 index 000000000..4b908633d --- /dev/null +++ b/flashloaders/stm32l4.s @@ -0,0 +1,39 @@ +.global start +.syntax unified + +@ Adapted from stm32f4.s +@ STM32L4's flash controller expects double-word writes, has the flash +@ controller mapped in a different location with the registers we care about +@ moved down from the base address, and has BSY moved to bit 16 of SR. +@ r0 = source +@ r1 = target +@ r2 = wordcount +@ r3 = flash_base +@ r4 = temp +@ r5 = temp + +start: + ldr r3, flash_base +next: + cbz r2, done + ldr r4, [r0] /* copy doubleword from source to target */ + ldr r5, [r0, #4] + str r4, [r1] + str r5, [r1, #4] + +wait: + ldrh r4, [r3, #0x12] /* high half of status register */ + tst r4, #1 /* BSY = bit 16 */ + bne wait + + add r0, #8 + add r1, #8 + sub r2, #2 + b next +done: + bkpt + +.align 2 + +flash_base: + .word 0x40022000 diff --git a/src/stlink-common.c b/src/stlink-common.c index 343795aeb..8c9d4090a 100644 --- a/src/stlink-common.c +++ b/src/stlink-common.c @@ -63,6 +63,32 @@ #define FLASH_L1_FPRG 10 #define FLASH_L1_PROG 3 +//32L4 register base is at FLASH_REGS_ADDR (0x40022000) +#define STM32L4_FLASH_KEYR (FLASH_REGS_ADDR + 0x08) +#define STM32L4_FLASH_SR (FLASH_REGS_ADDR + 0x10) +#define STM32L4_FLASH_CR (FLASH_REGS_ADDR + 0x14) +#define STM32L4_FLASH_OPTR (FLASH_REGS_ADDR + 0x20) + +#define STM32L4_FLASH_SR_BSY 16 +#define STM32L4_FLASH_SR_ERRMASK 0x3f8 /* SR [9:3] */ + +#define STM32L4_FLASH_CR_LOCK 31 /* Lock control register */ +#define STM32L4_FLASH_CR_PG 0 /* Program */ +#define STM32L4_FLASH_CR_PER 1 /* Page erase */ +#define STM32L4_FLASH_CR_MER1 2 /* Bank 1 erase */ +#define STM32L4_FLASH_CR_MER2 15 /* Bank 2 erase */ +#define STM32L4_FLASH_CR_STRT 16 /* Start command */ +#define STM32L4_FLASH_CR_BKER 11 /* Bank select for page erase */ +#define STM32L4_FLASH_CR_PNB 3 /* Page number (8 bits) */ +// Bits requesting flash operations (useful when we want to clear them) +#define STM32L4_FLASH_CR_OPBITS \ + ((1lu<chip_id == STM32_CHIPID_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) || (sl->chip_id == STM32_CHIPID_F446)) res = stlink_read_debug32(sl, FLASH_F4_CR); + else if (sl->chip_id == STM32_CHIPID_L4) + res = stlink_read_debug32(sl, STM32L4_FLASH_CR); else res = stlink_read_debug32(sl, FLASH_CR); #if DEBUG_FLASH @@ -166,12 +194,16 @@ static inline uint32_t read_flash_cr(stlink_t *sl) { static inline unsigned int is_flash_locked(stlink_t *sl) { /* return non zero for true */ + uint32_t cr = read_flash_cr(sl); + if ((sl->chip_id == STM32_CHIPID_F2) || (sl->chip_id == STM32_CHIPID_F4) || (sl->chip_id == STM32_CHIPID_F4_DE) || (sl->chip_id == STM32_CHIPID_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) || (sl->chip_id == STM32_CHIPID_F446)) - return read_flash_cr(sl) & (1 << FLASH_F4_CR_LOCK); + return cr & (1 << FLASH_F4_CR_LOCK); + else if (sl->chip_id == STM32_CHIPID_L4) + return cr & (1lu << STM32L4_FLASH_CR_LOCK); else - return read_flash_cr(sl) & (1 << FLASH_CR_LOCK); + return cr & (1 << FLASH_CR_LOCK); } static void unlock_flash(stlink_t *sl) { @@ -185,6 +217,9 @@ static void unlock_flash(stlink_t *sl) { (sl->chip_id == STM32_CHIPID_F446)) { stlink_write_debug32(sl, FLASH_F4_KEYR, FLASH_KEY1); stlink_write_debug32(sl, FLASH_F4_KEYR, FLASH_KEY2); + } else if (sl->chip_id == STM32_CHIPID_L4) { + stlink_write_debug32(sl, STM32L4_FLASH_KEYR, FLASH_KEY1); + stlink_write_debug32(sl, STM32L4_FLASH_KEYR, FLASH_KEY2); } else { stlink_write_debug32(sl, FLASH_KEYR, FLASH_KEY1); stlink_write_debug32(sl, FLASH_KEYR, FLASH_KEY2); @@ -212,6 +247,9 @@ static void lock_flash(stlink_t *sl) { (sl->chip_id == STM32_CHIPID_F446)) { const uint32_t n = read_flash_cr(sl) | (1 << FLASH_F4_CR_LOCK); stlink_write_debug32(sl, FLASH_F4_CR, n); + } else if (sl->chip_id == STM32_CHIPID_L4) { + const uint32_t n = read_flash_cr(sl) | (1lu << STM32L4_FLASH_CR_LOCK); + stlink_write_debug32(sl, STM32L4_FLASH_CR, n); } else { /* write to 1 only. reset by hw at unlock sequence */ const uint32_t n = read_flash_cr(sl) | (1 << FLASH_CR_LOCK); @@ -227,6 +265,11 @@ static void set_flash_cr_pg(stlink_t *sl) { uint32_t x = read_flash_cr(sl); x |= (1 << FLASH_CR_PG); stlink_write_debug32(sl, FLASH_F4_CR, x); + } else if (sl->chip_id == STM32_CHIPID_L4) { + uint32_t x = read_flash_cr(sl); + x &=~ STM32L4_FLASH_CR_OPBITS; + x |= (1 << STM32L4_FLASH_CR_PG); + stlink_write_debug32(sl, STM32L4_FLASH_CR, x); } else { const uint32_t n = 1 << FLASH_CR_PG; stlink_write_debug32(sl, FLASH_CR, n); @@ -259,7 +302,12 @@ static void set_flash_cr_mer(stlink_t *sl) { (sl->chip_id == STM32_CHIPID_F446)) stlink_write_debug32(sl, FLASH_F4_CR, stlink_read_debug32(sl, FLASH_F4_CR) | (1 << FLASH_CR_MER)); - else + else if (sl->chip_id == STM32_CHIPID_L4) { + uint32_t x = stlink_read_debug32(sl, STM32L4_FLASH_CR); + x &=~ STM32L4_FLASH_CR_OPBITS; + x |= (1lu << STM32L4_FLASH_CR_MER1) | (1lu << STM32L4_FLASH_CR_MER2); + stlink_write_debug32(sl, STM32L4_FLASH_CR, x); + } else stlink_write_debug32(sl, FLASH_CR, stlink_read_debug32(sl, FLASH_CR) | (1 << FLASH_CR_MER)); } @@ -282,6 +330,10 @@ static void set_flash_cr_strt(stlink_t *sl) { uint32_t x = read_flash_cr(sl); x |= (1 << FLASH_F4_CR_STRT); stlink_write_debug32(sl, FLASH_F4_CR, x); + } else if (sl->chip_id == STM32_CHIPID_L4) { + uint32_t x = read_flash_cr(sl); + x |= (1lu << STM32L4_FLASH_CR_STRT); + stlink_write_debug32(sl, STM32L4_FLASH_CR, x); } else { stlink_write_debug32(sl, FLASH_CR, stlink_read_debug32(sl, FLASH_CR) | (1 << FLASH_CR_STRT) ); @@ -298,6 +350,8 @@ static inline uint32_t read_flash_sr(stlink_t *sl) { (sl->chip_id == STM32_CHIPID_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) || (sl->chip_id == STM32_CHIPID_F446)) res = stlink_read_debug32(sl, FLASH_F4_SR); + else if (sl->chip_id == STM32_CHIPID_L4) + res = stlink_read_debug32(sl, STM32L4_FLASH_SR); else res = stlink_read_debug32(sl, FLASH_SR); //fprintf(stdout, "SR:0x%x\n", *(uint32_t*) sl->q_buf); @@ -309,6 +363,8 @@ static inline unsigned int is_flash_busy(stlink_t *sl) { (sl->chip_id == STM32_CHIPID_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) || (sl->chip_id == STM32_CHIPID_F446)) return read_flash_sr(sl) & (1 << FLASH_F4_SR_BSY); + else if (sl->chip_id == STM32_CHIPID_L4) + return read_flash_sr(sl) & (1 << STM32L4_FLASH_SR_BSY); else return read_flash_sr(sl) & (1 << FLASH_SR_BSY); } @@ -375,6 +431,18 @@ static inline void write_flash_cr_snb(stlink_t *sl, uint32_t n) { stlink_write_debug32(sl, FLASH_F4_CR, x); } +static inline void write_flash_cr_bker_pnb(stlink_t *sl, uint32_t n) { + uint32_t x = read_flash_cr(sl); + x &=~ STM32L4_FLASH_CR_OPBITS; + x &=~ STM32L4_FLASH_CR_PAGEMASK; + x |= (n << STM32L4_FLASH_CR_PNB); + x |= (1lu << STM32L4_FLASH_CR_PER); +#if DEBUG_FLASH + fprintf(stdout, "BKER:PNB:0x%x 0x%x\n", x, n); +#endif + stlink_write_debug32(sl, STM32L4_FLASH_CR, x); +} + // Delegates to the backends... void stlink_close(stlink_t *sl) { @@ -1036,6 +1104,23 @@ uint32_t calculate_F4_sectornum(uint32_t flashaddr){ } +// Returns BKER:PNB for the given page address +uint32_t calculate_L4_page(stlink_t *sl, uint32_t flashaddr) { + uint32_t bker = 0; + uint32_t flashopt = stlink_read_debug32(sl, STM32L4_FLASH_OPTR); + flashaddr -= STM32_FLASH_BASE; + if (flashopt & (1lu << STM32L4_FLASH_OPTR_DUALBANK)) { + uint32_t banksize = sl->flash_size / 2; + if (flashaddr > banksize) { + flashaddr -= banksize; + bker = 0x100; + } + } + // For 1MB chips without the dual-bank option set, the page address will + // overflow into the BKER bit, which gives us the correct bank:page value. + return bker | flashaddr/sl->flash_pgsz; +} + uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr){ if ((sl->chip_id == STM32_CHIPID_F2) || (sl->chip_id == STM32_CHIPID_F4) || (sl->chip_id == STM32_CHIPID_F4_DE) || (sl->chip_id == STM32_CHIPID_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) || @@ -1061,7 +1146,7 @@ int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr) { if ((sl->chip_id == STM32_CHIPID_F2) || (sl->chip_id == STM32_CHIPID_F4) || (sl->chip_id == STM32_CHIPID_F4_DE) || (sl->chip_id == STM32_CHIPID_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) || - (sl->chip_id == STM32_CHIPID_F446)) { + (sl->chip_id == STM32_CHIPID_F446) || (sl->chip_id == STM32_CHIPID_L4)) { /* wait for ongoing op to finish */ wait_flash_busy(sl); @@ -1069,15 +1154,22 @@ int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr) unlock_flash_if(sl); /* select the page to erase */ - // calculate the actual page from the address - uint32_t sector=calculate_F4_sectornum(flashaddr); + if (sl->chip_id == STM32_CHIPID_L4) { + // calculate the actual bank+page from the address + uint32_t page = calculate_L4_page(sl, flashaddr); + + write_flash_cr_bker_pnb(sl, page); + } else { + // calculate the actual page from the address + uint32_t sector=calculate_F4_sectornum(flashaddr); - fprintf(stderr, "EraseFlash - Sector:0x%x Size:0x%x\n", sector, stlink_calculate_pagesize(sl, flashaddr)); + fprintf(stderr, "EraseFlash - Sector:0x%x Size:0x%x\n", sector, stlink_calculate_pagesize(sl, flashaddr)); - //the SNB values for flash sectors in the second bank do not directly follow the values for the first bank on 2mb devices... - if (sector >= 12) sector += 4; + //the SNB values for flash sectors in the second bank do not directly follow the values for the first bank on 2mb devices... + if (sector >= 12) sector += 4; - write_flash_cr_snb(sl, sector); + write_flash_cr_snb(sl, sector); + } /* start erase operation */ set_flash_cr_strt(sl); @@ -1411,6 +1503,25 @@ int write_loader_to_sram(stlink_t *sl, stm32_addr_t* addr, size_t* size) { 0x00, 0x3c, 0x02, 0x40, }; + static const uint8_t loader_code_stm32l4[] = { + // flashloaders/stm32l4.s + 0x08, 0x4b, // start: ldr r3, [pc, #32] ; + 0x72, 0xb1, // next: cbz r2, + 0x04, 0x68, // ldr r4, [r0, #0] + 0x45, 0x68, // ldr r5, [r0, #4] + 0x0c, 0x60, // str r4, [r1, #0] + 0x4d, 0x60, // str r5, [r1, #4] + 0x5c, 0x8a, // wait: ldrh r4, [r3, #18] + 0x14, 0xf0, 0x01, 0x0f, // tst.w r4, #1 + 0xfb, 0xd1, // bne.n + 0x00, 0xf1, 0x08, 0x00, // add.w r0, r0, #8 + 0x01, 0xf1, 0x08, 0x01, // add.w r1, r1, #8 + 0xa2, 0xf1, 0x02, 0x02, // add.w r2, r2, #2 + 0xef, 0xe7, // b.n + 0x00, 0xbe, // done: bkpt 0x0000 + 0x00, 0x20, 0x02, 0x40 // flash_base: .word 0x40022000 + }; + const uint8_t* loader_code; size_t loader_size; @@ -1443,6 +1554,9 @@ int write_loader_to_sram(stlink_t *sl, stm32_addr_t* addr, size_t* size) { } else if (sl->chip_id == STM32_CHIPID_L0) { loader_code = loader_code_stm32l0; loader_size = sizeof(loader_code_stm32l0); + } else if (sl->chip_id == STM32_CHIPID_L4) { + loader_code = loader_code_stm32l4; + loader_size = sizeof(loader_code_stm32l4); } else { ELOG("unknown coreid, not sure what flash loader to use, aborting!: %x\n", sl->core_id); return -1; @@ -1618,10 +1732,11 @@ int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t (sl->chip_id == STM32_CHIPID_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) || - (sl->chip_id == STM32_CHIPID_F446)) { + (sl->chip_id == STM32_CHIPID_F446) || + (sl->chip_id == STM32_CHIPID_L4)) { /* todo: check write operation */ - ILOG("Starting Flash write for F2/F4\n"); + ILOG("Starting Flash write for F2/F4/L4\n"); /* flash loader initialization */ if (init_flash_loader(sl, &fl) == -1) { ELOG("init_flash_loader() == -1\n"); @@ -1632,14 +1747,23 @@ int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t unlock_flash_if(sl); /* TODO: Check that Voltage range is 2.7 - 3.6 V */ - /* set parallelisim to 32 bit*/ - int voltage = stlink_target_voltage(sl); - if (voltage > 2700) { - printf("enabling 32-bit flash writes\n"); - write_flash_cr_psiz(sl, 2); + if (sl->chip_id != STM32_CHIPID_L4) { + /* set parallelisim to 32 bit*/ + int voltage = stlink_target_voltage(sl); + if (voltage > 2700) { + printf("enabling 32-bit flash writes\n"); + write_flash_cr_psiz(sl, 2); + } else { + printf("Target voltage (%d mV) too low for 32-bit flash, using 8-bit flash writes\n", voltage); + write_flash_cr_psiz(sl, 0); + } } else { - printf("Target voltage (%d mV) too low for 32-bit flash, using 8-bit flash writes\n", voltage); - write_flash_cr_psiz(sl, 0); + /* L4 does not have a byte-write mode */ + int voltage = stlink_target_voltage(sl); + if (voltage <= 2700) { + printf("Target voltage (%d mV) too low for flash writes!\n", voltage); + return -1; + } } /* set programming mode */ @@ -1868,10 +1992,13 @@ int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, cons } else if (sl->chip_id == STM32_CHIPID_F2 || sl->chip_id == STM32_CHIPID_F4 || (sl->chip_id == STM32_CHIPID_F4_DE) || sl->chip_id == STM32_CHIPID_F4_LP || sl->chip_id == STM32_CHIPID_F4_HD || (sl->chip_id == STM32_CHIPID_F411RE) || - (sl->chip_id == STM32_CHIPID_F446)) { + (sl->chip_id == STM32_CHIPID_F446) || (sl->chip_id == STM32_CHIPID_L4)) { size_t count = size / sizeof(uint32_t); if (size % sizeof(uint32_t)) ++count; + if (sl->chip_id == STM32_CHIPID_L4) { + if (count % 2) ++count; + } /* setup core */ stlink_write_reg(sl, fl->buf_addr, 0); /* source */ @@ -1929,7 +2056,7 @@ int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, cons } else if (sl->chip_id == STM32_CHIPID_F2 || sl->chip_id == STM32_CHIPID_F4 || (sl->chip_id == STM32_CHIPID_F4_DE) || sl->chip_id == STM32_CHIPID_F4_LP || sl->chip_id == STM32_CHIPID_F4_HD || (sl->chip_id == STM32_CHIPID_F411RE) || - (sl->chip_id == STM32_CHIPID_F446)) { + (sl->chip_id == STM32_CHIPID_F446) || (sl->chip_id == STM32_CHIPID_L4)) { stlink_read_reg(sl, 2, &rr); if (rr.r[2] != 0) { diff --git a/src/stlink-common.h b/src/stlink-common.h index c3ac48d03..3fceeef62 100644 --- a/src/stlink-common.h +++ b/src/stlink-common.h @@ -103,7 +103,7 @@ extern "C" { #define STM32_CHIPID_F1_LOW 0x412 #define STM32_CHIPID_F4 0x413 #define STM32_CHIPID_F1_HIGH 0x414 - +#define STM32_CHIPID_L4 0x415 /* Seen on L4x6 (RM0351) */ #define STM32_CHIPID_L1_MEDIUM 0x416 #define STM32_CHIPID_L0 0x417 #define STM32_CHIPID_F1_CONN 0x418 @@ -466,6 +466,20 @@ extern "C" { .bootrom_base = 0x1fffd800, // 3.3.2 / Table 4 System Memory .bootrom_size = 0x2000 }, + { + // STM32L4x6 + // From RM0351. + .chip_id = STM32_CHIPID_L4, + .description = "L4 device", + .flash_size_reg = 0x1fff75e0, // "Flash size data register" (sec 45.2, page 1671) + .flash_pagesize = 0x800, // 2K (sec 3.2, page 78; also appears in sec 3.3.1 and tables 4-6 on pages 79-81) + // SRAM1 is "up to" 96k in the standard Cortex-M memory map; + // SRAM2 is 32k mapped at at 0x10000000 (sec 2.3, page 73 for + // sizes; table 2, page 74 for SRAM2 location) + .sram_size = 0x18000, + .bootrom_base = 0x1fff0000, // Tables 4-6, pages 80-81 (Bank 1 system memory) + .bootrom_size = 0x7000 // 28k (per bank), same source as base + }, };