diff options
author | Andrej Shadura <andrewsh@debian.org> | 2020-10-30 17:43:02 +0100 |
---|---|---|
committer | Andrej Shadura <andrewsh@debian.org> | 2020-10-30 17:43:02 +0100 |
commit | a06205fb5515908827dff6f6714e7ecc9776d81f (patch) | |
tree | 0fa4cbccf21c16db631f4944b25e2a1a2d6951eb | |
parent | f2e31af0dd5cad0b5c17d970be17f7e44814cce0 (diff) | |
parent | aa4fe601399f34a787731133ff31e7154a8f3f8c (diff) |
Update upstream source from tag 'upstream/0.5+git20200914+2b0f078'
Update to upstream version '0.5+git20200914+2b0f078'
with Debian dir 7967e499074a7740119e2bd266cdcf686e982b18
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | Makefile.am | 39 | ||||
-rw-r--r-- | compiler.h | 38 | ||||
-rw-r--r-- | configure.ac | 33 | ||||
-rw-r--r--[-rwxr-xr-x] | dev_table.c | 46 | ||||
-rw-r--r-- | i2c.c | 21 | ||||
-rw-r--r-- | init.c | 122 | ||||
-rw-r--r-- | init.h | 1 | ||||
-rw-r--r-- | main.c | 176 | ||||
-rw-r--r-- | parsers/Makefile.am | 11 | ||||
-rw-r--r-- | parsers/binary.c | 16 | ||||
-rw-r--r-- | parsers/hex.c | 8 | ||||
-rw-r--r-- | port.h | 1 | ||||
-rw-r--r-- | serial_posix.c | 36 | ||||
-rw-r--r-- | serial_w32.c | 18 | ||||
-rw-r--r-- | stm32.c | 78 | ||||
-rw-r--r-- | stm32.h | 5 | ||||
-rw-r--r-- | stm32flash.1 | 42 | ||||
-rw-r--r-- | utils.c | 7 | ||||
-rw-r--r-- | utils.h | 3 |
21 files changed, 583 insertions, 129 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a79f87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +stm32flash +*.o +parsers/parsers.a @@ -1,6 +1,14 @@ PREFIX = /usr/local CFLAGS += -Wall -g +ifndef CC + $(error CC is not defined) +endif + +ifndef AR + $(error AR is not defined) +endif + INSTALL = install OBJS = dev_table.o \ diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..c877c7b --- /dev/null +++ b/Makefile.am @@ -0,0 +1,39 @@ + +SUBDIRS = parsers + +bin_PROGRAMS = stm32flash + +AUTOMAKE_OPTIONS = subdir-objects + + +stm32flash_SOURCES = \ + dev_table.c \ + i2c.c \ + init.c \ + main.c \ + port.c \ + serial_common.c \ + serial_platform.c\ + stm32.c \ + utils.c + +stm32flash_LDADD = ${top_builddir}/parsers/parsers.la + +stm32flash_CFLAGS = \ + -g3 \ + -Os \ + -Wall \ + -Wextra \ + -I$(srcdir)/parsers + + +stm32flash_LDFLAGS = \ + -Wall \ + -g3 \ + -Wextra + +all: + + +.PHONY: all +.SILENT: all diff --git a/compiler.h b/compiler.h new file mode 100644 index 0000000..57e83ff --- /dev/null +++ b/compiler.h @@ -0,0 +1,38 @@ +/* + stm32flash - Open Source ST STM32 flash program for *nix + Copyright (C) 2017 Antonio Borneo <borneo.antonio@gmail.com> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef _H_COMPILER +#define _H_COMPILER + +#if defined(__GNUC__) +#undef __unused +#undef __maybe_unused +#define __unused __attribute__ ((unused)) +#define __maybe_unused __attribute__ ((unused)) +#endif + +#ifndef __unused +#define __unused +#endif + +#ifndef __maybe_unused +#define __maybe_unused +#endif + +#endif /* _H_COMPILER */ diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..c05a9e0 --- /dev/null +++ b/configure.ac @@ -0,0 +1,33 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.68]) +AC_INIT([stm32flash], [0.5], []) + +# Checks for programs. +AM_INIT_AUTOMAKE([foreign -Wall]) +AC_PROG_CC +AM_PROG_CC_C_O +AM_PROG_AR +AM_PROG_AS +LT_INIT +AC_CONFIG_MACRO_DIR([m4]) + +# Checks for programs. + +# Checks for libraries. + +# Checks for header files. + +# Checks for typedefs, structures, and compiler characteristics. + +# Checks for library functions. + + +AC_CONFIG_FILES( + [ + Makefile + parsers/Makefile + ]) + +AC_OUTPUT diff --git a/dev_table.c b/dev_table.c index f4ada72..ec6d599 100755..100644 --- a/dev_table.c +++ b/dev_table.c @@ -36,10 +36,11 @@ */ /* fixed size pages */ -static uint32_t p_128[] = { SZ_128, 0 }; -static uint32_t p_256[] = { SZ_256, 0 }; -static uint32_t p_1k[] = { SZ_1K, 0 }; -static uint32_t p_2k[] = { SZ_2K, 0 }; +static uint32_t p_128[] = { SZ_128, 0 }; +static uint32_t p_256[] = { SZ_256, 0 }; +static uint32_t p_1k[] = { SZ_1K, 0 }; +static uint32_t p_2k[] = { SZ_2K, 0 }; +static uint32_t p_128k[] = { SZ_128K, 0 }; /* F2 and F4 page size */ static uint32_t f2f4[] = { SZ_16K, SZ_16K, SZ_16K, SZ_16K, SZ_64K, SZ_128K, 0 }; /* F4 dual bank page size */ @@ -51,16 +52,21 @@ static uint32_t f4db[] = { static uint32_t f7[] = { SZ_32K, SZ_32K, SZ_32K, SZ_32K, SZ_128K, SZ_256K, 0 }; /* - * Device table, corresponds to the "Bootloader device-dependant parameters" + * Device table, corresponds to the "Bootloader device-dependent parameters" * table in ST document AN2606. * Note that the option bytes upper range is inclusive! + * + * When adding new devices, please double-check agaist the chip-specific + * sections and reference manuals, where also flash address and option + * byte ranges can be found. In the commit message, please reference the + * source documents including their revision. */ const stm32_dev_t devices[] = { /* ID "name" SRAM-address-range FLASH-address-range PPS PSize Option-byte-addr-range System-mem-addr-range Flags */ /* F0 */ {0x440, "STM32F030x8/F05xxx" , 0x20000800, 0x20002000, 0x08000000, 0x08010000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFEC00, 0x1FFFF800, 0}, - {0x442, "STM32F030xC/F09xxx" , 0x20001800, 0x20008000, 0x08000000, 0x08040000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFC800, 0x1FFFF800, F_OBLL}, {0x444, "STM32F03xx4/6" , 0x20000800, 0x20001000, 0x08000000, 0x08008000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFEC00, 0x1FFFF800, 0}, + {0x442, "STM32F030xC/F09xxx" , 0x20001800, 0x20008000, 0x08000000, 0x08040000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFD800, 0x1FFFF800, F_OBLL}, {0x445, "STM32F04xxx/F070x6" , 0x20001800, 0x20001800, 0x08000000, 0x08008000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFC400, 0x1FFFF800, 0}, {0x448, "STM32F070xB/F071xx/F72xx" , 0x20001800, 0x20004000, 0x08000000, 0x08020000, 2, p_2k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFC800, 0x1FFFF800, 0}, /* F1 */ @@ -86,25 +92,41 @@ const stm32_dev_t devices[] = { {0x433, "STM32F401xD(E)" , 0x20003000, 0x20018000, 0x08000000, 0x08080000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, {0x458, "STM32F410xx" , 0x20003000, 0x20008000, 0x08000000, 0x08020000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, {0x431, "STM32F411xx" , 0x20003000, 0x20020000, 0x08000000, 0x08080000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x441, "STM32F412xx" , 0x20003000, 0x20040000, 0x08000000, 0x08100000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, {0x421, "STM32F446xx" , 0x20003000, 0x20020000, 0x08000000, 0x08080000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, - {0x434, "STM32F469xx" , 0x20003000, 0x20060000, 0x08000000, 0x08200000, 1, f4db , 0x1FFEC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x434, "STM32F469xx/479xx" , 0x20003000, 0x20060000, 0x08000000, 0x08200000, 1, f4db , 0x1FFEC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + {0x463, "STM32F413xx/423xx" , 0x20003000, 0x20050000, 0x08000000, 0x08180000, 1, f2f4 , 0x1FFFC000, 0x1FFFC00F, 0x1FFF0000, 0x1FFF7800, 0}, + /* G0 */ + /* The chips with ID 0x466 have a mismatch in AN2606 Rev 44, where table 141 claims 0x2000000 - 0x20000fff to be readable + * as opposed to the correct table 85, where this range is correctly marked as reserved by the bootloader + */ + {0x466, "STM32G03xxx/04xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 1, p_2k , 0x1FFF7800, 0x1FFF787F, 0x1FFF0000, 0x1FFF7000, 0}, + {0x460, "STM32G07xxx/08xxx" , 0x20003000, 0x20009000, 0x08000000, 0x08020000, 1, p_2k , 0x1FFF7800, 0x1FFF787F, 0x1FFF0000, 0x1FFF7000, 0}, /* F7 */ + {0x452, "STM32F72xxx/73xxx" , 0x20004000, 0x20040000, 0x08000000, 0x08080000, 1, f2f4 , 0x1FFF0000, 0x1FFF001F, 0x1FF00000, 0x1FF0EDC0, 0}, {0x449, "STM32F74xxx/75xxx" , 0x20004000, 0x20050000, 0x08000000, 0x08100000, 1, f7 , 0x1FFF0000, 0x1FFF001F, 0x1FF00000, 0x1FF0EDC0, 0}, + {0x451, "STM32F76xxx/77xxx" , 0x20004000, 0x20080000, 0x08000000, 0x08200000, 1, f7 , 0x1FFF0000, 0x1FFF001F, 0x1FF00000, 0x1FF0EDC0, 0}, + /* H7 */ + {0x450, "STM32H74xxx/75xxx" , 0x20004100, 0x20020000, 0x08000000, 0x08200000, 1, p_128k, 0 , 0 , 0x1FF00000, 0x1FF1E800, 0}, /* L0 */ + {0x457, "STM32L01xxx/02xxx" , 0x20000800, 0x20000800, 0x08000000, 0x08004000, 32, p_128 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, 0}, {0x425, "STM32L031xx/041xx" , 0x20001000, 0x20002000, 0x08000000, 0x08008000, 32, p_128 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, 0}, - {0x417, "STM32L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, p_128 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, 0}, + {0x417, "STM32L05xxx/06xxx" , 0x20001000, 0x20002000, 0x08000000, 0x08010000, 32, p_128 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, F_NO_ME}, {0x447, "STM32L07xxx/08xxx" , 0x20002000, 0x20005000, 0x08000000, 0x08030000, 32, p_128 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF02000, 0}, /* L1 */ {0x416, "STM32L1xxx6(8/B)" , 0x20000800, 0x20004000, 0x08000000, 0x08020000, 16, p_256 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, F_NO_ME}, - {0x429, "STM32L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, p_256 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, 0}, - {0x427, "STM32L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08040000, 16, p_256 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF02000, 0}, + {0x429, "STM32L1xxx6(8/B)A" , 0x20001000, 0x20008000, 0x08000000, 0x08020000, 16, p_256 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF01000, F_NO_ME}, + {0x427, "STM32L1xxxC" , 0x20001000, 0x20008000, 0x08000000, 0x08040000, 16, p_256 , 0x1FF80000, 0x1FF8001F, 0x1FF00000, 0x1FF02000, F_NO_ME}, {0x436, "STM32L1xxxD" , 0x20001000, 0x2000C000, 0x08000000, 0x08060000, 16, p_256 , 0x1FF80000, 0x1FF8009F, 0x1FF00000, 0x1FF02000, 0}, {0x437, "STM32L1xxxE" , 0x20001000, 0x20014000, 0x08000000, 0x08080000, 16, p_256 , 0x1FF80000, 0x1FF8009F, 0x1FF00000, 0x1FF02000, F_NO_ME}, /* L4 */ - {0x415, "STM32L476xx/486xx" , 0x20003100, 0x20018000, 0x08000000, 0x08100000, 1, p_2k , 0x1FFF7800, 0x1FFFF80F, 0x1FFF0000, 0x1FFF7000, 0}, + {0x435, "STM32L43xxx/44xxx" , 0x20003100, 0x2000C000, 0x08000000, 0x08040000, 1, p_2k , 0x1FFF7800, 0x1FFF780F, 0x1FFF0000, 0x1FFF7000, 0}, + {0x462, "STM32L45xxx/46xxx" , 0x20003100, 0x20020000, 0x08000000, 0x08080000, 1, p_2k , 0x1FFF7800, 0x1FFF780F, 0x1FFF0000, 0x1FFF7000, F_PEMPTY}, + {0x415, "STM32L47xxx/48xxx" , 0x20003100, 0x20018000, 0x08000000, 0x08100000, 1, p_2k , 0x1FFF7800, 0x1FFFF80F, 0x1FFF0000, 0x1FFF7000, 0}, + {0x461, "STM32L496xx/4A6xx" , 0x20003100, 0x20040000, 0x08000000, 0x08100000, 1, p_2k , 0x1FFF7800, 0x1FFFF80F, 0x1FFF0000, 0x1FFF7000, 0}, /* These are not (yet) in AN2606: */ {0x641, "Medium_Density PL" , 0x20000200, 0x20005000, 0x08000000, 0x08020000, 4, p_1k , 0x1FFFF800, 0x1FFFF80F, 0x1FFFF000, 0x1FFFF800, 0}, {0x9a8, "STM32W-128K" , 0x20000200, 0x20002000, 0x08000000, 0x08020000, 4, p_1k , 0x08040800, 0x0804080F, 0x08040000, 0x08040800, 0}, {0x9b0, "STM32W-256K" , 0x20000200, 0x20004000, 0x08000000, 0x08040000, 4, p_2k , 0x08040800, 0x0804080F, 0x08040000, 0x08040800, 0}, - {0x0} + { /* sentinel */ } }; @@ -28,14 +28,15 @@ #include <sys/types.h> #include <sys/stat.h> +#include "compiler.h" #include "serial.h" #include "port.h" #if !defined(__linux__) -static port_err_t i2c_open(struct port_interface *port, - struct port_options *ops) +static port_err_t i2c_open(struct port_interface __unused *port, + struct port_options __unused *ops) { return PORT_ERR_NODEV; } @@ -149,7 +150,7 @@ static port_err_t i2c_read(struct port_interface *port, void *buf, if (h == NULL) return PORT_ERR_UNKNOWN; ret = read(h->fd, buf, nbyte); - if (ret != nbyte) + if (ret != (int)nbyte) return PORT_ERR_UNKNOWN; return PORT_ERR_OK; } @@ -164,13 +165,14 @@ static port_err_t i2c_write(struct port_interface *port, void *buf, if (h == NULL) return PORT_ERR_UNKNOWN; ret = write(h->fd, buf, nbyte); - if (ret != nbyte) + if (ret != (int)nbyte) return PORT_ERR_UNKNOWN; return PORT_ERR_OK; } -static port_err_t i2c_gpio(struct port_interface *port, serial_gpio_t n, - int level) +static port_err_t i2c_gpio(struct port_interface __unused *port, + serial_gpio_t __unused n, + int __unused level) { return PORT_ERR_OK; } @@ -194,11 +196,18 @@ static struct varlen_cmd i2c_cmd_get_reply[] = { { /* sentinel */ } }; +static port_err_t i2c_flush(struct port_interface __unused *port) +{ + /* We shouldn't need to flush I2C */ + return PORT_ERR_OK; +} + struct port_interface port_i2c = { .name = "i2c", .flags = PORT_STRETCH_W, .open = i2c_open, .close = i2c_close, + .flush = i2c_flush, .read = i2c_read, .write = i2c_write, .gpio = i2c_gpio, @@ -28,10 +28,15 @@ #include <string.h> #include <unistd.h> #include <sys/stat.h> + +#include "compiler.h" #include "init.h" #include "serial.h" #include "stm32.h" #include "port.h" +#include "utils.h" + +extern FILE *diag; struct gpio_list { struct gpio_list *next; @@ -40,6 +45,7 @@ struct gpio_list { int exported; /* 0 if gpio should be unexported. */ }; +#if defined(__linux__) static int write_to(const char *filename, const char *value) { int fd, ret; @@ -59,17 +65,10 @@ static int write_to(const char *filename, const char *value) return 1; } -#if !defined(__linux__) -static int drive_gpio(int n, int level, struct gpio_list **gpio_to_release) -{ - fprintf(stderr, "GPIO control only available in Linux\n"); - return 0; -} -#else static int read_from(const char *filename, char *buf, size_t len) { int fd, ret; - ssize_t n = 0; + size_t n = 0; fd = open(filename, O_RDONLY); if (fd < 0) { @@ -142,7 +141,6 @@ static int drive_gpio(int n, int level, struct gpio_list **gpio_to_release) return write_to(file, level ? "high" : "low"); } -#endif static int release_gpio(int n, int input, int exported) { @@ -159,14 +157,34 @@ static int release_gpio(int n, int input, int exported) return 1; } +#else +static int drive_gpio(int __unused n, int __unused level, + struct gpio_list __unused **gpio_to_release) +{ + fprintf(stderr, "GPIO control only available in Linux\n"); + return 0; +} +#endif -static int gpio_sequence(struct port_interface *port, const char *s, size_t l) +static int gpio_sequence(struct port_interface *port, const char *seq, size_t len_seq) { - struct gpio_list *gpio_to_release = NULL, *to_free; - int ret, level, gpio; + struct gpio_list *gpio_to_release = NULL; +#if defined(__linux__) + struct gpio_list *to_free; +#endif + int ret = 0, level, gpio; + int sleep_time = 0; + int delimiter = 0; + const char *sig_str = NULL; + const char *s = seq; + size_t l = len_seq; + + fprintf(diag, "\nGPIO sequence start\n"); + while (ret == 0 && *s && l > 0) { + sig_str = NULL; + sleep_time = 0; + delimiter = 0; - ret = 1; - while (ret == 1 && *s && l > 0) { if (*s == '-') { level = 0; s++; @@ -180,48 +198,78 @@ static int gpio_sequence(struct port_interface *port, const char *s, size_t l) s++; l--; } - } else if (!strncmp(s, "rts", 3)) { + } else if (l >= 3 && !strncmp(s, "rts", 3)) { + sig_str = s; gpio = -GPIO_RTS; s += 3; l -= 3; - } else if (!strncmp(s, "dtr", 3)) { + } else if (l >= 3 && !strncmp(s, "dtr", 3)) { + sig_str = s; gpio = -GPIO_DTR; s += 3; l -= 3; - } else if (!strncmp(s, "brk", 3)) { + } else if (l >= 3 && !strncmp(s, "brk", 3)) { + sig_str = s; gpio = -GPIO_BRK; s += 3; l -= 3; - } else { - fprintf(stderr, "Character \'%c\' is not a digit\n", *s); - ret = 0; - break; - } - - if (*s && (l > 0)) { + } else if (*s && (l > 0)) { + delimiter = 1; + /* The ',' delimiter adds a 100 ms delay between signal toggles. + * i.e -rts,dtr will reset rts, wait 100 ms, set dtr. + * + * The '&' delimiter adds no delay between signal toggles. + * i.e -rts&dtr will reset rts and immediately set dtr. + * + * Example: -rts&dtr,,,rts,-dtr will reset rts and set dtr + * without delay, then wait 300 ms, set rts, wait 100 ms, reset dtr. + */ if (*s == ',') { s++; l--; + sleep_time = 100000; + } else if (*s == '&') { + s++; + l--; } else { - fprintf(stderr, "Character \'%c\' is not a separator\n", *s); - ret = 0; + fprintf(stderr, "Character \'%c\' is not a valid signal or separator\n", *s); + ret = 1; break; } + } else { + /* E.g. modifier without signal */ + fprintf(stderr, "Invalid sequence %.*s\n", (int) len_seq, seq); + ret = 1; + break; + } + + if (!delimiter) { /* actual gpio/port signal driving */ + if (gpio < 0) { + gpio = -gpio; + fprintf(diag, " setting port signal %.3s to %i... ", sig_str, level); + ret = (port->gpio(port, gpio, level) != PORT_ERR_OK); + printStatus(diag, ret); + } else { + fprintf(diag, " setting gpio %i to %i... ", gpio, level); + ret = (drive_gpio(gpio, level, &gpio_to_release) != 1); + printStatus(diag, ret); + } } - if (gpio < 0) - ret = (port->gpio(port, -gpio, level) == PORT_ERR_OK); - else - ret = drive_gpio(gpio, level, &gpio_to_release); - usleep(100000); - } + if (sleep_time) { + fprintf(diag, " delay %i us\n", sleep_time); + usleep(sleep_time); + } + } +#if defined(__linux__) while (gpio_to_release) { release_gpio(gpio_to_release->gpio, gpio_to_release->input, gpio_to_release->exported); to_free = gpio_to_release; gpio_to_release = gpio_to_release->next; free(to_free); } - usleep(500000); +#endif + fprintf(diag, "GPIO sequence end\n\n"); return ret; } @@ -239,7 +287,7 @@ static int gpio_bl_entry(struct port_interface *port, const char *seq) return gpio_sequence(port, seq, s - seq); } -static int gpio_bl_exit(struct port_interface *port, const char *seq) +int gpio_bl_exit(struct port_interface *port, const char *seq) { char *s; @@ -258,7 +306,7 @@ int init_bl_entry(struct port_interface *port, const char *seq) if (seq) return gpio_bl_entry(port, seq); - return 1; + return 0; } int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq) @@ -266,7 +314,5 @@ int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq) if (seq && strchr(seq, ':')) return gpio_bl_exit(port, seq); - if (stm32_reset_device(stm) != STM32_ERR_OK) - return 0; - return 1; + return stm32_reset_device(stm); } @@ -27,5 +27,6 @@ int init_bl_entry(struct port_interface *port, const char *seq); int init_bl_exit(stm32_t *stm, struct port_interface *port, const char *seq); +int gpio_bl_exit(struct port_interface *port, const char *seq); #endif @@ -27,6 +27,7 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <signal.h> #include "init.h" #include "utils.h" @@ -38,13 +39,17 @@ #include "parsers/binary.h" #include "parsers/hex.h" +#if defined(__WIN32__) || defined(__CYGWIN__) +#include <windows.h> +#endif + #define VERSION "0.5" /* device globals */ stm32_t *stm = NULL; - void *p_st = NULL; parser_t *parser = NULL; +struct port_interface *port = NULL; /* settings */ struct port_options port_opts = { @@ -76,7 +81,9 @@ int retry = 10; char exec_flag = 0; uint32_t execute = 0; char init_flag = 1; +int use_stdinout = 0; char force_binary = 0; +FILE *diag; char reset_flag = 0; char *filename; char *gpio_seq = NULL; @@ -127,6 +134,17 @@ static int is_addr_in_flash(uint32_t addr) return addr >= stm->dev->fl_start && addr < stm->dev->fl_end; } +static int is_addr_in_opt_bytes(uint32_t addr) +{ + /* option bytes upper range is inclusive in our device table */ + return addr >= stm->dev->opt_start && addr <= stm->dev->opt_end; +} + +static int is_addr_in_sysmem(uint32_t addr) +{ + return addr >= stm->dev->mem_start && addr < stm->dev->mem_end; +} + /* returns the page that contains address "addr" */ static int flash_addr_to_page_floor(uint32_t addr) { @@ -191,22 +209,54 @@ static uint32_t flash_page_to_addr(int page) return addr; } + +#if defined(__WIN32__) || defined(__CYGWIN__) +BOOL CtrlHandler( DWORD fdwCtrlType ) +{ + fprintf(stderr, "\nCaught signal %lu\n",fdwCtrlType); + if (p_st && parser ) parser->close(p_st); + if (stm ) stm32_close (stm); + if (port) port->close(port); + exit(1); +} +#else +void sighandler(int s){ + fprintf(stderr, "\nCaught signal %d\n",s); + if (p_st && parser ) parser->close(p_st); + if (stm ) stm32_close (stm); + if (port) port->close(port); + exit(1); +} +#endif + int main(int argc, char* argv[]) { - struct port_interface *port = NULL; int ret = 1; stm32_err_t s_err; parser_err_t perr; - FILE *diag = stdout; + diag = stdout; - fprintf(diag, "stm32flash " VERSION "\n\n"); - fprintf(diag, "http://stm32flash.sourceforge.net/\n\n"); if (parse_options(argc, argv) != 0) goto close; - if ((action == ACT_READ) && filename[0] == '-') { + if (action == ACT_READ && use_stdinout) { diag = stderr; } + fprintf(diag, "stm32flash " VERSION "\n\n"); + fprintf(diag, "http://stm32flash.sourceforge.net/\n\n"); + +#if defined(__WIN32__) || defined(__CYGWIN__) + SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE ); +#else + struct sigaction sigIntHandler; + + sigIntHandler.sa_handler = sighandler; + sigemptyset(&sigIntHandler.sa_mask); + sigIntHandler.sa_flags = 0; + + sigaction(SIGINT, &sigIntHandler, NULL); +#endif + if (action == ACT_WRITE) { /* first try hex */ if (!force_binary) { @@ -259,8 +309,14 @@ int main(int argc, char* argv[]) { } fprintf(diag, "Interface %s: %s\n", port->name, port->get_cfg_str(port)); - if (init_flag && init_bl_entry(port, gpio_seq) == 0) + if (init_flag && init_bl_entry(port, gpio_seq)){ + ret = 1; + fprintf(stderr, "Failed to send boot enter sequence\n"); goto close; + } + + port->flush(port); + stm = stm32_init(port, init_flag); if (!stm) goto close; @@ -271,8 +327,8 @@ int main(int argc, char* argv[]) { fprintf(diag, "Option 2 : 0x%02x\n", stm->option2); } fprintf(diag, "Device ID : 0x%04x (%s)\n", stm->pid, stm->dev->name); - fprintf(diag, "- RAM : %dKiB (%db reserved by bootloader)\n", (stm->dev->ram_end - 0x20000000) / 1024, stm->dev->ram_start - 0x20000000); - fprintf(diag, "- Flash : %dKiB (size first sector: %dx%d)\n", (stm->dev->fl_end - stm->dev->fl_start ) / 1024, stm->dev->fl_pps, stm->dev->fl_ps[0]); + fprintf(diag, "- RAM : Up to %dKiB (%db reserved by bootloader)\n", (stm->dev->ram_end - 0x20000000) / 1024, stm->dev->ram_start - 0x20000000); + fprintf(diag, "- Flash : Up to %dKiB (size first sector: %dx%d)\n", (stm->dev->fl_end - stm->dev->fl_start ) / 1024, stm->dev->fl_pps, stm->dev->fl_ps[0]); fprintf(diag, "- Option RAM : %db\n", stm->dev->opt_end - stm->dev->opt_start + 1); fprintf(diag, "- System RAM : %dKiB\n", (stm->dev->mem_end - stm->dev->mem_start) / 1024); @@ -299,8 +355,17 @@ int main(int argc, char* argv[]) { no_erase = 1; if (is_addr_in_ram(start)) end = stm->dev->ram_end; - else - end = start + sizeof(uint32_t); + else if (is_addr_in_opt_bytes(start)) + end = stm->dev->opt_end + 1; + else if (is_addr_in_sysmem(start)) + end = stm->dev->mem_end; + else { + /* Unknown territory */ + if (readwrite_len) + end = start + readwrite_len; + else + end = start + sizeof(uint32_t); + } } if (readwrite_len && (end > start + readwrite_len)) @@ -379,20 +444,30 @@ int main(int argc, char* argv[]) { ret = 0; goto close; } else if (action == ACT_READ_PROTECT) { - fprintf(stdout, "Read-Protecting flash\n"); + fprintf(diag, "Read-Protecting flash\n"); /* the device automatically performs a reset after the sending the ACK */ reset_flag = 0; - stm32_readprot_memory(stm); - fprintf(stdout, "Done.\n"); + s_err = stm32_readprot_memory(stm); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read-protect flash\n"); + goto close; + } + fprintf(diag, "Done.\n"); + ret = 0; } else if (action == ACT_READ_UNPROTECT) { - fprintf(stdout, "Read-UnProtecting flash\n"); + fprintf(diag, "Read-UnProtecting flash\n"); /* the device automatically performs a reset after the sending the ACK */ reset_flag = 0; - stm32_runprot_memory(stm); - fprintf(stdout, "Done.\n"); + s_err = stm32_runprot_memory(stm); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to read-unprotect flash\n"); + goto close; + } + fprintf(diag, "Done.\n"); + ret = 0; } else if (action == ACT_ERASE_ONLY) { ret = 0; - fprintf(stdout, "Erasing flash\n"); + fprintf(diag, "Erasing flash\n"); if (num_pages != STM32_MASS_ERASE && (start != flash_page_to_addr(first_page) @@ -408,18 +483,23 @@ int main(int argc, char* argv[]) { ret = 1; goto close; } + ret = 0; } else if (action == ACT_WRITE_UNPROTECT) { fprintf(diag, "Write-unprotecting flash\n"); /* the device automatically performs a reset after the sending the ACK */ reset_flag = 0; - stm32_wunprot_memory(stm); + s_err = stm32_wunprot_memory(stm); + if (s_err != STM32_ERR_OK) { + fprintf(stderr, "Failed to write-unprotect flash\n"); + goto close; + } fprintf(diag, "Done.\n"); - + ret = 0; } else if (action == ACT_WRITE) { fprintf(diag, "Write to memory\n"); - off_t offset = 0; - ssize_t r; + unsigned int offset = 0; + unsigned int r; unsigned int size; unsigned int max_wlen, max_rlen; @@ -430,7 +510,7 @@ int main(int argc, char* argv[]) { max_rlen = max_rlen < max_wlen ? max_rlen : max_wlen; /* Assume data from stdin is whole device */ - if (filename[0] == '-' && filename[1] == '\0') + if (use_stdinout) size = end - start; else size = parser->size(p_st); @@ -440,7 +520,7 @@ int main(int argc, char* argv[]) { // if ((start % stm->dev->fl_ps[i]) != 0 || (end % stm->dev->fl_ps[i]) != 0) { // fprintf(stderr, "Specified start & length are invalid (must be page aligned)\n"); // goto close; - // } + // } // TODO: If writes are not page aligned, we should probably read out existing flash // contents first, so it can be preserved and combined with new data @@ -464,14 +544,14 @@ int main(int argc, char* argv[]) { goto close; if (len == 0) { - if (filename[0] == '-') { + if (use_stdinout) { break; } else { fprintf(stderr, "Failed to read input file\n"); goto close; } } - + again: s_err = stm32_write_memory(stm, addr, buffer, len); if (s_err != STM32_ERR_OK) { @@ -560,11 +640,17 @@ close: } if (stm && reset_flag) { - fprintf(diag, "\nResetting device... "); + fprintf(diag, "\nResetting device... \n"); fflush(diag); - if (init_bl_exit(stm, port, gpio_seq)) - fprintf(diag, "done.\n"); - else fprintf(diag, "failed.\n"); + if (init_bl_exit(stm, port, gpio_seq)) { + ret = 1; + fprintf(diag, "Reset failed.\n"); + } else + fprintf(diag, "Reset done.\n"); + } else if (port) { + /* Always run exit sequence if present */ + if (gpio_seq && strchr(gpio_seq, ':')) + ret = gpio_bl_exit(port, gpio_seq) || ret; } if (p_st ) parser->close(p_st); @@ -617,7 +703,8 @@ int parse_options(int argc, char *argv[]) } action = (c == 'r') ? ACT_READ : ACT_WRITE; filename = optarg; - if (filename[0] == '-') { + if (filename[0] == '-' && filename[1] == '\0') { + use_stdinout = 1; force_binary = 1; } break; @@ -627,7 +714,7 @@ int parse_options(int argc, char *argv[]) return 1; } npages = strtoul(optarg, NULL, 0); - if (npages > 0xFF || npages < 0) { + if (npages > STM32_MAX_PAGES || npages < 0) { fprintf(stderr, "ERROR: You need to specify a page count between 0 and 255"); return 1; } @@ -815,10 +902,20 @@ void show_help(char *name) { " -c Resume the connection (don't send initial INIT)\n" " *Baud rate must be kept the same as the first init*\n" " This is useful if the reset fails\n" + " -R Reset device at exit.\n" " -i GPIO_string GPIO sequence to enter/exit bootloader mode\n" " GPIO_string=[entry_seq][:[exit_seq]]\n" - " sequence=[-]n[,sequence]\n" - " -R Reset device at exit.\n" + " sequence=[[-]signal]&|,[sequence]\n" + "\n" + "GPIO sequence:\n" + " The following signals can appear in a sequence:\n" + " Integer number representing GPIO pin\n" + " 'dtr', 'rts' or 'brk' representing serial port signal\n" + " The sequence can use the following delimiters:\n" + " ',' adds 100 ms delay between signals\n" + " '&' adds no delay between signals\n" + " The following modifiers can be prepended to a signal:\n" + " '-' reset signal (low) instead of setting it (high)\n" "\n" "Examples:\n" " Get device information:\n" @@ -839,9 +936,14 @@ void show_help(char *name) { " %s -g 0x0 /dev/ttyS0\n" "\n" " GPIO sequence:\n" - " - entry sequence: GPIO_3=low, GPIO_2=low, GPIO_2=high\n" - " - exit sequence: GPIO_3=high, GPIO_2=low, GPIO_2=high\n" - " %s -R -i -3,-2,2:3,-2,2 /dev/ttyS0\n", + " - entry sequence: GPIO_3=low, GPIO_2=low, 100ms delay, GPIO_2=high\n" + " - exit sequence: GPIO_3=high, GPIO_2=low, 300ms delay, GPIO_2=high\n" + " %s -i '-3&-2,2:3&-2,,,2' /dev/ttyS0\n" + " GPIO sequence adding delay after port opening:\n" + " - entry sequence: delay 500ms\n" + " - exit sequence: rts=high, dtr=low, 300ms delay, GPIO_2=high\n" + " %s -R -i ',,,,,:rts&-dtr,,,2' /dev/ttyS0\n", + name, name, name, name, diff --git a/parsers/Makefile.am b/parsers/Makefile.am new file mode 100644 index 0000000..033d49b --- /dev/null +++ b/parsers/Makefile.am @@ -0,0 +1,11 @@ +noinst_LTLIBRARIES = parsers.la + + +parsers_la_SOURCES = binary.c hex.c + +parsers_la_CXXFLAGS = -Wall -g + + +parsers_la_LDFLAGS = -module -avoid-version + + diff --git a/parsers/binary.c b/parsers/binary.c index f491952..a1a30b4 100644 --- a/parsers/binary.c +++ b/parsers/binary.c @@ -39,7 +39,7 @@ void* binary_init() { parser_err_t binary_open(void *storage, const char *filename, const char write) { binary_t *st = storage; if (write) { - if (filename[0] == '-') + if (filename[0] == '-' && filename[1] == '\0') st->fd = 1; else st->fd = open( @@ -57,7 +57,7 @@ parser_err_t binary_open(void *storage, const char *filename, const char write) ); st->stat.st_size = 0; } else { - if (filename[0] == '-') { + if (filename[0] == '-' && filename[1] == '\0') { st->fd = 0; } else { if (stat(filename, &st->stat) != 0) @@ -92,11 +92,13 @@ unsigned int binary_size(void *storage) { parser_err_t binary_read(void *storage, void *data, unsigned int *len) { binary_t *st = storage; unsigned int left = *len; + unsigned char *d = data; + if (st->write) return PARSER_ERR_WRONLY; ssize_t r; while(left > 0) { - r = read(st->fd, data, left); + r = read(st->fd, d, left); /* If there is no data to read at all, return OK, but with zero read */ if (r == 0 && left == *len) { *len = 0; @@ -104,7 +106,7 @@ parser_err_t binary_read(void *storage, void *data, unsigned int *len) { } if (r <= 0) return PARSER_ERR_SYSTEM; left -= r; - data += r; + d += r; } *len = *len - left; @@ -113,16 +115,18 @@ parser_err_t binary_read(void *storage, void *data, unsigned int *len) { parser_err_t binary_write(void *storage, void *data, unsigned int len) { binary_t *st = storage; + unsigned char *d = data; + if (!st->write) return PARSER_ERR_RDONLY; ssize_t r; while(len > 0) { - r = write(st->fd, data, len); + r = write(st->fd, d, len); if (r < 1) return PARSER_ERR_SYSTEM; st->stat.st_size += r; len -= r; - data += r; + d += r; } return PARSER_ERR_OK; diff --git a/parsers/hex.c b/parsers/hex.c index 1c92ea4..b3eaddf 100644 --- a/parsers/hex.c +++ b/parsers/hex.c @@ -27,6 +27,7 @@ #include <string.h> #include "hex.h" +#include "../compiler.h" #include "../utils.h" typedef struct { @@ -45,9 +46,9 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) { return PARSER_ERR_RDONLY; } else { char mark; - int i, fd; + int fd; uint8_t checksum; - unsigned int c; + unsigned int c, i; uint32_t base = 0; unsigned int last_address = 0x0; @@ -153,6 +154,7 @@ parser_err_t hex_open(void *storage, const char *filename, const char write) { /* address record */ case 4: base = base << 12; + /* fall-through */ case 2: base = base << 4; /* Reset last_address since our base changed */ last_address = 0; @@ -213,7 +215,7 @@ parser_err_t hex_read(void *storage, void *data, unsigned int *len) { return PARSER_ERR_OK; } -parser_err_t hex_write(void *storage, void *data, unsigned int len) { +parser_err_t hex_write(void __unused *storage, void __unused *data, unsigned int __unused len) { return PARSER_ERR_RDONLY; } @@ -62,6 +62,7 @@ struct port_interface { unsigned flags; port_err_t (*open)(struct port_interface *port, struct port_options *ops); port_err_t (*close)(struct port_interface *port); + port_err_t (*flush)(struct port_interface *port); port_err_t (*read)(struct port_interface *port, void *buf, size_t nbyte); port_err_t (*write)(struct port_interface *port, void *buf, size_t nbyte); port_err_t (*gpio)(struct port_interface *port, serial_gpio_t n, int level); diff --git a/serial_posix.c b/serial_posix.c index ec6694f..00625b8 100644 --- a/serial_posix.c +++ b/serial_posix.c @@ -27,10 +27,17 @@ #include <unistd.h> #include <sys/ioctl.h> #include <stdio.h> +#include <sys/file.h> #include "serial.h" #include "port.h" +#ifndef TERMIOS_TIMEOUT_MS +#define TERMIOS_TIMEOUT_MS 500 +#endif + +#define TERMIOS_TIMEOUT ((TERMIOS_TIMEOUT_MS)/100) + struct serial { int fd; struct termios oldtio; @@ -47,6 +54,13 @@ static serial_t *serial_open(const char *device) free(h); return NULL; } + + if(lockf(h->fd,F_TLOCK,0) != 0) + { + fprintf(stderr, "Error: %s is already open\n", device); + free(h); + return NULL; + } fcntl(h->fd, F_SETFL, 0); tcgetattr(h->fd, &h->oldtio); @@ -64,6 +78,7 @@ static void serial_close(serial_t *h) { serial_flush(h); tcsetattr(h->fd, TCSANOW, &h->oldtio); + lockf(h->fd, F_ULOCK, 0); close(h->fd); free(h); } @@ -129,8 +144,8 @@ static port_err_t serial_setup(serial_t *h, const serial_baud_t baud, switch (parity) { case SERIAL_PARITY_NONE: port_parity = 0; break; - case SERIAL_PARITY_EVEN: port_parity = INPCK | PARENB; break; - case SERIAL_PARITY_ODD: port_parity = INPCK | PARENB | PARODD; break; + case SERIAL_PARITY_EVEN: port_parity = PARENB; break; + case SERIAL_PARITY_ODD: port_parity = PARENB | PARODD; break; default: return PORT_ERR_UNKNOWN; @@ -174,9 +189,11 @@ static port_err_t serial_setup(serial_t *h, const serial_baud_t baud, port_stop | CLOCAL | CREAD; + if ( port_parity != 0 ) + h->newtio.c_iflag |= INPCK; h->newtio.c_cc[VMIN] = 0; - h->newtio.c_cc[VTIME] = 5; /* in units of 0.1 s */ + h->newtio.c_cc[VTIME] = TERMIOS_TIMEOUT; /* in units of 0.1 s */ /* set the settings */ serial_flush(h); @@ -344,11 +361,24 @@ static const char *serial_posix_get_cfg_str(struct port_interface *port) return h ? h->setup_str : "INVALID"; } +static port_err_t serial_posix_flush(struct port_interface *port) +{ + serial_t *h; + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_flush(h); + + return PORT_ERR_OK; +} + struct port_interface port_serial = { .name = "serial_posix", .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, .open = serial_posix_open, .close = serial_posix_close, + .flush = serial_posix_flush, .read = serial_posix_read, .write = serial_posix_write, .gpio = serial_posix_gpio, diff --git a/serial_w32.c b/serial_w32.c index 0bcce93..97f7f24 100644 --- a/serial_w32.c +++ b/serial_w32.c @@ -27,6 +27,7 @@ #include <windows.h> #include <ctype.h> +#include "compiler.h" #include "serial.h" #include "port.h" @@ -88,7 +89,7 @@ static serial_t *serial_open(const char *device) return h; } -static void serial_flush(const serial_t *h) +static void serial_flush(const serial_t __unused *h) { /* We shouldn't need to flush in non-overlapping (blocking) mode */ /* tcflush(h->fd, TCIFLUSH); */ @@ -257,8 +258,6 @@ static port_err_t serial_w32_read(struct port_interface *port, void *buf, ReadFile(h->fd, pos, nbyte, &r, NULL); if (r == 0) return PORT_ERR_TIMEDOUT; - if (r < 0) - return PORT_ERR_UNKNOWN; nbyte -= r; pos += r; @@ -337,11 +336,24 @@ static const char *serial_w32_get_cfg_str(struct port_interface *port) return h ? h->setup_str : "INVALID"; } +static port_err_t serial_w32_flush(struct port_interface *port) +{ + serial_t *h; + h = (serial_t *)port->private; + if (h == NULL) + return PORT_ERR_UNKNOWN; + + serial_flush(h); + + return PORT_ERR_OK; +} + struct port_interface port_serial = { .name = "serial_w32", .flags = PORT_BYTE | PORT_GVR_ETX | PORT_CMD_INIT | PORT_RETRY, .open = serial_w32_open, .close = serial_w32_close, + .flush = serial_w32_flush, .read = serial_w32_read, .write = serial_w32_write, .gpio = serial_w32_gpio, @@ -117,6 +117,59 @@ static const uint8_t stm_obl_launch_code[] = { static const uint32_t stm_obl_launch_code_length = sizeof(stm_obl_launch_code); +/* RM0394, Empty check + * On STM32L452 (and possibly all STM32L45xxx/46xxx) internal empty check flag is + * implemented to allow easy programming of the virgin devices by the boot loader. This flag is + * used when BOOT0 pin is defining Main Flash memory as the target boot space. When the + * flag is set, the device is considered as empty and System memory (boot loader) is selected + * instead of the Main Flash as a boot space to allow user to program the Flash memory. + * This flag is updated only during Option bytes loading: it is set when the content of the + * address 0x08000 0000 is read as 0xFFFF FFFF, otherwise it is cleared. It means a power + * on or setting of OBL_LAUNCH bit in FLASH_CR register or a toggle of PEMPTY bit in FLASH_SR + * register is needed to clear this flag after after programming of a virgin device to execute + * user code after System reset. + * In STM32L45xxx/46xxx the register FLASH_CR could be locked and a special SW sequence is + * required for unlocking it. If a previous unsuccessful unlock has happened, a reset is + * required before the unlock. Due to such complications, toggling the PEMPTY bit in FLASH_SR + * seams the most reasonable choice. + * The code below check first word in flash and flag PEMPTY. If they do not match, then it + * toggles PEMPTY. At last, it resets. + */ + +static const uint8_t stm_pempty_launch_code[] = { + 0x08, 0x48, // ldr r0, [pc, #32] ; (<BASE_FLASH>) + 0x00, 0x68, // ldr r0, [r0, #0] + 0x01, 0x30, // adds r0, #1 + 0x41, 0x1e, // subs r1, r0, #1 + 0x88, 0x41, // sbcs r0, r1 + + 0x07, 0x49, // ldr r1, [pc, #28] ; (<FLASH_SR>) + 0x07, 0x4a, // ldr r2, [pc, #28] ; (<PEMPTY_MASK>) + 0x0b, 0x68, // ldr r3, [r1, #0] + 0x13, 0x40, // ands r3, r2 + 0x5c, 0x1e, // subs r4, r3, #1 + 0xa3, 0x41, // sbcs r3, r4 + + 0x98, 0x42, // cmp r0, r3 + 0x00, 0xd1, // bne.n skip1 + + 0x0a, 0x60, // str r2, [r1, #0] + + 0x04, 0x48, // skip1: ldr r0, [pc, #16] ; (<AIRCR_OFFSET>) + 0x05, 0x49, // ldr r1, [pc, #16] ; (<AIRCR_RESET_VALUE>) + 0x01, 0x60, // str r1, [r0, #0] + + 0xfe, 0xe7, // endless: b.n endless + + 0x00, 0x00, 0x00, 0x08, // .word 0x08000000 <BASE_FLASH> + 0x10, 0x20, 0x02, 0x40, // .word 0x40022010 <FLASH_SR> + 0x00, 0x00, 0x02, 0x00, // .word 0x00020000 <PEMPTY_MASK> + 0x0c, 0xed, 0x00, 0xe0, // .word 0xe000ed0c <AIRCR_OFFSET> = NVIC AIRCR register address + 0x04, 0x00, 0xfa, 0x05 // .word 0x05fa0004 <AIRCR_RESET_VALUE> = VECTKEY | SYSRESETREQ +}; + +static const uint32_t stm_pempty_launch_code_length = sizeof(stm_pempty_launch_code); + extern const stm32_dev_t devices[]; int flash_addr_to_page_ceil(uint32_t addr); @@ -606,7 +659,7 @@ stm32_err_t stm32_write_memory(const stm32_t *stm, uint32_t address, s_err = stm32_get_ack_timeout(stm, STM32_BLKWRITE_TIMEOUT); if (s_err != STM32_ERR_OK) { - if (port->flags & PORT_STRETCH_W + if ((port->flags & PORT_STRETCH_W) && stm->cmd->wm != STM32_CMD_WM_NS) stm32_warn_stretching("write"); return STM32_ERR_UNKNOWN; @@ -633,7 +686,7 @@ stm32_err_t stm32_wunprot_memory(const stm32_t *stm) return STM32_ERR_UNKNOWN; } if (s_err != STM32_ERR_OK) { - if (port->flags & PORT_STRETCH_W + if ((port->flags & PORT_STRETCH_W) && stm->cmd->uw != STM32_CMD_UW_NS) stm32_warn_stretching("WRITE UNPROTECT"); return STM32_ERR_UNKNOWN; @@ -660,7 +713,7 @@ stm32_err_t stm32_wprot_memory(const stm32_t *stm) return STM32_ERR_UNKNOWN; } if (s_err != STM32_ERR_OK) { - if (port->flags & PORT_STRETCH_W + if ((port->flags & PORT_STRETCH_W) && stm->cmd->wp != STM32_CMD_WP_NS) stm32_warn_stretching("WRITE PROTECT"); return STM32_ERR_UNKNOWN; @@ -687,7 +740,7 @@ stm32_err_t stm32_runprot_memory(const stm32_t *stm) return STM32_ERR_UNKNOWN; } if (s_err != STM32_ERR_OK) { - if (port->flags & PORT_STRETCH_W + if ((port->flags & PORT_STRETCH_W) && stm->cmd->ur != STM32_CMD_UR_NS) stm32_warn_stretching("READOUT UNPROTECT"); return STM32_ERR_UNKNOWN; @@ -714,7 +767,7 @@ stm32_err_t stm32_readprot_memory(const stm32_t *stm) return STM32_ERR_UNKNOWN; } if (s_err != STM32_ERR_OK) { - if (port->flags & PORT_STRETCH_W + if ((port->flags & PORT_STRETCH_W) && stm->cmd->rp != STM32_CMD_RP_NS) stm32_warn_stretching("READOUT PROTECT"); return STM32_ERR_UNKNOWN; @@ -755,9 +808,9 @@ static stm32_err_t stm32_mass_erase(const stm32_t *stm) s_err = stm32_get_ack_timeout(stm, STM32_MASSERASE_TIMEOUT); if (s_err != STM32_ERR_OK) { fprintf(stderr, "Mass erase failed. Try specifying the number of pages to be erased.\n"); - if (port->flags & PORT_STRETCH_W - && stm->cmd->er != STM32_CMD_EE_NS) - stm32_warn_stretching("mass erase"); + if ((port->flags & PORT_STRETCH_W) + && stm->cmd->er != STM32_CMD_EE_NS) + stm32_warn_stretching("mass erase"); return STM32_ERR_UNKNOWN; } return STM32_ERR_OK; @@ -842,7 +895,7 @@ static stm32_err_t stm32_pages_erase(const stm32_t *stm, uint32_t spage, uint32_ s_err = stm32_get_ack_timeout(stm, pages * STM32_PAGEERASE_TIMEOUT); if (s_err != STM32_ERR_OK) { fprintf(stderr, "Page-by-page erase failed. Check the maximum pages your device supports.\n"); - if (port->flags & PORT_STRETCH_W + if ((port->flags & PORT_STRETCH_W) && stm->cmd->er != STM32_CMD_EE_NS) stm32_warn_stretching("erase"); return STM32_ERR_UNKNOWN; @@ -972,6 +1025,9 @@ stm32_err_t stm32_reset_device(const stm32_t *stm) if (stm->dev->flags & F_OBLL) { /* set the OBL_LAUNCH bit to reset device (see RM0360, 2.5) */ return stm32_run_raw_code(stm, target_address, stm_obl_launch_code, stm_obl_launch_code_length); + } else if (stm->dev->flags & F_PEMPTY) { + /* clear the PEMPTY bit to reset the device (see RM0394) */ + return stm32_run_raw_code(stm, target_address, stm_pempty_launch_code, stm_pempty_launch_code_length); } else { return stm32_run_raw_code(stm, target_address, stm_reset_code, stm_reset_code_length); } @@ -983,7 +1039,7 @@ stm32_err_t stm32_crc_memory(const stm32_t *stm, uint32_t address, struct port_interface *port = stm->port; uint8_t buf[5]; - if (address & 0x3 || length & 0x3) { + if ((address & 0x3) || (length & 0x3)) { fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); return STM32_ERR_UNKNOWN; } @@ -1076,7 +1132,7 @@ stm32_err_t stm32_crc_wrapper(const stm32_t *stm, uint32_t address, uint8_t buf[256]; uint32_t start, total_len, len, current_crc; - if (address & 0x3 || length & 0x3) { + if ((address & 0x3) || (length & 0x3)) { fprintf(stderr, "Start and end addresses must be 4 byte aligned\n"); return STM32_ERR_UNKNOWN; } @@ -38,8 +38,9 @@ typedef enum { } stm32_err_t; typedef enum { - F_NO_ME = 1 << 0, /* Mass-Erase not supported */ - F_OBLL = 1 << 1, /* OBL_LAUNCH required */ + F_NO_ME = 1 << 0, /* Mass-Erase not supported */ + F_OBLL = 1 << 1, /* OBL_LAUNCH required */ + F_PEMPTY = 1 << 2, /* clear PEMPTY bit required */ } flags_t; typedef struct stm32 stm32_t; diff --git a/stm32flash.1 b/stm32flash.1 index ca7acb7..6870ae0 100644 --- a/stm32flash.1 +++ b/stm32flash.1 @@ -226,7 +226,7 @@ The format of .RS GPIO_string = [entry sequence][:[exit sequence]] .P -sequence = [\-]n[,sequence] +sequence = [[\-]signal]&|,[sequence] .RE .PD .P @@ -239,9 +239,20 @@ The string "brk" forces the UART to send a BREAK sequence on TX line; after BREAK the UART is returned in normal "non\-break" mode. Note: the string "\-brk" has no effect and is ignored. .PD - .P -Note that the exit sequence is only executed if -R is specified. If -R is specified, but no exit sequence, a software-triggered reset will be performed. +The ',' delimiter adds 100 ms of delay between signal toggles, whereas +the '&' delimiter adds no delay. +An empty signal, thus repeated ',' delimiters, can be used to insert larger +delays in multiples of 100 ms. +E.g. "rts,,,,\-dtr" will set RTS, then wait 400 ms, then reset DTR. +"rts&\-dtr" will set RTS and reset DTR without delay. You can use ',' delimiters +alone to simply add a delay between opening port and starting to flash. +.DP +.P +Note that since version 0.6, an exit sequence will always be executed if +specified, regardless of the -R option, to ensure the signals are reset. +If -R is specified, but no exit sequence, a software-triggered reset will +be performed. .PD 0 As example, let's suppose the following connection between host and STM32: @@ -267,7 +278,7 @@ The corresponding string for .I GPIO_string is "\-4,\-3,3". -The complete command line flag is "\-R \-i 4,\-5,\-3,3:\-4,\-3,3". +The complete command line flag is "\-i 4,\-5,\-3,3:\-4,\-3,3". STM32W uses pad PA5 to select boot mode; if during reset PA5 is "low" then STM32W will enter in bootloader mode; if PA5 is "high" it will execute the @@ -277,15 +288,30 @@ As example, supposing GPIO_3 connected to PA5 and GPIO_2 to STM32W's reset. The command: .PD 0 .RS -stm32flash \-R \-i \-3,\-2,2:3,\-2,2 /dev/ttyS0 +stm32flash \-i '\-3&\-2,2:3&\-2,,,2' /dev/ttyS0 .RE provides: .IP \(bu 2 -entry sequence: GPIO_3=low, GPIO_2=low, GPIO_2=high +entry sequence: GPIO_3=low, GPIO_2=low, 100 ms delay, GPIO_2=high .IP \(bu 2 -exit sequence: GPIO_3=high, GPIO_2=low, GPIO_2=high +exit sequence: GPIO_3=high, GPIO_2=low, 300 ms delay, GPIO_2=high .PD + +GPIO sequence to bring delays on start after port opening. +The command: +.PD 0 +.RS +stm32flash \-i ',,,,,:rts&\-dtr,,,2' /dev/ttyS0\n", +.RE +provides: +.IP \(bu 2 +entry sequence: delay 500 ms +.IP \(bu 2 +exit sequence: RTS=high, DTR=low, 300 ms delay, GPIO_2=high +.PD + + .SH EXAMPLES Get device information: .RS @@ -327,7 +353,7 @@ entry sequence: RTS=low, DTR=low, DTR=high exit sequence: RTS=high, DTR=low, DTR=high .P .RS -stm32flash \-R \-i \-rts,\-dtr,dtr:rts,\-dtr,dtr /dev/ttyS0 +stm32flash \-i \-rts,\-dtr,dtr:rts,\-dtr,dtr /dev/ttyS0 .PD .RE @@ -43,3 +43,10 @@ uint32_t le_u32(const uint32_t v) { ((v & 0x000000FF) << 24); return v; } + +void printStatus(FILE *fd, int condition){ + if(condition) + fprintf(fd, "Error!\n"); + else + fprintf(fd, "OK\n"); +} @@ -22,9 +22,12 @@ #define _H_UTILS #include <stdint.h> +#include <stdio.h> char cpu_le(); uint32_t be_u32(const uint32_t v); uint32_t le_u32(const uint32_t v); +void printStatus(FILE *fd, int condition); + #endif |