Basic support for F446
[fw/stlink] / src / stlink-common.c
index 4e78a1d01ff558c5e0f460a28eb3286bd6194668..343795aeb99731b96e237365eaa8b838315882f2 100644 (file)
 #define FLASH_L1_FPRG 10
 #define FLASH_L1_PROG 3
 
+//STM32L0x flash register base and offsets
+//same as 32L1 above
+// RM0090 - DM00031020.pdf
+#define STM32L0_FLASH_REGS_ADDR ((uint32_t)0x40022000)
+#define FLASH_ACR_OFF     ((uint32_t) 0x00)
+#define FLASH_PECR_OFF    ((uint32_t) 0x04)
+#define FLASH_PDKEYR_OFF  ((uint32_t) 0x08)
+#define FLASH_PEKEYR_OFF  ((uint32_t) 0x0c)
+#define FLASH_PRGKEYR_OFF ((uint32_t) 0x10)
+#define FLASH_OPTKEYR_OFF ((uint32_t) 0x14)
+#define FLASH_SR_OFF      ((uint32_t) 0x18)
+#define FLASH_OBR_OFF     ((uint32_t) 0x1c)
+#define FLASH_WRPR_OFF    ((uint32_t) 0x20)
+
+
 
 //STM32F4
 #define FLASH_F4_REGS_ADDR ((uint32_t)0x40023c00)
 #define FLASH_F4_CR_LOCK 31
 #define FLASH_F4_CR_SER 1
 #define FLASH_F4_CR_SNB 3
-#define FLASH_F4_CR_SNB_MASK 0x38
+#define FLASH_F4_CR_SNB_MASK 0xf8
 #define FLASH_F4_SR_BSY 16
 
+#define L1_WRITE_BLOCK_SIZE 0x80
+#define L0_WRITE_BLOCK_SIZE 0x40
 
 void write_uint32(unsigned char* buf, uint32_t ui) {
     if (!is_bigendian()) { // le -> le (don't swap)
@@ -136,7 +153,8 @@ static inline uint32_t read_flash_obr(stlink_t *sl) {
 static inline uint32_t read_flash_cr(stlink_t *sl) {
     uint32_t res;
     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_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
         res = stlink_read_debug32(sl, FLASH_CR);
@@ -149,7 +167,8 @@ 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 */
     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_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);
     else
         return read_flash_cr(sl) & (1 << FLASH_CR_LOCK);
@@ -162,7 +181,8 @@ static void unlock_flash(stlink_t *sl) {
        the FPEC block until next reset.
        */
     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_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (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 {
@@ -188,7 +208,8 @@ static int unlock_flash_if(stlink_t *sl) {
 
 static void lock_flash(stlink_t *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_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (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 {
@@ -201,7 +222,8 @@ static void lock_flash(stlink_t *sl) {
 
 static void set_flash_cr_pg(stlink_t *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_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (sl->chip_id == STM32_CHIPID_F446)) {
         uint32_t x = read_flash_cr(sl);
         x |= (1 << FLASH_CR_PG);
         stlink_write_debug32(sl, FLASH_F4_CR, x);
@@ -214,7 +236,8 @@ static void set_flash_cr_pg(stlink_t *sl) {
 static void __attribute__((unused)) clear_flash_cr_pg(stlink_t *sl) {
     const uint32_t n = read_flash_cr(sl) & ~(1 << FLASH_CR_PG);
     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_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (sl->chip_id == STM32_CHIPID_F446))
         stlink_write_debug32(sl, FLASH_F4_CR, n);
     else
         stlink_write_debug32(sl, FLASH_CR, n);
@@ -232,7 +255,8 @@ static void __attribute__((unused)) clear_flash_cr_per(stlink_t *sl) {
 
 static void set_flash_cr_mer(stlink_t *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_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (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
@@ -242,7 +266,8 @@ static void set_flash_cr_mer(stlink_t *sl) {
 
 static void __attribute__((unused)) clear_flash_cr_mer(stlink_t *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_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (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
@@ -252,7 +277,8 @@ static void __attribute__((unused)) clear_flash_cr_mer(stlink_t *sl) {
 
 static void set_flash_cr_strt(stlink_t *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_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (sl->chip_id == STM32_CHIPID_F446)) {
         uint32_t x = read_flash_cr(sl);
         x |= (1 << FLASH_F4_CR_STRT);
         stlink_write_debug32(sl, FLASH_F4_CR, x);
@@ -269,7 +295,8 @@ static inline uint32_t read_flash_acr(stlink_t *sl) {
 static inline uint32_t read_flash_sr(stlink_t *sl) {
     uint32_t res;
     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_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
         res = stlink_read_debug32(sl, FLASH_SR);
@@ -279,7 +306,8 @@ static inline uint32_t read_flash_sr(stlink_t *sl) {
 
 static inline unsigned int is_flash_busy(stlink_t *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_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
         return read_flash_sr(sl) & (1 << FLASH_SR_BSY);
@@ -447,6 +475,8 @@ int stlink_load_device_params(stlink_t *sl) {
 
     if ((sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS) && ( flash_size == 0 )) {
         sl->flash_size = 128 * 1024;
+    } else if (sl->chip_id == STM32_CHIPID_L1_CAT2) {
+        sl->flash_size = (flash_size & 0xff) * 1024;
     } else if ((sl->chip_id & 0xFFF) == STM32_CHIPID_L1_HIGH) {
         // 0 is 384k and 1 is 256k
         if ( flash_size == 0 ) {
@@ -462,6 +492,12 @@ int stlink_load_device_params(stlink_t *sl) {
     sl->sys_base = params->bootrom_base;
     sl->sys_size = params->bootrom_size;
 
+    //medium and low devices have the same chipid. ram size depends on flash size.
+    //STM32F100xx datasheet Doc ID 16455 Table 2
+    if(sl->chip_id == STM32_CHIPID_F1_VL_MEDIUM_LOW && sl->flash_size < 64 * 1024){
+        sl->sram_size = 0x1000;
+    }
+
     ILOG("Device connected is: %s, id %#x\n", params->description, chip_id);
     // TODO make note of variable page size here.....
     ILOG("SRAM size: %#x bytes (%d KiB), Flash: %#x bytes (%d KiB) in pages of %zd bytes\n",
@@ -912,8 +948,9 @@ int stlink_fread(stlink_t* sl, const char* path, stm32_addr_t addr, size_t size)
     int error = -1;
     size_t off;
     int num_empty = 0;
-    unsigned char erased_pattern = (sl->chip_id == STM32_CHIPID_L1_MEDIUM  || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS
-            || sl->chip_id == STM32_CHIPID_L1_HIGH || sl->chip_id == STM32_CHIPID_L152_RE) ? 0:0xff;
+    unsigned char erased_pattern = (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2
+            || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH
+            || sl->chip_id == STM32_CHIPID_L152_RE) ? 0:0xff;
 
     const int fd = open(path, O_RDWR | O_TRUNC | O_CREAT, 00700);
     if (fd == -1) {
@@ -984,20 +1021,29 @@ int write_buffer_to_sram(stlink_t *sl, flash_loader_t* fl, const uint8_t* buf, s
 }
 
 uint32_t calculate_F4_sectornum(uint32_t flashaddr){
+    uint32_t offset = 0;
     flashaddr &= ~STM32_FLASH_BASE;    //Page now holding the actual flash address
-    if (flashaddr<0x4000) return (0);
-    else if(flashaddr<0x8000) return(1);
-    else if(flashaddr<0xc000) return(2);
-    else if(flashaddr<0x10000) return(3);
-    else if(flashaddr<0x20000) return(4);
-    else return(flashaddr/0x20000)+4;
+    if (flashaddr >= 0x100000) {
+        offset = 12;
+        flashaddr -= 0x100000;
+    } 
+    if (flashaddr<0x4000) return (offset + 0);
+    else if(flashaddr<0x8000) return(offset + 1);
+    else if(flashaddr<0xc000) return(offset + 2);
+    else if(flashaddr<0x10000) return(offset + 3);
+    else if(flashaddr<0x20000) return(offset + 4);
+    else return offset + (flashaddr/0x20000) +4;
 
 }
 
 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_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (sl->chip_id == STM32_CHIPID_F446)) {
         uint32_t sector=calculate_F4_sectornum(flashaddr);
+        if (sector>= 12) {
+            sector -= 12;
+        }
         if (sector<4) sl->flash_pgsz=0x4000;
         else if(sector<5) sl->flash_pgsz=0x10000;
         else sl->flash_pgsz=0x20000;
@@ -1014,7 +1060,8 @@ uint32_t stlink_calculate_pagesize(stlink_t *sl, uint32_t flashaddr){
 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_F4_LP) || (sl->chip_id == STM32_CHIPID_F4_HD) || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (sl->chip_id == STM32_CHIPID_F446)) {
         /* wait for ongoing op to finish */
         wait_flash_busy(sl);
 
@@ -1026,6 +1073,10 @@ int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr)
         uint32_t sector=calculate_F4_sectornum(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;
+
         write_flash_cr_snb(sl, sector);
 
         /* start erase operation */
@@ -1040,31 +1091,38 @@ int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr)
 #if DEBUG_FLASH
         fprintf(stdout, "Erase Final CR:0x%x\n", read_flash_cr(sl));
 #endif
-    } else if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS
-            || sl->chip_id == STM32_CHIPID_L1_HIGH || sl->chip_id == STM32_CHIPID_L152_RE) {
+    } else if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2
+            || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH
+            || sl->chip_id == STM32_CHIPID_L152_RE || sl->chip_id == STM32_CHIPID_L0) {
 
         uint32_t val;
+        uint32_t flash_regs_base;
+        if (sl->chip_id == STM32_CHIPID_L0) {
+            flash_regs_base = STM32L0_FLASH_REGS_ADDR;
+        } else {
+            flash_regs_base = STM32L_FLASH_REGS_ADDR;
+        }
 
         /* check if the locks are set */
-        val = stlink_read_debug32(sl, STM32L_FLASH_PECR);
+        val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF);
         if((val & (1<<0))||(val & (1<<1))) {
             /* disable pecr protection */
-            stlink_write_debug32(sl, STM32L_FLASH_PEKEYR, 0x89abcdef);
-            stlink_write_debug32(sl, STM32L_FLASH_PEKEYR, 0x02030405);
+            stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x89abcdef);
+            stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x02030405);
 
             /* check pecr.pelock is cleared */
-            val = stlink_read_debug32(sl, STM32L_FLASH_PECR);
+            val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF);
             if (val & (1 << 0)) {
                 WLOG("pecr.pelock not clear (%#x)\n", val);
                 return -1;
             }
 
             /* unlock program memory */
-            stlink_write_debug32(sl, STM32L_FLASH_PRGKEYR, 0x8c9daebf);
-            stlink_write_debug32(sl, STM32L_FLASH_PRGKEYR, 0x13141516);
+            stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x8c9daebf);
+            stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x13141516);
 
             /* check pecr.prglock is cleared */
-            val = stlink_read_debug32(sl, STM32L_FLASH_PECR);
+            val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF);
             if (val & (1 << 1)) {
                 WLOG("pecr.prglock not clear (%#x)\n", val);
                 return -1;
@@ -1073,8 +1131,7 @@ int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr)
 
         /* set pecr.{erase,prog} */
         val |= (1 << 9) | (1 << 3);
-        stlink_write_debug32(sl, STM32L_FLASH_PECR, val);
-
+        stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val);
 #if 0 /* fix_to_be_confirmed */
 
         /* wait for sr.busy to be cleared
@@ -1095,17 +1152,19 @@ int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr)
            page erase command, even though PM0062 recommends to wait before it.
            Test shows that a few iterations is performed in the following loop
            before busy bit is cleared.*/
-        while ((stlink_read_debug32(sl, STM32L_FLASH_SR) & (1 << 0)) != 0)
+        while ((stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF) & (1 << 0)) != 0)
             ;
 
         /* reset lock bits */
-        val = stlink_read_debug32(sl, STM32L_FLASH_PECR)
+        val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF)
             | (1 << 0) | (1 << 1) | (1 << 2);
-        stlink_write_debug32(sl, STM32L_FLASH_PECR, val);
+        stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val);
     } else if (sl->core_id == STM32VL_CORE_ID 
             || sl->core_id == STM32F0_CORE_ID 
             || sl->chip_id == STM32_CHIPID_F3 
-            || sl->chip_id == STM32_CHIPID_F37x) {
+            || sl->chip_id == STM32_CHIPID_F303_HIGH
+            || sl->chip_id == STM32_CHIPID_F37x
+            || sl->chip_id == STM32_CHIPID_F334)  {
         /* wait for ongoing op to finish */
         wait_flash_busy(sl);
 
@@ -1137,8 +1196,9 @@ int stlink_erase_flash_page(stlink_t *sl, stm32_addr_t flashaddr)
 }
 
 int stlink_erase_flash_mass(stlink_t *sl) {
-    if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS 
-            || sl->chip_id == STM32_CHIPID_L1_HIGH || sl->chip_id == STM32_CHIPID_L152_RE) {
+    if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2
+            || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH
+            || sl->chip_id == STM32_CHIPID_L152_RE || sl->chip_id == STM32_CHIPID_L0) {
         /* erase each page */
         int i = 0, num_pages = sl->flash_size/sl->flash_pgsz;
         for (i = 0; i < num_pages; i++) {
@@ -1283,6 +1343,29 @@ int write_loader_to_sram(stlink_t *sl, stm32_addr_t* addr, size_t* size) {
         0x00, 0xbe
     };
 
+    static const uint8_t loader_code_stm32l0[] = {
+
+        /*
+           r0, input, dest addr
+           r1, input, source addr
+           r2, input, word count
+           r3, output, word count
+         */
+
+        0x00, 0x23,
+        0x04, 0xe0,
+
+        0x0c, 0x68,
+        0x04, 0x60,
+        0x01, 0x33,
+        0x04, 0x31,
+        0x04, 0x30,
+
+        0x93, 0x42,
+        0xf8, 0xd3,
+        0x00, 0xbe
+    };
+
     static const uint8_t loader_code_stm32f4[] = {
         // flashloaders/stm32f4.s
 
@@ -1305,23 +1388,61 @@ 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_stm32f4_lv[] = {
+        // flashloaders/stm32f4lv.s
+        0x92, 0x00,
+
+        0x08, 0x4b,
+        0x62, 0xb1,
+        0x04, 0x78,
+        0x0c, 0x70,
+
+        0xdc, 0x89,
+        0x14, 0xf0, 0x01, 0x0f,
+        0xfb, 0xd1,
+        0x00, 0xf1, 0x01, 0x00,
+        0x01, 0xf1, 0x01, 0x01,
+        0xa2, 0xf1, 0x01, 0x02,
+        0xf1, 0xe7,
+
+        0x00, 0xbe,
+        0x00, 0xbf,
+
+        0x00, 0x3c, 0x02, 0x40,
+    };
+
     const uint8_t* loader_code;
     size_t loader_size;
 
-    if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS
-            || sl->chip_id == STM32_CHIPID_L1_HIGH || sl->chip_id == STM32_CHIPID_L152_RE ) { /* stm32l */
+    if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2
+            || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH
+            || sl->chip_id == STM32_CHIPID_L152_RE) { /* stm32l */
         loader_code = loader_code_stm32l;
         loader_size = sizeof(loader_code_stm32l);
-    } else if (sl->core_id == STM32VL_CORE_ID || sl->chip_id == STM32_CHIPID_F3  || sl->chip_id == STM32_CHIPID_F37x) {
+    } else if (sl->core_id == STM32VL_CORE_ID 
+            || sl->chip_id == STM32_CHIPID_F3
+            || sl->chip_id == STM32_CHIPID_F303_HIGH
+            || sl->chip_id == STM32_CHIPID_F37x
+            || sl->chip_id == STM32_CHIPID_F334) {
         loader_code = loader_code_stm32vl;
         loader_size = sizeof(loader_code_stm32vl);
     } 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){
-        loader_code = loader_code_stm32f4;
-        loader_size = sizeof(loader_code_stm32f4);
-    } else if (sl->chip_id == STM32_CHIPID_F0 || sl->chip_id == STM32_CHIPID_F0_CAN || sl->chip_id == STM32_CHIPID_F0_SMALL) {
+            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)){
+        int voltage = stlink_target_voltage(sl);
+        if (voltage > 2700) {
+            loader_code = loader_code_stm32f4;
+            loader_size = sizeof(loader_code_stm32f4);
+        } else {
+            loader_code = loader_code_stm32f4_lv;
+            loader_size = sizeof(loader_code_stm32f4_lv);
+        }
+    } else if (sl->chip_id == STM32_CHIPID_F0 || sl->chip_id == STM32_CHIPID_F04 || sl->chip_id == STM32_CHIPID_F0_CAN || sl->chip_id == STM32_CHIPID_F0_SMALL || sl->chip_id == STM32_CHIPID_F09X) {
         loader_code = loader_code_stm32f0;
         loader_size = sizeof(loader_code_stm32f0);
+    } else if (sl->chip_id == STM32_CHIPID_L0) {
+        loader_code = loader_code_stm32l0;
+        loader_size = sizeof(loader_code_stm32l0);
     } else {
         ELOG("unknown coreid, not sure what flash loader to use, aborting!: %x\n", sl->core_id);
         return -1;
@@ -1388,12 +1509,20 @@ int stlink_verify_write_flash(stlink_t *sl, stm32_addr_t address, uint8_t *data,
 
 }
 
-int stm32l1_write_half_pages(stlink_t *sl, stm32_addr_t addr, uint8_t* base, unsigned num_half_pages)
+int stm32l1_write_half_pages(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t len, uint32_t pagesize)
 {
     unsigned int count;
+    unsigned int num_half_pages = len / pagesize;
     uint32_t val;
+    uint32_t flash_regs_base;
     flash_loader_t fl;
 
+    if (sl->chip_id == STM32_CHIPID_L0) {
+        flash_regs_base = STM32L0_FLASH_REGS_ADDR;
+    } else {
+        flash_regs_base = STM32L_FLASH_REGS_ADDR;
+    }
+
     ILOG("Starting Half page flash write for STM32L core id\n");
     /* flash loader initialization */
     if (init_flash_loader(sl, &fl) == -1) {
@@ -1401,21 +1530,20 @@ int stm32l1_write_half_pages(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uns
         return -1;
     }
     /* Unlock already done */
-    val = stlink_read_debug32(sl, STM32L_FLASH_PECR);
+    val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF);
     val |= (1 << FLASH_L1_FPRG);
-    stlink_write_debug32(sl, STM32L_FLASH_PECR, val);
+    stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val);
 
     val |= (1 << FLASH_L1_PROG);
-    stlink_write_debug32(sl, STM32L_FLASH_PECR, val);
-    while ((stlink_read_debug32(sl, STM32L_FLASH_SR) & (1 << 0)) != 0) {}
+    stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val);
+    while ((stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF) & (1 << 0)) != 0) {}
 
-#define L1_WRITE_BLOCK_SIZE 0x80
     for (count = 0; count  < num_half_pages; count ++) {
-        if (run_flash_loader(sl, &fl, addr + count * L1_WRITE_BLOCK_SIZE, base + count * L1_WRITE_BLOCK_SIZE, L1_WRITE_BLOCK_SIZE) == -1) {
-            WLOG("l1_run_flash_loader(%#zx) failed! == -1\n", addr + count * L1_WRITE_BLOCK_SIZE);
-            val = stlink_read_debug32(sl, STM32L_FLASH_PECR);
+        if (run_flash_loader(sl, &fl, addr + count * pagesize, base + count * pagesize, pagesize) == -1) {
+            WLOG("l1_run_flash_loader(%#zx) failed! == -1\n", addr + count * pagesize);
+            val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF);
             val &= ~((1 << FLASH_L1_FPRG) |(1 << FLASH_L1_PROG));
-            stlink_write_debug32(sl, STM32L_FLASH_PECR, val);
+            stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val);
             return -1;
         }
         /* wait for sr.busy to be cleared */
@@ -1425,15 +1553,15 @@ int stm32l1_write_half_pages(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uns
             fprintf(stdout, "\r%3u/%u halfpages written", count + 1, num_half_pages);
             fflush(stdout);
         }
-        while ((stlink_read_debug32(sl, STM32L_FLASH_SR) & (1 << 0)) != 0) {
+        while ((stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF) & (1 << 0)) != 0) {
         }
     }
-    val = stlink_read_debug32(sl, STM32L_FLASH_PECR);
+    val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF);
     val &= ~(1 << FLASH_L1_PROG);
-    stlink_write_debug32(sl, STM32L_FLASH_PECR, val);
-    val = stlink_read_debug32(sl, STM32L_FLASH_PECR);
+    stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val);
+    val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF);
     val &= ~(1 << FLASH_L1_FPRG);
-    stlink_write_debug32(sl, STM32L_FLASH_PECR, val);
+    stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val);
 
     return 0;
 }
@@ -1484,8 +1612,13 @@ int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t
     ILOG("Finished erasing %d pages of %d (%#x) bytes\n",
             page_count, sl->flash_pgsz, sl->flash_pgsz);
 
-    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)) {
+    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)) {
         /* todo: check write operation */
 
         ILOG("Starting Flash write for F2/F4\n");
@@ -1500,7 +1633,14 @@ int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t
 
         /* TODO: Check that Voltage range is 2.7 - 3.6 V */
         /* set parallelisim to 32 bit*/
-        write_flash_cr_psiz(sl, 2);
+        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);
+        }
 
         /* set programming mode */
         set_flash_cr_pg(sl);
@@ -1523,41 +1663,52 @@ int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t
 
     }  //STM32F4END
 
-    else if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS
-            || sl->chip_id == STM32_CHIPID_L1_HIGH || sl->chip_id == STM32_CHIPID_L152_RE ) {
+    else if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2
+            || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH
+            || sl->chip_id == STM32_CHIPID_L152_RE || sl->chip_id == STM32_CHIPID_L0) {
         /* use fast word write. todo: half page. */
         uint32_t val;
+        uint32_t flash_regs_base;
+        uint32_t pagesize;
+
+        if (sl->chip_id == STM32_CHIPID_L0) {
+            flash_regs_base = STM32L0_FLASH_REGS_ADDR;
+            pagesize = L0_WRITE_BLOCK_SIZE;
+        } else {
+            flash_regs_base = STM32L_FLASH_REGS_ADDR;
+            pagesize = L1_WRITE_BLOCK_SIZE;
+        }
 
         /* todo: check write operation */
 
         /* disable pecr protection */
-        stlink_write_debug32(sl, STM32L_FLASH_PEKEYR, 0x89abcdef);
-        stlink_write_debug32(sl, STM32L_FLASH_PEKEYR, 0x02030405);
+        stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x89abcdef);
+        stlink_write_debug32(sl, flash_regs_base + FLASH_PEKEYR_OFF, 0x02030405);
 
         /* check pecr.pelock is cleared */
-        val = stlink_read_debug32(sl, STM32L_FLASH_PECR);
+        val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF);
         if (val & (1 << 0)) {
             fprintf(stderr, "pecr.pelock not clear\n");
             return -1;
         }
 
         /* unlock program memory */
-        stlink_write_debug32(sl, STM32L_FLASH_PRGKEYR, 0x8c9daebf);
-        stlink_write_debug32(sl, STM32L_FLASH_PRGKEYR, 0x13141516);
+        stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x8c9daebf);
+        stlink_write_debug32(sl, flash_regs_base + FLASH_PRGKEYR_OFF, 0x13141516);
 
         /* check pecr.prglock is cleared */
-        val = stlink_read_debug32(sl, STM32L_FLASH_PECR);
+        val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF);
         if (val & (1 << 1)) {
             fprintf(stderr, "pecr.prglock not clear\n");
             return -1;
         }
         off = 0;
-        if (len > L1_WRITE_BLOCK_SIZE) {
-            if (stm32l1_write_half_pages(sl, addr, base, len/L1_WRITE_BLOCK_SIZE) == -1) {
+        if (len > pagesize) {
+            if (stm32l1_write_half_pages(sl, addr, base, len, pagesize) == -1) {
                 /* This may happen on a blank device! */
                 WLOG("\nwrite_half_pages failed == -1\n");
             } else {
-                off = (len /L1_WRITE_BLOCK_SIZE)*L1_WRITE_BLOCK_SIZE;
+                off = (len / pagesize)*pagesize;
             }
         }
 
@@ -1577,7 +1728,7 @@ int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t
             stlink_write_debug32(sl, addr + off, data);
 
             /* wait for sr.busy to be cleared */
-            while ((stlink_read_debug32(sl, STM32L_FLASH_SR) & (1 << 0)) != 0)
+            while ((stlink_read_debug32(sl, flash_regs_base + FLASH_SR_OFF) & (1 << 0)) != 0)
                 ;
 
             /* todo: check redo write operation */
@@ -1585,11 +1736,16 @@ int stlink_write_flash(stlink_t *sl, stm32_addr_t addr, uint8_t* base, uint32_t
         }
         fprintf(stdout, "\n");
         /* reset lock bits */
-        val = stlink_read_debug32(sl, STM32L_FLASH_PECR)
+        val = stlink_read_debug32(sl, flash_regs_base + FLASH_PECR_OFF)
             | (1 << 0) | (1 << 1) | (1 << 2);
-        stlink_write_debug32(sl, STM32L_FLASH_PECR, val);
-    } else if (sl->core_id == STM32VL_CORE_ID || sl->core_id == STM32F0_CORE_ID || sl->chip_id == STM32_CHIPID_F3  || sl->chip_id == STM32_CHIPID_F37x) {
-        ILOG("Starting Flash write for VL/F0 core id\n");
+        stlink_write_debug32(sl, flash_regs_base + FLASH_PECR_OFF, val);
+    } else if (sl->core_id == STM32VL_CORE_ID ||
+                sl->core_id == STM32F0_CORE_ID ||
+                sl->chip_id == STM32_CHIPID_F3  ||
+                sl->chip_id == STM32_CHIPID_F303_HIGH ||
+                sl->chip_id == STM32_CHIPID_F334 ||
+                sl->chip_id == STM32_CHIPID_F37x) {
+        ILOG("Starting Flash write for VL/F0/F3 core id\n");
         /* flash loader initialization */
         if (init_flash_loader(sl, &fl) == -1) {
             ELOG("init_flash_loader() == -1\n");
@@ -1638,8 +1794,9 @@ int stlink_fwrite_flash(stlink_t *sl, const char* path, stm32_addr_t addr) {
     /* write the file in flash at addr */
     int err;
     unsigned int num_empty = 0, index;
-    unsigned char erased_pattern =(sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS
-            || sl->chip_id == STM32_CHIPID_L1_HIGH || sl->chip_id == STM32_CHIPID_L152_RE )?0:0xff;
+    unsigned char erased_pattern = (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2
+            || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH
+            || sl->chip_id == STM32_CHIPID_L152_RE) ? 0:0xff;
     mapped_file_t mf = MAPPED_FILE_INITIALIZER;
     if (map_file(&mf, path) == -1) {
         ELOG("map_file() == -1\n");
@@ -1679,8 +1836,9 @@ int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, cons
         return -1;
     }
 
-    if (sl->chip_id == STM32_CHIPID_L1_MEDIUM  || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS
-            || sl->chip_id == STM32_CHIPID_L1_HIGH || sl->chip_id == STM32_CHIPID_L152_RE ) {
+    if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2
+            || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH
+            || sl->chip_id == STM32_CHIPID_L152_RE || sl->chip_id == STM32_CHIPID_L0) {
 
         size_t count = size / sizeof(uint32_t);
         if (size % sizeof(uint32_t)) ++count;
@@ -1691,7 +1849,12 @@ int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, cons
         stlink_write_reg(sl, count, 2); /* count (32 bits words) */
         stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */
 
-    } else if (sl->core_id == STM32VL_CORE_ID || sl->core_id == STM32F0_CORE_ID || sl->chip_id == STM32_CHIPID_F3  || sl->chip_id == STM32_CHIPID_F37x) {
+    } else if (sl->core_id == STM32VL_CORE_ID ||
+                sl->core_id == STM32F0_CORE_ID ||
+                sl->chip_id == STM32_CHIPID_F3 ||
+                sl->chip_id == STM32_CHIPID_F303_HIGH ||
+                sl->chip_id == STM32_CHIPID_F37x ||
+                sl->chip_id == STM32_CHIPID_F334) {
 
         size_t count = size / sizeof(uint16_t);
         if (size % sizeof(uint16_t)) ++count;
@@ -1704,7 +1867,8 @@ int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, cons
         stlink_write_reg(sl, fl->loader_addr, 15); /* pc register */
 
     } 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_F4_LP || sl->chip_id == STM32_CHIPID_F4_HD || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (sl->chip_id == STM32_CHIPID_F446)) {
 
         size_t count = size / sizeof(uint32_t);
         if (size % sizeof(uint32_t)) ++count;
@@ -1737,8 +1901,9 @@ int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, cons
     }
 
     /* check written byte count */
-    if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS
-            || sl->chip_id == STM32_CHIPID_L1_HIGH || sl->chip_id == STM32_CHIPID_L152_RE ) {
+    if (sl->chip_id == STM32_CHIPID_L1_MEDIUM || sl->chip_id == STM32_CHIPID_L1_CAT2
+            || sl->chip_id == STM32_CHIPID_L1_MEDIUM_PLUS || sl->chip_id == STM32_CHIPID_L1_HIGH
+            || sl->chip_id == STM32_CHIPID_L152_RE || sl->chip_id == STM32_CHIPID_L0) {
 
         size_t count = size / sizeof(uint32_t);
         if (size % sizeof(uint32_t)) ++count;
@@ -1749,7 +1914,12 @@ int run_flash_loader(stlink_t *sl, flash_loader_t* fl, stm32_addr_t target, cons
             return -1;
         }
 
-    } else if (sl->core_id == STM32VL_CORE_ID || sl->core_id == STM32F0_CORE_ID || sl->chip_id == STM32_CHIPID_F3  || sl->chip_id == STM32_CHIPID_F37x) {
+    } else if (sl->core_id == STM32VL_CORE_ID ||
+                sl->core_id == STM32F0_CORE_ID ||
+                sl->chip_id == STM32_CHIPID_F3 ||
+                sl->chip_id == STM32_CHIPID_F303_HIGH ||
+                sl->chip_id == STM32_CHIPID_F37x ||
+                sl->chip_id == STM32_CHIPID_F334) {
 
         stlink_read_reg(sl, 2, &rr);
         if (rr.r[2] != 0) {
@@ -1758,7 +1928,8 @@ 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_F4_LP || sl->chip_id == STM32_CHIPID_F4_HD || (sl->chip_id == STM32_CHIPID_F411RE) ||
+            (sl->chip_id == STM32_CHIPID_F446)) {
 
         stlink_read_reg(sl, 2, &rr);
         if (rr.r[2] != 0) {