Merge pull request #1466 from dbeinder/stm32wb0

[feature] STM32 flash type implementation for WB05, WB06/07, WB09, WL3x
testing
nightwalker-87 2025-04-17 23:04:13 +02:00 zatwierdzone przez GitHub
commit 5259daa4bf
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
15 zmienionych plików z 418 dodań i 29 usunięć

Wyświetl plik

@ -0,0 +1,15 @@
# Chip-ID file for STM32WB05 (formerly BlueNRG-LPS) device
#
dev_type STM32WB05
ref_manual_id 0491
chip_id 0x028 // STM32_CHIPID_WB05
flash_type WB0
flash_size_reg 0x40001014
flash_pagesize 0x800 // 2 KB
sram_size 0x6000 // 24 KB (no variants)
bootrom_base 0x0
bootrom_size 0x4000 // 16 KB
option_base 0x0 // protection via FLASH controller commands
option_size 0x0
otp_base 0x10001800
otp_size 0x400 // 1 KB

Wyświetl plik

@ -0,0 +1,15 @@
# Chip-ID file for STM32WB06 / STM32WB07 (formerly BlueNRG-LP) device
#
dev_type STM32WB06_WB07
ref_manual_id 0530 // also RM0479
chip_id 0x01e // STM32_CHIPID_WB06_WB07
flash_type WB0
flash_size_reg 0x40001014
flash_pagesize 0x800 // 2 KB
sram_size 0x10000 // 64 KB
bootrom_base 0x0
bootrom_size 0x4000 // 16 KB
option_base 0x0 // protection via FLASH controller commands
option_size 0x0
otp_base 0x10001800
otp_size 0x400 // 1 KB

Wyświetl plik

@ -0,0 +1,15 @@
# Chip-ID file for STM32WB09 device
#
dev_type STM32WB06_WB07
ref_manual_id 0505
chip_id 0x032 // STM32_CHIPID_WB09
flash_type WB0
flash_size_reg 0x40001014
flash_pagesize 0x800 // 2 KB
sram_size 0x10000 // 64 KB
bootrom_base 0x0
bootrom_size 0x4000 // 16 KB
option_base 0x0 // protection via FLASH controller commands
option_size 0x0
otp_base 0x10001800
otp_size 0x400 // 1 KB

Wyświetl plik

@ -0,0 +1,15 @@
# Chip-ID file for STM32WL3x device
#
dev_type WL3x
ref_manual_id 0491
chip_id 0x027 // STM32_CHIPID_WL3x
flash_type WB0
flash_size_reg 0x40001014
flash_pagesize 0x800 // 2 KB
sram_size 0x8000 // 32 KB
bootrom_base 0x0
bootrom_size 0x4000 // 16 KB
option_base 0x0 // protection via FLASH controller commands
option_size 0x0
otp_base 0x10001800
otp_size 0x400 // 1 KB

Wyświetl plik

@ -12,7 +12,7 @@ XXDFLAGS = -i -c 4
CFLAGS_ARMV6_M = -mcpu=Cortex-M0 -Tlinker.ld -ffreestanding -nostdlib
CFLAGS_ARMV7_M = -mcpu=Cortex-M3 -Tlinker.ld -ffreestanding -nostdlib
all: stm32vl.h stm32f0.h stm32lx.h stm32f4.h stm32f4lv.h stm32l4.h stm32f7.h stm32f7lv.h
all: stm32vl.h stm32f0.h stm32wb0.h stm32lx.h stm32f4.h stm32f4lv.h stm32l4.h stm32f7.h stm32f7lv.h
%.h: %.bin
@ -26,6 +26,10 @@ all: stm32vl.h stm32f0.h stm32lx.h stm32f4.h stm32f4lv.h stm32l4.h stm32f7.h stm
stm32f0.o: stm32f0.s
$(CC) stm32f0.s $(CFLAGS_ARMV6_M) -o stm32f0.o
# separate rule for STM32WB0 (Cortex M0+)
stm32wb0.o: stm32wb0.s
$(CC) stm32wb0.s $(CFLAGS_ARMV6_M) -o stm32wb0.o
# separate rule for STM32F1/F3
stm32vl.o: stm32f0.s
$(CC) stm32f0.s $(CFLAGS_ARMV7_M) -o stm32vl.o

Wyświetl plik

@ -40,6 +40,23 @@ How to wait for the write process: Read a word from FLASH_SR, loop until the bus
Exit: After the copying process and before triggering the breakpoint, clear the PG bit in FLASH_CR.
## stm32wb0.s
`flash_origin`: 0x10040000 (start of flash in memory map)
`flash_base`: 0x40001000 (base address of the `FLASH` controller peripheral)
**Reference**:
[STM32WB05 RM0491](https://www.st.com/resource/en/reference_manual/rm0491-the-bluenrglps-arm-cortex-m0based-stmicroelectronics.pdf)
[STM32WB06/07 RM0530](https://www.st.com/resource/en/reference_manual/rm0530--stm32wb07xc-and-stm32wb06xc-ultralow-power-wireless-32bit-mcus-armbased-cortexm0-with-bluetooth-low-energy-and-24-ghz-radio-solution-stmicroelectronics.pdf)
[STM32WB09 RM0505](https://www.st.com/resource/en/reference_manual/rm0505-stm32wb09xe-ultralow-power-wireless-32bit-mcu-armbased-cortexm0-with-bluetooth-low-energy-and-24-ghz-radio-solution-stmicroelectronics.pdf)
[STM32WL3x RM0511](https://www.st.com/resource/en/reference_manual/rm0511-stm32wl33xx-armbased-wireless-mcus-with-subghz-radio-solution-stmicroelectronics.pdf)
**Special requirements**:
Data is flashed in blocks of 16 bytes, so the destination address must be aligned to 16, and the length must be a multiple of 16 bytes. In contrast to other STM32 devices, writing happens through registers of the `FLASH` peripheral. The loader exits early if an error is encountered - `r2` holds the number that didn't get written.
## stm32f4.s
`flash_base`: 0x40023c00

Wyświetl plik

@ -0,0 +1,73 @@
.syntax unified
.text
/*
* Arguments:
* r0 - source memory ptr
* r1 - target memory ptr
* r2 - count of bytes
* Register usage:
* r0 - SRAM source pointer (incrementing)
* r1 - FLASH word address (incrementing)
* r2 - remaining bytes count (decrementing)
* r3 - scratch
* r4 - scratch
* r5 - scratch
* r6 - scratch
* r7 - FLASH peripheral pointer
*/
.global copy
copy:
# subtract flash_origin and divide by 4, to get a FLASH word address
ldr r3, flash_origin
subs r1, r3
lsrs r1, #2
# load address of FLASH peripheral
ldr r7, flash_base
loop:
# fill ADDRESS (@ 0x18)
str r1, [r7, #0x18]
# fill DATA0-3 (@ 0x40 - 0x4C)
adds r7, #0x40
ldmia r0!, { r3, r4, r5, r6 }
stmia r7!, { r3, r4, r5, r6 }
subs r7, #0x50
# clear CMDDONE, CMDERR and ILLCMD in IRQRAW (@0x10)
movs r5, #0x0D
str r5, [r7, #0x10]
# write BURSTWRITE to COMMAND (@ 0x00)
movs r4, #0xCC
str r4, [r7]
wait:
# wait until CMDDONE, CMDERR or ILLCMD is set in IRQRAW
ldr r4, [r7, #0x10]
ands r4, r5
beq wait
# remove the lowest bit (CMDDONE), exit if error bits remain
lsrs r4, #1
bne exit
# add 4 words to ADDRESS, subtract 16 bytes from count
adds r1, #4
subs r2, #16
# loop if count > 0
bgt loop
exit:
bkpt
.align 2
flash_base:
.word 0x40001000
flash_origin:
.word 0x10040000

Wyświetl plik

@ -265,7 +265,7 @@ int32_t stlink_cpu_id(stlink_t *sl, cortex_m3_cpuid_t *cpuid);
uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr);
void stlink_print_data(stlink_t *sl);
bool stlink_is_core_halted(stlink_t *sl);
int32_t write_buffer_to_sram(stlink_t *sl, flash_loader_t* fl, const uint8_t* buf, uint16_t size);
int32_t write_buffer_to_sram(stlink_t *sl, flash_loader_t* fl, const uint8_t* buf, uint16_t size, uint16_t padded_size);
int32_t stlink_fread(stlink_t* sl, const char* path, bool is_ihex, stm32_addr_t addr, uint32_t size);
// int32_t stlink_chip_id(stlink_t *sl, uint32_t *chip_id);
int32_t stlink_load_device_params(stlink_t *sl);

Wyświetl plik

@ -63,6 +63,7 @@ enum stm32_flash_type {
STM32_FLASH_TYPE_L4 = 10,
STM32_FLASH_TYPE_L5_U5_H5 = 11,
STM32_FLASH_TYPE_WB_WL = 12,
STM32_FLASH_TYPE_WB0 = 13,
};
/* STM32 chip-ids */
@ -72,6 +73,10 @@ enum stm32_flash_type {
enum stm32_chipids {
STM32_CHIPID_UNKNOWN = 0x000,
STM32_CHIPID_WL3x = 0x027, /* RM0511, p.176 PART_NUMBER in JTAG_ID */
STM32_CHIPID_WB05 = 0x028, /* RM0491, p.116 PART_NUMBER in JTAG_ID */
STM32_CHIPID_WB06_WB07 = 0x01e, /* RM0530, p.125 PART_NUMBER in JTAG_ID */
STM32_CHIPID_WB09 = 0x032, /* RM0505, p.167 PART_NUMBER in JTAG_ID */
STM32_CHIPID_F1_MD = 0x410, /* medium density */
STM32_CHIPID_F2 = 0x411,
STM32_CHIPID_F1_LD = 0x412, /* low density */
@ -232,6 +237,14 @@ enum stm32_chipids {
#define STM32WB_RCC_AHB1ENR 0x58000048
#define STM32WB_RCC_DMAEN 0x00000003 // DMA2EN | DMA1EN
#define STM32WB0_FLASH_BASE 0x10040000
#define STM32WB0_JTAG_ID 0x40000004
#define STM32WB0_JTAG_PART_NR (12)
#define STM32WB0_RCC_AHBENR 0x48400050
#define STM32WB0_RCC_AHB_DMAEN 0x00000001
#define STM32WB0_RCC_APB0ENR 0x48400054
#define STM32WB0_RCC_APB0_WDGEN 0x00004000
#define STM32L5_PWR_CR1 0x40007000 // RM0438, p. 93,324
#define STM32L5_PWR_CR1_VOS 9

Wyświetl plik

@ -398,6 +398,54 @@
#define STM32_FLASH_L5_NSSR_BSY 16 /* Busy */
#define STM32_FLASH_L5_NSSR_ERROR_MASK (0x20fa)
// == STM32WB0 == (RM0530)
// WB0 FLASH registers
#define STM32_FLASH_WB0_REGS_ADDR ((uint32_t) 0x40001000)
#define STM32_FLASH_WB0_COMMAND (STM32_FLASH_WB0_REGS_ADDR + 0x00)
#define STM32_FLASH_WB0_CONFIG (STM32_FLASH_WB0_REGS_ADDR + 0x04)
#define STM32_FLASH_WB0_IRQSTAT (STM32_FLASH_WB0_REGS_ADDR + 0x08)
#define STM32_FLASH_WB0_IRQMASK (STM32_FLASH_WB0_REGS_ADDR + 0x0c)
#define STM32_FLASH_WB0_IRQRAW (STM32_FLASH_WB0_REGS_ADDR + 0x10)
#define STM32_FLASH_WB0_FLASH_SIZE (STM32_FLASH_WB0_REGS_ADDR + 0x14)
#define STM32_FLASH_WB0_ADDRESS (STM32_FLASH_WB0_REGS_ADDR + 0x18)
#define STM32_FLASH_WB0_LFSRVAL (STM32_FLASH_WB0_REGS_ADDR + 0x24)
#define STM32_FLASH_WB0_PAGEPROT0 (STM32_FLASH_WB0_REGS_ADDR + 0x34)
#define STM32_FLASH_WB0_PAGEPROT1 (STM32_FLASH_WB0_REGS_ADDR + 0x38)
#define STM32_FLASH_WB0_DATA0 (STM32_FLASH_WB0_REGS_ADDR + 0x40)
#define STM32_FLASH_WB0_DATA1 (STM32_FLASH_WB0_REGS_ADDR + 0x44)
#define STM32_FLASH_WB0_DATA2 (STM32_FLASH_WB0_REGS_ADDR + 0x48)
#define STM32_FLASH_WB0_DATA3 (STM32_FLASH_WB0_REGS_ADDR + 0x4c)
// WB0 FLASH commands
#define STM32_FLASH_WB0_CMD_ERASE_PAGE (0x11)
#define STM32_FLASH_WB0_CMD_MASS_ERASE (0x22) //WB06,WB07,WB09 only
#define STM32_FLASH_WB0_CMD_WRITE (0x33)
#define STM32_FLASH_WB0_CMD_MASSREAD (0x55)
#define STM32_FLASH_WB0_CMD_BURSTWRITE (0xCC)
#define STM32_FLASH_WB0_CMD_OTPWRITE (0xEE)
#define STM32_FLASH_WB0_CMD_KEYWRITE (0xFF)
// FLASH_SIZE register bits
#define STM32_FLASH_WB0_RAM_SIZE (17)
#define STM32_FLASH_WB0_FLASH_SECURE (19)
#define STM32_FLASH_WB0_SWD_DISABLE (20)
// FLASH IRQ (status) bits
#define STM32_FLASH_WB0_IRQ_ILLCMD (1 << 3)
#define STM32_FLASH_WB0_IRQ_CMDERR (1 << 2)
#define STM32_FLASH_WB0_IRQ_CMDSTART (1 << 1)
#define STM32_FLASH_WB0_IRQ_CMDDONE (1 << 0)
#define STM32_FLASH_WB0_IRQ_ERR_MASK (STM32_FLASH_WB0_IRQ_CMDERR | STM32_FLASH_WB0_IRQ_ILLCMD)
#define STM32_FLASH_WB0_IRQ_ALL (STM32_FLASH_WB0_IRQ_CMDDONE | STM32_FLASH_WB0_IRQ_CMDSTART | STM32_FLASH_WB0_IRQ_ERR_MASK)
// key values, to be written into DATA0-3
#define STM32_FLASH_WB0_KEY01_UNLOCK (0xFFFFFFFF)
#define STM32_FLASH_WB0_KEY01_READOUT_PROT (0xAAAAAAAA)
#define STM32_FLASH_WB0_KEY01_SWD_DISABLED (0xABACABAD) /* irreversible protection level */
#define STM32_FLASH_WB0_KEY2 (0xC7EF584D)
#define STM32_FLASH_WB0_KEY3 (0xB3A21096)
// == STM32WB == (RM0434)
// WB Flash registers
#define STM32_FLASH_WB_REGS_ADDR ((uint32_t) 0x58004000)

Wyświetl plik

@ -56,6 +56,7 @@ static void usage(void) {
puts(" --connect-under-reset Pull reset low while connecting.");
puts(" --hot-plug Connect without reset.");
puts(" --reset Reset after writing.");
puts(" --mass-erase Mass erase before writing");
puts(" --format {binary|ihex} Format of file to read or write. When writing");
puts(" with ihex specifying addr is not needed.");
puts(" --flash <size> Specify size of flash, e.g. 128k, 1M.");

Wyświetl plik

@ -123,6 +123,8 @@ void process_chipfile(char *fname) {
ts->flash_type = STM32_FLASH_TYPE_L5_U5_H5;
} else if(strcmp(value, "WB_WL") == 0) {
ts->flash_type = STM32_FLASH_TYPE_WB_WL;
} else if(strcmp(value, "WB0") == 0) {
ts->flash_type = STM32_FLASH_TYPE_WB0;
} else {
ts->flash_type = STM32_FLASH_TYPE_UNKNOWN;
}

Wyświetl plik

@ -159,6 +159,8 @@ static inline int32_t write_flash_sr(stlink_t *sl, uint32_t bank, uint32_t val)
sr_reg = STM32_FLASH_L5_NSSR;
} else if(sl->flash_type == STM32_FLASH_TYPE_WB_WL) {
sr_reg = STM32_FLASH_WB_SR;
} else if(sl->flash_type == STM32_FLASH_TYPE_WB0) {
sr_reg = STM32_FLASH_WB0_IRQRAW;
} else {
ELOG("method 'write_flash_sr' is unsupported\n");
return (-1);
@ -207,6 +209,9 @@ void clear_flash_error(stlink_t *sl) {
case STM32_FLASH_TYPE_WB_WL:
write_flash_sr(sl, BANK_1, STM32_FLASH_WB_SR_ERROR_MASK);
break;
case STM32_FLASH_TYPE_WB0:
write_flash_sr(sl, BANK_1, STM32_FLASH_WB0_IRQ_ERR_MASK);
break;
default:
break;
}
@ -237,6 +242,8 @@ uint32_t read_flash_sr(stlink_t *sl, uint32_t bank) {
sr_reg = STM32_FLASH_L5_NSSR;
} else if(sl->flash_type == STM32_FLASH_TYPE_WB_WL) {
sr_reg = STM32_FLASH_WB_SR;
} else if(sl->flash_type == STM32_FLASH_TYPE_WB0) {
sr_reg = STM32_FLASH_WB0_IRQRAW;
} else {
ELOG("method 'read_flash_sr' is unsupported\n");
return (-1);
@ -271,6 +278,10 @@ uint32_t is_flash_busy(stlink_t *sl) {
sr_busy_shift = STM32_FLASH_L5_NSSR_BSY;
} else if(sl->flash_type == STM32_FLASH_TYPE_WB_WL) {
sr_busy_shift = STM32_FLASH_WB_SR_BSY;
} else if(sl->flash_type == STM32_FLASH_TYPE_WB0) {
res = read_flash_sr(sl, BANK_1);
uint32_t has_errors = res & STM32_FLASH_WB0_IRQ_ERR_MASK;
return !has_errors && ((~res) & STM32_FLASH_WB0_IRQ_CMDDONE);
} else {
ELOG("method 'is_flash_busy' is unsupported\n");
return (-1);
@ -371,6 +382,18 @@ int32_t check_flash_error(stlink_t *sl) {
PROGERR = (1 << STM32_FLASH_WB_SR_PROGERR);
PGAERR = (1 << STM32_FLASH_WB_SR_PGAERR);
break;
case STM32_FLASH_TYPE_WB0:
res = read_flash_sr(sl, BANK_1) & STM32_FLASH_WB0_IRQ_ERR_MASK;
if (res) {
if (res & STM32_FLASH_WB0_IRQ_CMDERR) {
ELOG("Internal error: COMMAND written while busy!\n");
}
if (res & STM32_FLASH_WB0_IRQ_ILLCMD) {
ELOG("FLASH has refused operation (write protected?)\n");
}
return -1;
}
break;
default:
break;
}
@ -432,6 +455,8 @@ static inline uint32_t is_flash_locked(stlink_t *sl) {
} else if(sl->flash_type == STM32_FLASH_TYPE_WB_WL) {
cr_reg = STM32_FLASH_WB_CR;
cr_lock_shift = STM32_FLASH_WB_CR_LOCK;
} else if(sl->flash_type == STM32_FLASH_TYPE_WB0) {
return 0;
} else {
ELOG("unsupported flash method, abort\n");
return (-1);
@ -1179,6 +1204,12 @@ int32_t stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr) {
wait_flash_busy(sl); // wait for the 'busy' bit to clear
clear_flash_cr_per(sl, BANK_1); // clear the 'enable page erase' bit
lock_flash(sl);
} else if(sl->flash_type == STM32_FLASH_TYPE_WB0) {
uint32_t flash_page = ((flashaddr - sl->flash_base) / sl->flash_pgsz);
stlink_write_debug32(sl, STM32_FLASH_WB0_IRQRAW, STM32_FLASH_WB0_IRQ_ALL);
stlink_write_debug32(sl, STM32_FLASH_WB0_ADDRESS, (flash_page * sl->flash_pgsz) >> 2);
stlink_write_debug32(sl, STM32_FLASH_WB0_COMMAND, STM32_FLASH_WB0_CMD_ERASE_PAGE);
wait_flash_busy(sl);
} else if(sl->flash_type == STM32_FLASH_TYPE_F0_F1_F3 ||
sl->flash_type == STM32_FLASH_TYPE_F1_XL) {
uint32_t bank = (flashaddr < STM32_F1_FLASH_BANK2_BASE) ? BANK_1 : BANK_2;
@ -1253,6 +1284,16 @@ int32_t stlink_erase_flash_mass(stlink_t *sl) {
err = stlink_erase_flash_section(sl, sl->flash_base, sl->flash_size, false);
} else if(sl->flash_type == STM32_FLASH_TYPE_WB0) {
if(sl->chip_id == STM32_CHIPID_WB06_WB07 || sl->chip_id == STM32_CHIPID_WB09) {
stlink_write_debug32(sl, STM32_FLASH_WB0_IRQRAW, STM32_FLASH_WB0_IRQ_ALL);
stlink_write_debug32(sl, STM32_FLASH_WB0_COMMAND, STM32_FLASH_WB0_CMD_MASS_ERASE);
wait_flash_busy_progress(sl);
} else {
// WB05 & WL3x do not support mass erase
err = stlink_erase_flash_section(sl, sl->flash_base, sl->flash_size, false);
}
} else {
wait_flash_busy(sl);
clear_flash_error(sl);
@ -1420,29 +1461,33 @@ int32_t stlink_fcheck_flash(stlink_t *sl, const char *path, stm32_addr_t addr) {
*/
int32_t stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data, uint32_t length) {
uint32_t off;
uint32_t cmp_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz;
uint32_t chunk_size = (sl->flash_pgsz > 0x1800) ? 0x1800 : sl->flash_pgsz;
ILOG("Starting verification of write complete\n");
for(off = 0; off < length; off += cmp_size) {
for(off = 0; off < length; ) {
uint32_t aligned_size;
uint32_t read_address = address + off;
uint32_t aligned_read_address = read_address & ~(4 - 1);
uint32_t alignment_offset = read_address - aligned_read_address;
uint32_t cmp_size = chunk_size - alignment_offset;
// adjust last page size
if((off + cmp_size) > length) {
cmp_size = length - off;
}
aligned_size = cmp_size;
aligned_size = alignment_offset + cmp_size;
if(aligned_size & (4 - 1)) {
aligned_size = (cmp_size + 4) & ~(4 - 1);
aligned_size = (aligned_size + 4) & ~(4 - 1);
}
stlink_read_mem32(sl, address + off, (uint16_t) aligned_size);
stlink_read_mem32(sl, aligned_read_address, (uint16_t) aligned_size);
if(memcmp(sl->q_buf, data + off, cmp_size)) {
if(memcmp(sl->q_buf + alignment_offset, data + off, cmp_size)) {
ELOG("Verification of flash failed at offset: %u\n", off);
return (-1);
}
off += cmp_size;
}
ILOG("Flash written and verified! jolly good!\n");

Wyświetl plik

@ -112,6 +112,12 @@ static void stop_wdg_in_debug(stlink_t *sl) {
set = (1 << STM32WB_DBGMCU_APB1FZR1_IWDG_STOP) |
(1 << STM32WB_DBGMCU_APB1FZR1_WWDG_STOP);
break;
case STM32_FLASH_TYPE_WB0:
// WB0 has no DBGMCU, watchdog can only be turned off
if(!stlink_read_debug32(sl, STM32WB0_RCC_APB0ENR, &value)) {
stlink_write_debug32(sl, STM32WB0_RCC_APB0ENR, value & (~STM32WB0_RCC_APB0_WDGEN));
}
return;
default:
return;
}
@ -1077,20 +1083,31 @@ bool stlink_is_core_halted(stlink_t *sl) {
return (sl->core_stat == TARGET_HALTED);
}
int32_t write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf, uint16_t size) {
// write the buffer right after the loader
int32_t write_buffer_to_sram(stlink_t *sl, flash_loader_t *fl, const uint8_t *buf, uint16_t size, uint16_t padded_size) {
// write the buffer right after the loader, and pad the end with 0xFF if padded_size is larger than size
int32_t ret = 0;
uint16_t chunk = size & ~0x3;
uint16_t rem = size & 0x3;
if(chunk) {
memcpy(sl->q_buf, buf, chunk);
ret = stlink_write_mem32(sl, fl->buf_addr, chunk);
uint16_t data_remaining = size;
if (padded_size < size) {
padded_size = size;
}
if(rem && !ret) {
memcpy(sl->q_buf, buf + chunk, rem);
ret = stlink_write_mem8(sl, (fl->buf_addr) + chunk, rem);
uint16_t word_chunk = padded_size & ~0x3;
uint16_t byte_chunk = padded_size & 0x3;
if(word_chunk) {
uint16_t data_cnt = word_chunk > data_remaining ? data_remaining : word_chunk;
memcpy(sl->q_buf, buf, data_cnt);
memset(sl->q_buf + data_cnt, 0xFF, word_chunk - data_cnt);
ret = stlink_write_mem32(sl, fl->buf_addr, word_chunk);
data_remaining -= data_cnt;
}
if(byte_chunk && !ret) {
uint16_t data_cnt = byte_chunk > data_remaining ? data_remaining : byte_chunk;
memcpy(sl->q_buf, buf + word_chunk, data_cnt);
memset(sl->q_buf + data_cnt, 0xFF, byte_chunk - data_cnt);
ret = stlink_write_mem8(sl, (fl->buf_addr) + word_chunk, byte_chunk);
}
return (ret);
@ -1157,6 +1174,17 @@ int32_t stlink_chip_id(stlink_t *sl, uint32_t *chip_id) {
// STM32L0 (RM0377, pg813; RM0367, pg915; RM0376, pg917)
// STM32G0 (RM0444, pg1367)
ret = stlink_read_debug32(sl, 0x40015800, chip_id);
if (*chip_id == 0) {
// these devices don't have a DBG_IDCODE register
// we follow STM32CubeProg and use PART_NUMBER from JTAG_ID instead
// STM32WB05 (RM0491, pg115)
// STM32WB06/WB07 (RM0530, pg105)
// STM32WB09 (RM0505, pg167)
// STM32WL3x (RM0511, pg176)
ret = stlink_read_debug32(sl, STM32WB0_JTAG_ID, chip_id);
*chip_id = (*chip_id) >> STM32WB0_JTAG_PART_NR;
}
} else if(cpu_id.part == STM32_REG_CMx_CPUID_PARTNO_CM33) {
// STM32L5 (RM0438, pg2157)
ret = stlink_read_debug32(sl, 0xE0044000, chip_id);
@ -1230,7 +1258,9 @@ int32_t stlink_load_device_params(stlink_t *sl) {
flash_size = flash_size >> 16;
}
flash_size = flash_size & 0xffff;
if(params->flash_type != STM32_FLASH_TYPE_WB0) {
flash_size = flash_size & 0xffff;
}
if((sl->chip_id == STM32_CHIPID_L1_MD ||
sl->chip_id == STM32_CHIPID_F1_VL_MD_LD ||
@ -1246,6 +1276,27 @@ int32_t stlink_load_device_params(stlink_t *sl) {
} else {
sl->flash_size = 256 * 1024;
}
} else if(params->flash_type == STM32_FLASH_TYPE_WB0) {
sl->flash_base = STM32WB0_FLASH_BASE;
sl->flash_size = ((flash_size & 0x1ffff) + 1) * 4;
if(sl->chip_id == STM32_CHIPID_WL3x) {
// RM0511, p211
switch((flash_size >> STM32_FLASH_WB0_RAM_SIZE) & 0x01)
{
case 0: sl->sram_size = 16*1024; break;
case 1: sl->sram_size = 32*1024; break;
}
} else if(sl->chip_id != STM32_CHIPID_WB05) {
// WB06/WB07 RM0530, p149
// WB09 RM0505, p183
switch((flash_size >> STM32_FLASH_WB0_RAM_SIZE) & 0x03)
{
case 0: // also 32kB
case 1: sl->sram_size = 32*1024; break;
case 2: sl->sram_size = 48*1024; break;
case 3: sl->sram_size = 64*1024; break;
}
}
} else {
sl->flash_size = flash_size * 1024;
}

Wyświetl plik

@ -69,6 +69,23 @@ static const uint8_t loader_code_stm32f0[] = {
0x14, 0x00, 0x00, 0x00
};
// flashloaders/stm32wb0.s
static const uint8_t loader_code_stm32wb0[] = {
0x0b, 0x4b, 0xc9, 0x1a,
0x89, 0x08, 0x09, 0x4f,
0xb9, 0x61, 0x40, 0x37,
0x78, 0xc8, 0x78, 0xc7,
0x50, 0x3f, 0x0d, 0x25,
0x3d, 0x61, 0xcc, 0x24,
0x3c, 0x60, 0x3c, 0x69,
0x2c, 0x40, 0xfc, 0xd0,
0x64, 0x08, 0x02, 0xd1,
0x04, 0x31, 0x10, 0x3a,
0xee, 0xdc, 0x00, 0xbe,
0x00, 0x10, 0x00, 0x40,
0x00, 0x00, 0x04, 0x10
};
// flashloaders/stm32lx.s -- compiled for armv6-m for compatibility with both
// armv6-m cores (STM32L0) and armv7-m cores (STM32L1)
static const uint8_t loader_code_stm32lx[] = {
@ -305,6 +322,9 @@ int32_t stlink_flash_loader_write_to_sram(stlink_t *sl, stm32_addr_t* addr, uint
sl->chip_id == STM32_CHIPID_F09x) {
loader_code = loader_code_stm32f0;
loader_size = sizeof(loader_code_stm32f0);
} else if(sl->flash_type == STM32_FLASH_TYPE_WB0) {
loader_code = loader_code_stm32wb0;
loader_size = sizeof(loader_code_stm32wb0);
} else if((sl->chip_id == STM32_CHIPID_L4) ||
(sl->chip_id == STM32_CHIPID_L41x_L42x) ||
(sl->chip_id == STM32_CHIPID_L43x_L44x) ||
@ -335,10 +355,19 @@ int32_t stlink_flash_loader_run(stlink_t *sl, flash_loader_t* fl, stm32_addr_t t
uint32_t timeout;
uint32_t flash_base = 0;
uint32_t dhcsr, dfsr, cfsr, hfsr;
uint16_t padded_size = size, pad_modulo = 1;
if(sl->flash_type == STM32_FLASH_TYPE_WB0) {
pad_modulo = 16;
}
uint16_t unaligned_cnt = size % pad_modulo;
if (unaligned_cnt) {
padded_size = size + (pad_modulo - unaligned_cnt);
}
DLOG("Running flash loader, write address:%#x, size: %u\n", target, size);
DLOG("Running flash loader, write address:%#x, size: %u, padded_size: %u\n", target, size, padded_size);
if(write_buffer_to_sram(sl, fl, buf, size) == -1) {
if(write_buffer_to_sram(sl, fl, buf, size, padded_size) == -1) {
ELOG("write_buffer_to_sram() == -1\n");
return (-1);
}
@ -350,7 +379,7 @@ int32_t stlink_flash_loader_run(stlink_t *sl, flash_loader_t* fl, stm32_addr_t t
/* Setup core */
stlink_write_reg(sl, fl->buf_addr, 0); // source
stlink_write_reg(sl, target, 1); // target
stlink_write_reg(sl, size, 2); // count
stlink_write_reg(sl, padded_size, 2); // count
stlink_write_reg(sl, flash_base, 3); // flash register base
// only used on VL/F1_XL, but harmless for others
stlink_write_reg(sl, fl->loader_addr, 15); // pc register
@ -401,7 +430,7 @@ int32_t stlink_flash_loader_run(stlink_t *sl, flash_loader_t* fl, stm32_addr_t t
* firmware size.
*/
if((int32_t) rr.r[2] > 0 || (int32_t) rr.r[2] < -7) {
ELOG("Flash loader write error\n");
ELOG("Flash loader write error at 0x%08X\n", target + padded_size - rr.r[2]);
goto error;
}
@ -409,13 +438,18 @@ int32_t stlink_flash_loader_run(stlink_t *sl, flash_loader_t* fl, stm32_addr_t t
error:
dhcsr = dfsr = cfsr = hfsr = 0;
stlink_force_debug(sl); // ensure we can read regs, even after timeout
stlink_read_debug32(sl, STM32_REG_DHCSR, &dhcsr);
stlink_read_debug32(sl, STM32_REG_DFSR, &dfsr);
stlink_read_debug32(sl, STM32_REG_CFSR, &cfsr);
stlink_read_debug32(sl, STM32_REG_HFSR, &hfsr);
stlink_read_all_regs(sl, &rr);
WLOG("Loader state: R2 0x%X R15 0x%X\n", rr.r[2], rr.r[15]);
WLOG("Loader state: PC 0x%08X\n", rr.r[15]);
WLOG(" R0 0x%08X R1 0x%08X\n", rr.r[0], rr.r[1]);
WLOG(" R2 0x%08X R3 0x%08X\n", rr.r[2], rr.r[3]);
WLOG(" R4 0x%08X R5 0x%08X\n", rr.r[4], rr.r[5]);
WLOG(" R6 0x%08X R7 0x%08X\n", rr.r[6], rr.r[7]);
if(dhcsr != 0x3000B || dfsr || cfsr || hfsr) {
WLOG("MCU state: DHCSR 0x%X DFSR 0x%X CFSR 0x%X HFSR 0x%X\n", dhcsr, dfsr, cfsr, hfsr);
}
@ -575,6 +609,10 @@ static void set_dma_state(stlink_t *sl, flash_loader_t *fl, int32_t bckpRstr) {
rcc = STM32WB_RCC_AHB1ENR;
rcc_dma_mask = STM32WB_RCC_DMAEN;
break;
case STM32_FLASH_TYPE_WB0:
rcc = STM32WB0_RCC_AHBENR;
rcc_dma_mask = STM32WB0_RCC_AHB_DMAEN;
break;
default:
return;
}
@ -685,6 +723,13 @@ int32_t stlink_flashloader_start(stlink_t *sl, flash_loader_t *fl) {
// L0/L1 have fallback to soft write
WLOG("stlink_flash_loader_init() == -1\n");
}
} else if(sl->flash_type == STM32_FLASH_TYPE_WB0) {
ILOG("Starting Flash write for WB0\n");
if(stlink_flash_loader_init(sl, fl) == -1) {
ELOG("stlink_flash_loader_init() == -1\n");
return (-1);
}
} else if((sl->flash_type == STM32_FLASH_TYPE_F0_F1_F3) ||
(sl->flash_type == STM32_FLASH_TYPE_F1_XL)) {
ILOG("Starting Flash write for VL/F0/F3/F1_XL\n");
@ -728,11 +773,15 @@ int32_t stlink_flashloader_start(stlink_t *sl, flash_loader_t *fl) {
int32_t stlink_flashloader_write(stlink_t *sl, flash_loader_t *fl, stm32_addr_t addr, uint8_t *base, uint32_t len) {
uint32_t off;
bool is_exclusively_otp = addr >= sl->otp_base && addr < sl->otp_base + sl->otp_size;
bool is_exclusively_flash = addr >= sl->flash_base && addr < sl->flash_base + sl->flash_size;
if((sl->flash_type == STM32_FLASH_TYPE_F2_F4) ||
(sl->flash_type == STM32_FLASH_TYPE_F7) ||
(sl->flash_type == STM32_FLASH_TYPE_L4)) {
uint32_t buf_size = (sl->sram_size > 0x8000) ? 0x8000 : 0x4000;
(sl->flash_type == STM32_FLASH_TYPE_L4) ||
(sl->flash_type == STM32_FLASH_TYPE_WB0 && is_exclusively_flash)) {
uint32_t buf_size = sl->sram_size - 0x1000;
buf_size = buf_size > 0x8000 ? 0x8000 : buf_size;
for(off = 0; off < len;) {
uint32_t size = len - off > buf_size ? buf_size : len - off;
if(stlink_flash_loader_run(sl, fl, addr + off, base + off, size) == -1) {
@ -861,6 +910,32 @@ int32_t stlink_flashloader_write(stlink_t *sl, flash_loader_t *fl, stm32_addr_t
if(sl->verbose >= 1) {
fprintf(stdout, "\n");
}
} else if((sl->flash_type == STM32_FLASH_TYPE_WB0) && is_exclusively_otp) {
// WB0 OTP area can not be written with BURSTWRITE as implemented in flashloader
// Writes are done as 32bit words, out of bounds bytes are written as 0xFF (no change to flash)
for(off = 0; off < len; ) {
uint32_t current_addr = addr + off;
uint32_t remaining = len - off;
uint32_t word_base = current_addr & (~0x03);
uint32_t unused_front = current_addr - word_base;
uint32_t max_bytes = 4 - unused_front;
uint32_t bytes_to_copy = remaining < max_bytes ? remaining : max_bytes;
uint32_t data_word = 0xFFFFFFFF;
memcpy(((uint8_t*)&data_word) + unused_front, base + off, bytes_to_copy);
stlink_write_debug32(sl, STM32_FLASH_WB0_IRQRAW, STM32_FLASH_WB0_IRQ_ALL);
stlink_write_debug32(sl, STM32_FLASH_WB0_DATA0, data_word);
stlink_write_debug32(sl, STM32_FLASH_WB0_ADDRESS, (current_addr) >> 2);
stlink_write_debug32(sl, STM32_FLASH_WB0_COMMAND, STM32_FLASH_WB0_CMD_OTPWRITE);
wait_flash_busy(sl);
if (check_flash_error(sl)) {
ELOG("Failed to writing OTP word at 0x%08X!\n", current_addr);
return (-1);
}
off += bytes_to_copy;
}
} else {
return (-1);
}