Browse Source

STM32F7: Update Bootloader

Keir Fraser 5 years ago
parent
commit
1e578a767d

+ 6 - 2
Makefile

@@ -28,8 +28,8 @@ dist:
 	mkdir -p $(PROJ)-$(VER)/alt
 	$(MAKE) clean
 	stm32=f1 $(MAKE) all blinky
-	cp -a $(PROJ)-$(VER).hex $(PROJ)-$(VER)/
-	cp -a $(PROJ)-$(VER).upd $(PROJ)-$(VER)/
+	cp -a $(PROJ)-$(VER).hex $(PROJ)-$(VER)/$(PROJ)-F1-$(VER).hex
+	cp -a $(PROJ)-$(VER).upd $(PROJ)-$(VER)/$(PROJ)-F1-$(VER).upd
 	cp -a blinky_test/Blinky.hex $(PROJ)-$(VER)/alt/Blinky_Test-$(VER).hex
 	cp -a COPYING $(PROJ)-$(VER)/
 	cp -a README.md $(PROJ)-$(VER)/
@@ -43,6 +43,10 @@ dist:
 		$(PROJ)-$(VER)/scripts/greaseweazle/tools/
 	cp -a RELEASE_NOTES $(PROJ)-$(VER)/
 	$(MAKE) clean
+	stm32=f7 $(MAKE) all
+	cp -a $(PROJ)-$(VER).hex $(PROJ)-$(VER)/$(PROJ)-F7-$(VER).hex
+	cp -a $(PROJ)-$(VER).upd $(PROJ)-$(VER)/$(PROJ)-F7-$(VER).upd
+	$(MAKE) clean
 	zip -r $(PROJ)-$(VER).zip $(PROJ)-$(VER)
 
 mrproper: clean

+ 1 - 1
blinky_test/Blinky.ld.S

@@ -4,4 +4,4 @@
 #define RAM_BASE   0x20000000
 #define RAM_LEN    20K
 
-#include "../scripts/stm32f1.ld.S"
+#include "../scripts/stm32.ld.S"

+ 1 - 1
blinky_test/Makefile

@@ -5,7 +5,7 @@ OBJS += cortex.o
 OBJS += stm32$(stm32).o
 OBJS += blinky.o
 OBJS += util.o
-OBJS += fpec.o
+OBJS += fpec_$(stm32).o
 OBJS += console.o
 OBJS += string.o
 

+ 13 - 2
bootloader/Bootloader.ld.S

@@ -1,7 +1,18 @@
+
+#if STM32F == 1
+
 #define FLASH_BASE 0x08000000
 #define FLASH_LEN  8K
-
 #define RAM_BASE   0x20000000
 #define RAM_LEN    20K
 
-#include "../scripts/stm32f1.ld.S"
+#elif STM32F == 7
+
+#define FLASH_BASE 0x08000000
+#define FLASH_LEN  16K
+#define RAM_BASE   0x20000000
+#define RAM_LEN    64K
+
+#endif
+
+#include "../scripts/stm32.ld.S"

+ 1 - 1
bootloader/Makefile

@@ -9,7 +9,7 @@ OBJS += string.o
 OBJS += cortex.o
 OBJS += stm32$(stm32).o
 OBJS += util.o
-OBJS-$(stm32f1) += fpec.o
+OBJS += fpec_$(stm32).o
 
 OBJS-$(debug) += console.o
 

+ 8 - 0
inc/cdc_acm_protocol.h

@@ -39,6 +39,9 @@
 #define CMD_GET_INDEX_TIMES 9
 /* CMD_SELECT, length=3, select_mask */
 #define CMD_SELECT         10
+/* CMD_SWITCH_FW_MODE, length=3, <mode> */
+#define CMD_SWITCH_FW_MODE 11
+#define CMD_MAX            11
 
 /* [BOOTLOADER] CMD_UPDATE, length=6, <update_len>. 
  * Host follows with <update_len> bytes.
@@ -78,6 +81,7 @@ struct packed gw_info {
     uint8_t max_index;
     uint8_t max_cmd;
     uint32_t sample_freq;
+    uint16_t hw_type;
 };
 
 /* CMD_READ_FLUX */
@@ -101,6 +105,10 @@ struct packed gw_delay {
     uint16_t auto_off;     /* msec */
 };
 
+/* CMD_SWITCH_FW_MODE */
+#define FW_MODE_BOOTLOADER 0
+#define FW_MODE_NORMAL     1
+
 /*
  * Local variables:
  * mode: C

+ 1 - 0
inc/usb.h

@@ -25,6 +25,7 @@ extern const struct usb_class_ops usb_cdc_acm_ops;
 
 /* Main entry points for USB processing. */
 void usb_init(void);
+void usb_deinit(void);
 void usb_process(void);
 
 /* Does OUT endpoint have data ready? If so return packet length, else -1. */

+ 6 - 2
scripts/greaseweazle/tools/update.py

@@ -7,7 +7,7 @@
 # This is free and unencumbered software released into the public domain.
 # See the file COPYING for more details, or visit <http://unlicense.org>.
 
-import sys, argparse, struct
+import sys, argparse, serial, struct
 import crcmod.predefined
 
 from greaseweazle.tools import util
@@ -36,7 +36,11 @@ def update_firmware(usb, args):
         print("** UPDATE FAILED: Please retry!")
         return
     print("Done.")
-    print("** Disconnect Greaseweazle and remove the Programming Jumper.")
+    
+    if usb.hw_type == 7:
+        util.usb_reopen(usb, is_update=False)
+    else:
+        print("** Disconnect Greaseweazle and remove the Programming Jumper.")
 
 
 def main(argv):

+ 32 - 4
scripts/greaseweazle/tools/util.py

@@ -7,7 +7,7 @@
 # This is free and unencumbered software released into the public domain.
 # See the file COPYING for more details, or visit <http://unlicense.org>.
 
-import os, sys, serial
+import os, sys, serial, struct, time
 
 from greaseweazle import version
 from greaseweazle import usb as USB
@@ -39,16 +39,39 @@ def with_drive_selected(fn, usb, args):
         usb.drive_select(False)
 
 
+def usb_reopen(usb, is_update):
+    mode = { False: 1, True: 0 }
+    try:
+        usb.switch_fw_mode(mode[is_update])
+    except (serial.SerialException, struct.error):
+        # Mac and Linux raise SerialException ("... returned no data")
+        # Win10 pyserial returns a short read which fails struct.unpack
+        pass
+    usb.ser.close()
+    for i in range(10):
+        time.sleep(0.5)
+        try:
+            usb.ser.open()
+        except serial.SerialException:
+            # Device not found
+            pass
+        else:
+            return USB.Unit(usb.ser)
+    return None
+
+
 def usb_open(devicename, is_update=False):
 
     usb = USB.Unit(serial.Serial(devicename))
 
-    print("** %s v%u.%u, Host Tools v%u.%u"
+    print("** %s v%u.%u [F%u], Host Tools v%u.%u"
           % (("Greaseweazle", "Bootloader")[usb.update_mode],
-             usb.major, usb.minor,
+             usb.major, usb.minor, usb.hw_type,
              version.major, version.minor))
 
     if usb.update_mode and not is_update:
+        if usb.hw_type == 7:
+            return usb_reopen(usb, is_update)
         print("Greaseweazle is in Firmware Update Mode:")
         print(" The only available action is \"update <update_file>\"")
         if usb.update_jumpered:
@@ -58,6 +81,8 @@ def usb_open(devicename, is_update=False):
         sys.exit(1)
 
     if is_update and not usb.update_mode:
+        if usb.hw_type == 7:
+            return usb_reopen(usb, is_update)
         print("Greaseweazle is in Normal Mode:")
         print(" To \"update\" you must install the Update Jumper")
         sys.exit(1)
@@ -65,7 +90,10 @@ def usb_open(devicename, is_update=False):
     if not usb.update_mode and usb.update_needed:
         print("Firmware is out of date: Require v%u.%u"
               % (version.major, version.minor))
-        print("Install the Update Jumper and \"update <update_file>\"")
+        if usb.hw_type == 7:
+            print("Run \"update <update_file>\"")
+        else:
+            print("Install the Update Jumper and \"update <update_file>\"")
         sys.exit(1)
 
     return usb

+ 12 - 2
scripts/greaseweazle/usb.py

@@ -28,6 +28,7 @@ class Cmd:
     GetFluxStatus   =  8
     GetIndexTimes   =  9
     Select          = 10
+    SwitchFwMode    = 11
     # Bootloader specific:
     Update          =  1
 
@@ -80,9 +81,12 @@ class Unit:
         self.reset()
         # Copy firmware info to instance variables (see above for definitions).
         self._send_cmd(struct.pack("3B", Cmd.GetInfo, 3, 0))
-        x = struct.unpack("<4BI24x", self.ser.read(32))
+        x = struct.unpack("<4BIH22x", self.ser.read(32))
         (self.major, self.minor, self.max_index,
-         self.max_cmd, self.sample_freq) = x
+         self.max_cmd, self.sample_freq, self.hw_type) = x
+        # Old firmware doesn't report HW type but runs on STM32F1 only.
+        if self.hw_type == 0:
+            self.hw_type = 1
         # Check whether firmware is in update mode: limited command set if so.
         self.update_mode = (self.max_index == 0)
         if self.update_mode:
@@ -150,6 +154,12 @@ class Unit:
         return x
 
 
+    ## switch_fw_mode:
+    ## Switch between update bootloader and main firmware.
+    def switch_fw_mode(self, mode):
+        self._send_cmd(struct.pack("3B", Cmd.SwitchFwMode, 3, int(mode)))
+
+
     ## update_firmware:
     ## Update Greaseweazle to the given new firmware.
     def update_firmware(self, dat):

+ 0 - 0
scripts/stm32f1.ld.S → scripts/stm32.ld.S


+ 13 - 2
src/Greaseweazle.ld.S

@@ -1,7 +1,18 @@
+
+#if STM32F == 1
+
 #define FLASH_BASE 0x08002000
 #define FLASH_LEN  56K
-
 #define RAM_BASE   0x20000000
 #define RAM_LEN    20K
 
-#include "../scripts/stm32f1.ld.S"
+#elif STM32F == 7
+
+#define FLASH_BASE 0x08004000
+#define FLASH_LEN  48K
+#define RAM_BASE   0x20000000
+#define RAM_LEN    64K
+
+#endif
+
+#include "../scripts/stm32.ld.S"

+ 1 - 0
src/cortex.c

@@ -117,6 +117,7 @@ void system_reset(void)
     IRQ_global_disable();
     printk("Resetting...\n");
     /* Request reset and loop waiting for it to happen. */
+    cpu_sync();
     scb->aircr = SCB_AIRCR_VECTKEY | SCB_AIRCR_SYSRESETREQ;
     for (;;) ;
 }

+ 16 - 2
src/floppy.c

@@ -241,8 +241,9 @@ void floppy_init(void)
 }
 
 static struct gw_info gw_info = {
-    .max_index = 15, .max_cmd = CMD_SELECT,
-    .sample_freq = 72000000u
+    .max_index = 15, .max_cmd = CMD_MAX,
+    .sample_freq = 72000000u,
+    .hw_type = STM32F
 };
 
 static void auto_off_arm(void)
@@ -837,6 +838,19 @@ static void process_command(void)
         drive_select(mask & 1);
         break;
     }
+    case CMD_SWITCH_FW_MODE: {
+        uint8_t mode = u_buf[2];
+        if ((len != 3) || (mode & ~1))
+            goto bad_command;
+        if (mode == FW_MODE_BOOTLOADER) {
+            usb_deinit();
+            delay_ms(500);
+            /* Poke a flag in SRAM1, picked up by the bootloader. */
+            *(volatile uint32_t *)0x20010000 = 0xdeadbeef;
+            system_reset();
+        }
+        break;
+    }
     default:
         goto bad_command;
     }

+ 1 - 1
src/fpec.c → src/fpec_f1.c

@@ -1,5 +1,5 @@
 /*
- * fpec.c
+ * fpec_f1.c
  * 
  * STM32F10x Flash Memory Program/Erase Controller (FPEC).
  * 

+ 61 - 0
src/fpec_f7.c

@@ -0,0 +1,61 @@
+/*
+ * fpec_f7.c
+ * 
+ * STM32F73x Flash Memory Program/Erase Controller (FPEC).
+ * 
+ * Written & released by Keir Fraser <keir.xen@gmail.com>
+ * 
+ * This is free and unencumbered software released into the public domain.
+ * See the file COPYING for more details, or visit <http://unlicense.org>.
+ */
+
+static void fpec_wait_and_clear(void)
+{
+    cpu_sync();
+    while (flash->sr & FLASH_SR_BSY)
+        continue;
+    flash->cr = 0;
+}
+
+void fpec_init(void)
+{
+    /* Unlock the FPEC. */
+    if (flash->cr & FLASH_CR_LOCK) {
+        flash->keyr = 0x45670123;
+        flash->keyr = 0xcdef89ab;
+    }
+
+    fpec_wait_and_clear();
+}
+
+void fpec_page_erase(uint32_t flash_address)
+{
+    int sector = (flash_address - 0x08000000) >> 14;
+    fpec_wait_and_clear();
+    flash->cr = FLASH_CR_PSIZE(2) | FLASH_CR_SER | FLASH_CR_SNB(sector);
+    flash->cr |= FLASH_CR_STRT;
+    fpec_wait_and_clear();
+}
+
+void fpec_write(const void *data, unsigned int size, uint32_t flash_address)
+{
+    uint16_t *_f = (uint16_t *)flash_address;
+    const uint16_t *_d = data;
+
+    fpec_wait_and_clear();
+    for (; size != 0; size -= 2) {
+        flash->cr = FLASH_CR_PSIZE(1) | FLASH_CR_PG;
+        *_f++ = *_d++; 
+        fpec_wait_and_clear();
+   }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 36 - 11
src/fw_update.c

@@ -9,9 +9,15 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
-/* Main bootloader: flashes the main firmware. */
+#if STM32F == 1
+/*  8kB-64kB (56kB total) */
 #define FIRMWARE_START 0x08002000
 #define FIRMWARE_END   0x08010000
+#elif STM32F == 7
+/* 16kB-64KB (48kB total) */
+#define FIRMWARE_START 0x08004000
+#define FIRMWARE_END   0x08010000
+#endif
 
 int EXC_reset(void) __attribute__((alias("main")));
 
@@ -28,9 +34,8 @@ static bool_t pa14_strapped;
 
 static struct gw_info gw_info = {
     /* Max Index == 0 signals that this is the Bootloader. */
-    .max_index = 0,
-    /* Only support two commands: GET_INFO and UPDATE. */
-    .max_cmd = CMD_UPDATE,
+    .max_index = 0, .max_cmd = CMD_MAX,
+    .hw_type = STM32F
 };
 
 static void update_reset(void)
@@ -135,6 +140,17 @@ static void process_command(void)
         update_prep(u_len);
         break;
     }
+    case CMD_SWITCH_FW_MODE: {
+        uint8_t mode = u_buf[2];
+        if ((len != 3) || (mode & ~1))
+            goto bad_command;
+        if (mode == FW_MODE_NORMAL) {
+            usb_deinit();
+            delay_ms(500);
+            system_reset();
+        }
+        break;
+    }
     default:
         goto bad_command;
     }
@@ -179,13 +195,8 @@ static void update_process(void)
     }
 }
 
-int main(void)
+static bool_t enter_bootloader(void)
 {
-    /* Relocate DATA. Initialise BSS. */
-    if (_sdat != _ldat)
-        memcpy(_sdat, _ldat, _edat-_sdat);
-    memset(_sbss, 0, _ebss-_sbss);
-
 #if STM32F == 1
     /* Turn on AFIO and GPIOA clocks. */
     rcc->apb2enr = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_AFIOEN;
@@ -204,9 +215,23 @@ int main(void)
 
     /* Enter update mode only if PA14 (DCLK) is strapped to GND. */
     pa14_strapped = !gpio_read_pin(gpioa, 14);
+    return pa14_strapped;
+#else
+    /* Check-and-clear a magic value poked into SRAM1 by the main firmware. */
+    bool_t match = (*(volatile uint32_t *)0x20010000 == 0xdeadbeef);
+    *(volatile uint32_t *)0x20010000 = 0;
+    return match;
 #endif
+}
+
+int main(void)
+{
+    /* Relocate DATA. Initialise BSS. */
+    if (_sdat != _ldat)
+        memcpy(_sdat, _ldat, _edat-_sdat);
+    memset(_sbss, 0, _ebss-_sbss);
 
-    if (!pa14_strapped) {
+    if (!enter_bootloader()) {
         /* Nope, so jump straight at the main firmware. */
         uint32_t sp = *(uint32_t *)FIRMWARE_START;
         uint32_t pc = *(uint32_t *)(FIRMWARE_START + 4);

+ 0 - 5
src/stm32f7.c

@@ -9,11 +9,6 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
-/* XXX */
-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) {}
-
 static void clock_init(void)
 {
     /* Flash controller: reads require 7 wait states at 216MHz. */

+ 18 - 0
src/usb/hw_dwc_otg.c

@@ -192,6 +192,24 @@ void usb_init(void)
     delay_ms(3);
 }
 
+void usb_deinit(void)
+{
+    switch (conf_port) {
+    case PORT_FS:
+        gpio_configure_pin(gpioa, 11, GPI_floating);
+        gpio_configure_pin(gpioa, 12, GPI_floating);
+        rcc->ahb2enr &= ~RCC_AHB2ENR_OTGFSEN;
+        break;
+    case PORT_HS:
+        gpio_configure_pin(gpiob, 14, GPI_floating);
+        gpio_configure_pin(gpiob, 15, GPI_floating);
+        rcc->ahb1enr &= ~RCC_AHB1ENR_OTGHSEN;
+        break;
+    default:
+        ASSERT(0);
+    }
+}
+
 int ep_rx_ready(uint8_t ep)
 {
     return eps[ep].rx_ready ? eps[ep].rx_count : -1;

+ 6 - 0
src/usb/hw_f1.c

@@ -42,6 +42,12 @@ void usb_init(void)
     gpio_configure_pin(gpioa, 0, GPO_pushpull(_2MHz, HIGH));
 }
 
+void usb_deinit(void)
+{
+    gpio_configure_pin(gpioa, 0, GPI_floating);
+    rcc->apb1enr &= ~RCC_APB1ENR_USBEN;
+}
+
 #if 0
 static void dump_ep(uint8_t ep)
 {