Browse Source

STM32F1, AT32F4: Dynamic USB buffer sizing.

Also tweak firmware update with useful error messages if updates
don't fit in SRAM (bootloader updates) or Flash (main firmware updates).
Keir Fraser 3 years ago
parent
commit
08916f8129

+ 2 - 0
inc/cdc_acm_protocol.h

@@ -104,6 +104,8 @@
 #define ACK_BAD_UNIT        9
 #define ACK_BAD_PIN        10
 #define ACK_BAD_CYLINDER   11
+#define ACK_OUT_OF_SRAM    12
+#define ACK_OUT_OF_FLASH   13
 
 
 /*

+ 1 - 0
inc/mcu/stm32/common.h

@@ -91,6 +91,7 @@ bool_t gpio_pins_connected(GPIO gpio1, unsigned int pin1,
 void fpec_init(void);
 void fpec_page_erase(uint32_t flash_address);
 void fpec_write(const void *data, unsigned int size, uint32_t flash_address);
+extern unsigned int flash_kb;
 
 /* Pin mappings */
 enum { _A = 0, _B, _C, _D, _E, _F, _G, _H, _I };

+ 2 - 0
inc/mcu/stm32/f1.h

@@ -63,6 +63,8 @@ static SER_ID ser_id = (uint32_t *)0x1ffff7e8;
 /* No secondary RAM region */
 #define section_ext_ram
 
+extern unsigned int sram_kb;
+
 enum {
     F1SM_basic = 0,
     F1SM_plus,

+ 10 - 1
scripts/greaseweazle/tools/update.py

@@ -114,7 +114,16 @@ def main(argv):
         usb = util.usb_open(args.device, is_update=not args.bootloader)
         update_firmware(usb, args)
     except USB.CmdError as error:
-        print("Command Failed: %s" % error)
+        if error.code == USB.Ack.OutOfSRAM and args.bootloader:
+            # Special warning for Low-Density F1 devices. The new bootloader
+            # cannot be fully buffered in the limited RAM available.
+            print("ERROR: Bootloader update unsupported on this device "
+                  "(insufficient SRAM)")
+        elif error.code == USB.Ack.OutOfFlash and not args.bootloader:
+            print("ERROR: New firmware is too large for this device "
+                  "(insufficient Flash memory)")
+        else:
+            print("Command Failed: %s" % error)
 
 
 if __name__ == "__main__":

+ 5 - 1
scripts/greaseweazle/usb.py

@@ -84,6 +84,8 @@ class Ack:
     BadUnit         =  9
     BadPin          = 10
     BadCylinder     = 11
+    OutOfSRAM       = 12
+    OutOfFlash      = 13
     str = {
         Okay: "Okay",
         BadCommand: "Bad Command",
@@ -96,7 +98,9 @@ class Ack:
         NoBus: "No bus type (eg. Shugart, IBM/PC) specified",
         BadUnit: "Invalid unit number",
         BadPin: "Invalid pin",
-        BadCylinder: "Invalid cylinder"
+        BadCylinder: "Invalid cylinder",
+        OutOfSRAM: "Out of SRAM",
+        OutOfFlash: "Out of Flash"
     }
 
 

+ 5 - 1
scripts/stm32.ld.S

@@ -7,7 +7,7 @@ MEMORY
   RAM (rwx)       : ORIGIN = 0x20000000, LENGTH = 20K
 #elif MCU == STM32F7
   RAM (rwx)       : ORIGIN = 0x20000000, LENGTH = 64K
-  EXT_RAM (rwx)   : ORIGIN = 0x20010000, LENGTH = 192K
+  EXT_RAM (rwx)   : ORIGIN = 0x20010000, LENGTH = 64K /* 192K - U_BUF_SZ */
 #elif MCU == AT32F4
   RAM (rwx)       : ORIGIN = 0x20000000, LENGTH = 32K
 #endif
@@ -58,6 +58,9 @@ SECTIONS
     *(.bss*)
     . = ALIGN(4);
     _ebss = .;
+#if MCU == STM32F1 || MCU == AT32F4
+    u_buf = .;
+#endif
   } >RAM
 
 #if MCU == STM32F7
@@ -68,6 +71,7 @@ SECTIONS
     *(.ext_ram)
     . = ALIGN(4);
     _ext_ram_end = .;
+    u_buf = .; /* 128K */
   } >EXT_RAM
 #endif
 

+ 15 - 5
src/floppy.c

@@ -45,6 +45,8 @@ static const struct gw_delay factory_delay_params = {
     .watchdog = 10000
 };
 
+extern uint8_t u_buf[];
+
 #if MCU == STM32F1
 #include "mcu/stm32f1/floppy.c"
 #elif MCU == STM32F7
@@ -1302,12 +1304,22 @@ static void erase_old_bootloader(void)
         fpec_page_erase(p);
 }
 
-static void update_prep(uint32_t len)
+static uint8_t update_prep(uint32_t len)
 {
+    /* Just a bad-sized payload. Shouldn't even have got here. Bad command. */
+    if ((len & 3) || (len > BL_SIZE))
+        return ACK_BAD_COMMAND;
+
+    /* Doesn't fit in our RAM buffer? Return a special error code. */
+    if (len > U_BUF_SZ)
+        return ACK_OUT_OF_SRAM;
+
     floppy_state = ST_update_bootloader;
     update.len = len;
 
     printk("Update Bootloader: %u bytes\n", len);
+
+    return ACK_OKAY;
 }
 
 static void update_continue(void)
@@ -1390,11 +1402,9 @@ static void process_command(void)
         uint32_t u_len = *(uint32_t *)&u_buf[2];
         uint32_t signature = *(uint32_t *)&u_buf[6];
         if (len != 10) goto bad_command;
-        if (u_len & 3) goto bad_command;
-        if (u_len > BL_SIZE) goto bad_command;
         if (signature != 0xdeafbee3) goto bad_command;
-        update_prep(u_len);
-        break;
+        u_buf[1] = update_prep(u_len);
+        goto out;
     }
     case CMD_SEEK: {
         int8_t cyl = u_buf[2];

+ 27 - 15
src/fw_update.c

@@ -10,13 +10,9 @@
  */
 
 #if MCU == STM32F1
-/*  8kB-64kB (56kB total) */
 #define FIRMWARE_START 0x08002000
-#define FIRMWARE_END   0x08010000
 #elif MCU == STM32F7 || MCU == AT32F4
-/* 16kB-64KB (48kB total) */
 #define FIRMWARE_START 0x08004000
-#define FIRMWARE_END   0x08010000
 #endif
 
 int EXC_reset(void) __attribute__((alias("main")));
@@ -32,7 +28,8 @@ static enum {
     ST_update,
 } state = ST_inactive;
 
-static uint8_t u_buf[2048] aligned(4);
+extern uint8_t u_buf[];
+#define U_BUF_SZ 2048
 static uint32_t u_prod;
 
 static bool_t upd_strapped;
@@ -81,23 +78,37 @@ static struct {
     uint32_t cur;
 } update;
 
-static void erase_old_firmware(void)
+static void erase_old_firmware(uint32_t len)
 {
     uint32_t p;
-    for (p = FIRMWARE_START; p < FIRMWARE_END; p += FLASH_PAGE_SIZE)
+    uint32_t start = FIRMWARE_START;
+    uint32_t end = start + len;
+    for (p = start; p < end; p += FLASH_PAGE_SIZE)
         fpec_page_erase(p);
 }
 
-static void update_prep(uint32_t len)
+static uint8_t update_prep(uint32_t len)
 {
+    unsigned int flash_end = (uint32_t)_stext + (flash_kb << 10);
+
+    /* Just a bad-sized payload. Shouldn't even have got here. Bad command. */
+    if (len & 3)
+        return ACK_BAD_COMMAND;
+
+    /* Doesn't fit in available Flash memory? Return a special error code. */
+    if (len > (flash_end - FIRMWARE_START))
+        return ACK_OUT_OF_FLASH;
+
     fpec_init();
-    erase_old_firmware();
+    erase_old_firmware(len);
 
     state = ST_update;
     update.cur = 0;
     update.len = len;
 
     printk("Update: %u bytes\n", len);
+
+    return ACK_OKAY;
 }
 
 static void update_continue(void)
@@ -105,7 +116,7 @@ static void update_continue(void)
     int len;
 
     if ((len = ep_rx_ready(EP_RX)) >= 0) {
-        len = min_t(int, len, update.len - update.cur);
+        len = min_t(int, len, update.len - update.cur - u_prod);
         usb_read(EP_RX, &u_buf[u_prod], len);
         u_prod += len;
     }
@@ -125,7 +136,7 @@ static void update_continue(void)
         state = ST_command_wait;
         end_command(u_buf, 1);
         if (crc)
-            erase_old_firmware();
+            erase_old_firmware(update.len);
     }
 }
 
@@ -155,9 +166,8 @@ static void process_command(void)
     case CMD_UPDATE: {
         uint32_t u_len = *(uint32_t *)&u_buf[2];
         if (len != 6) goto bad_command;
-        if (u_len & 3) goto bad_command;
-        update_prep(u_len);
-        break;
+        u_buf[1] = update_prep(u_len);
+        goto out;
     }
     case CMD_SWITCH_FW_MODE: {
         uint8_t mode = u_buf[2];
@@ -200,7 +210,7 @@ static void update_process(void)
         }
 
         len = ep_rx_ready(EP_RX);
-        if ((len >= 0) && (len < (sizeof(u_buf)-u_prod))) {
+        if ((len >= 0) && (len < (U_BUF_SZ-u_prod))) {
             usb_read(EP_RX, &u_buf[u_prod], len);
             u_prod += len;
         }
@@ -316,8 +326,10 @@ int main(void)
     board_init();
 
     printk("\n** Greaseweazle Update Bootloader v%u.%u\n", fw_major, fw_minor);
+#if MCU != STM32F1 /* Not enough Flash space */
     printk("** Keir Fraser <keir.xen@gmail.com>\n");
     printk("** https://github.com/keirf/Greaseweazle\n\n");
+#endif
 
     usb_init();
 

+ 7 - 3
src/mcu/at32f4/floppy.c

@@ -51,14 +51,18 @@ typedef uint16_t timcnt_t;
 #define irq_index 40
 void IRQ_40(void) __attribute__((alias("IRQ_INDEX_changed"))); /* EXTI15_10 */
 
-/* We sometimes cast u_buf to uint32_t[], hence the alignment constraint. */
-#define U_BUF_SZ 16384
-static uint8_t u_buf[U_BUF_SZ] aligned(4);
+static unsigned int U_BUF_SZ;
 
 static void floppy_mcu_init(void)
 {
     const struct pin_mapping *mpin;
     const struct pin_mapping *upin;
+    unsigned int avail_kb;
+
+    avail_kb = sram_kb - ((((unsigned long)_ebss - 0x20000000) + 1023) >> 10);
+    for (U_BUF_SZ = 128; U_BUF_SZ > avail_kb; U_BUF_SZ >>= 1)
+        continue;
+    U_BUF_SZ <<= 10;
 
     /* Map PA15 -> TIM2 Ch1. */
     afio->mapr = (AFIO_MAPR_SWJ_ON_JTAG_OFF

+ 7 - 1
src/mcu/at32f4/stm32.c

@@ -12,6 +12,9 @@
 unsigned int FLASH_PAGE_SIZE = 2048;
 unsigned int at32f4_series;
 
+unsigned int flash_kb;
+unsigned int sram_kb;
+
 static void clock_init(void)
 {
     uint32_t cfgr;
@@ -115,14 +118,17 @@ static void peripheral_init(void)
 
 static void identify_mcu(void)
 {
-    unsigned int flash_kb = *(uint16_t *)0x1ffff7e0;
+    flash_kb = *(uint16_t *)0x1ffff7e0;
     if (flash_kb <= 128)
         FLASH_PAGE_SIZE = 1024;
 
     at32f4_series = *(uint8_t *)0x1ffff7f3; /* UID[95:88] */
     switch (at32f4_series) {
     case AT32F403:
+        sram_kb = 96;
+        break;
     case AT32F415:
+        sram_kb = 32;
         break;
     default:
         early_fatal(4);

+ 7 - 3
src/mcu/stm32f1/floppy.c

@@ -55,14 +55,18 @@ typedef uint16_t timcnt_t;
 #define irq_index 23
 void IRQ_23(void) __attribute__((alias("IRQ_INDEX_changed"))); /* EXTI9_5 */
 
-/* We sometimes cast u_buf to uint32_t[], hence the alignment constraint. */
-#define U_BUF_SZ 8192
-static uint8_t u_buf[U_BUF_SZ] aligned(4);
+static unsigned int U_BUF_SZ;
 
 static void floppy_mcu_init(void)
 {
     const struct pin_mapping *mpin;
     const struct pin_mapping *upin;
+    unsigned int avail_kb;
+
+    avail_kb = sram_kb - ((((unsigned long)_ebss - 0x20000000) + 1023) >> 10);
+    for (U_BUF_SZ = 128; U_BUF_SZ > avail_kb; U_BUF_SZ >>= 1)
+        continue;
+    U_BUF_SZ <<= 10;
 
     switch (gw_info.hw_submodel) {
     case F1SM_basic:

+ 14 - 0
src/mcu/stm32f1/stm32.c

@@ -9,6 +9,9 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
+unsigned int flash_kb;
+unsigned int sram_kb;
+
 static void clock_init(void)
 {
     /* Flash controller: reads require 2 wait states at 72MHz. */
@@ -71,9 +74,20 @@ static void peripheral_init(void)
     gpio_init(gpioc);
 }
 
+static void identify_mcu(void)
+{
+    flash_kb = *(volatile uint16_t *)0x1ffff7e0;
+    switch (flash_kb) {
+    case 16: sram_kb =  6; break; /* STM31F103x4 Low Density */
+    case 32: sram_kb = 10; break; /* STM32F103x6 Low Density */
+    default: sram_kb = 20; break; /* STM32F103xx Medium Density */
+    }
+}
+
 void stm32_init(void)
 {
     cortex_init();
+    identify_mcu();
     clock_init();
     peripheral_init();
     cpu_sync();

+ 0 - 2
src/mcu/stm32f7/floppy.c

@@ -55,9 +55,7 @@ typedef uint32_t timcnt_t;
 #define irq_index 8
 void IRQ_8(void) __attribute__((alias("IRQ_INDEX_changed"))); /* EXTI2 */
 
-/* We sometimes cast u_buf to uint32_t[], hence the alignment constraint. */
 #define U_BUF_SZ (128*1024)
-static uint8_t u_buf[U_BUF_SZ] aligned(4) section_ext_ram;
 
 static void floppy_mcu_init(void)
 {

+ 2 - 0
src/mcu/stm32f7/stm32.c

@@ -9,6 +9,8 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
+unsigned int flash_kb = 64;
+
 static void clock_init(void)
 {
     unsigned int hse = board_config->hse_mhz;