kopia lustrzana https://github.com/stlink-org/stlink
Merge pull request #1466 from dbeinder/stm32wb0
[feature] STM32 flash type implementation for WB05, WB06/07, WB09, WL3xtesting
commit
5259daa4bf
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
13
inc/stm32.h
13
inc/stm32.h
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue