2022-01-25 23:53:25 +00:00
|
|
|
#!/usr/bin/env python3
|
2011-02-04 07:25:12 +00:00
|
|
|
#
|
|
|
|
# bootprog.py: STM32 SystemMemory Production Programmer -- version 1.1
|
2011-02-08 23:06:50 +00:00
|
|
|
# Copyright (C) 2011 Black Sphere Technologies
|
2011-02-04 07:25:12 +00:00
|
|
|
# Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
|
|
#
|
|
|
|
# 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
import serial
|
|
|
|
import struct
|
|
|
|
from time import sleep
|
|
|
|
|
|
|
|
class stm32_boot:
|
|
|
|
def __init__(self, port, baud=115200):
|
2021-12-19 20:35:35 +00:00
|
|
|
self.serial = serial.Serial(port, baud, 8, 'E', 1,
|
2011-02-04 07:25:12 +00:00
|
|
|
timeout=1)
|
|
|
|
|
|
|
|
# Turn on target device in SystemMemory boot mode
|
|
|
|
self.serial.setDTR(1)
|
2021-12-19 20:35:35 +00:00
|
|
|
sleep(0.1)
|
2011-02-04 07:25:12 +00:00
|
|
|
|
|
|
|
self._sync()
|
|
|
|
|
|
|
|
def _sync(self):
|
|
|
|
# Send sync byte
|
2022-01-25 23:53:25 +00:00
|
|
|
#print("sending sync byte")
|
2022-04-15 18:39:14 +00:00
|
|
|
self.serial.write(b"\x7f")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
|
|
|
|
def _sendcmd(self, cmd):
|
2022-04-15 18:39:14 +00:00
|
|
|
cmd = ord(cmd)
|
2022-01-25 23:53:25 +00:00
|
|
|
cmd = bytes((cmd, cmd ^ 0xff))
|
|
|
|
#print("sendcmd:", repr(cmd))
|
2011-02-04 07:25:12 +00:00
|
|
|
self.serial.write(cmd)
|
|
|
|
|
|
|
|
def _send(self, data):
|
|
|
|
csum = 0
|
2022-01-25 23:53:25 +00:00
|
|
|
for c in data: csum ^= c
|
|
|
|
data = data + bytes((csum,))
|
|
|
|
#print("sending:", repr(data))
|
2011-02-04 07:25:12 +00:00
|
|
|
self.serial.write(data)
|
|
|
|
|
|
|
|
def _checkack(self):
|
2022-04-15 18:39:14 +00:00
|
|
|
ACK = b"\x79"
|
2011-02-04 07:25:12 +00:00
|
|
|
b = self.serial.read(1)
|
|
|
|
if b != ACK: raise Exception("Invalid ack: %r" % b)
|
2022-01-25 23:53:25 +00:00
|
|
|
#print("got ack!")
|
2011-02-04 07:25:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get(self):
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(b"\x00")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
2022-01-25 23:53:25 +00:00
|
|
|
num = self.serial.read(1)[0]
|
2011-02-04 07:25:12 +00:00
|
|
|
data = self.serial.read(num+1)
|
|
|
|
self._checkack()
|
|
|
|
return data
|
|
|
|
|
|
|
|
def eraseall(self):
|
|
|
|
# Send erase cmd
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(b"\x43")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
# Global erase
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(b"\xff")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
|
|
|
|
def read(self, addr, len):
|
|
|
|
# Send read cmd
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(b"\x11")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
# Send address
|
|
|
|
self._send(struct.pack(">L", addr))
|
|
|
|
self._checkack()
|
|
|
|
# Send length
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(bytes((len-1,)))
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
return self.serial.read(len)
|
|
|
|
|
|
|
|
def write(self, addr, data):
|
|
|
|
# Send write cmd
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(b"\x31")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
# Send address
|
|
|
|
self._send(struct.pack(">L", addr))
|
|
|
|
self._checkack()
|
|
|
|
# Send data
|
2022-01-25 23:53:25 +00:00
|
|
|
self._send(bytes((len(data)-1,)) + data)
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
|
|
|
|
def write_protect(self, sectors):
|
|
|
|
# Send WP cmd
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(b"\x63")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
# Send sector list
|
2022-01-25 23:53:25 +00:00
|
|
|
self._send(bytes((len(sectors)-1,)) + bytes(sectors))
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
# Resync after system reset
|
|
|
|
self._sync()
|
|
|
|
|
|
|
|
def write_unprotect(self):
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(b"\x73")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
self._checkack()
|
|
|
|
self._sync()
|
|
|
|
|
|
|
|
def read_protect(self):
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(b"\x82")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
self._checkack()
|
|
|
|
self._sync()
|
|
|
|
|
|
|
|
def read_unprotect(self):
|
2022-04-15 18:39:14 +00:00
|
|
|
self._sendcmd(b"\x92")
|
2011-02-04 07:25:12 +00:00
|
|
|
self._checkack()
|
|
|
|
self._checkack()
|
|
|
|
self._sync()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
from sys import stdout, argv, platform
|
|
|
|
from getopt import getopt
|
|
|
|
|
2022-01-25 23:53:25 +00:00
|
|
|
if platform == "linux":
|
2021-12-19 20:35:35 +00:00
|
|
|
print("\x1b\x5b\x48\x1b\x5b\x32\x4a") # clear terminal screen
|
|
|
|
print("STM32 SystemMemory Production Programmer -- version 1.1")
|
|
|
|
print("Copyright (C) 2011 Black Sphere Technologies")
|
|
|
|
print("License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>")
|
|
|
|
print()
|
2011-02-04 07:25:12 +00:00
|
|
|
|
|
|
|
dev = "COM1" if platform == "win32" else "/dev/ttyUSB0"
|
|
|
|
baud = 115200
|
|
|
|
addr = 0x8000000
|
|
|
|
try:
|
|
|
|
opts, args = getopt(argv[1:], "b:d:a:")
|
|
|
|
for opt in opts:
|
|
|
|
if opt[0] == "-b": baud = int(opt[1])
|
|
|
|
elif opt[0] == "-d": dev = opt[1]
|
|
|
|
else: raise Exception()
|
|
|
|
|
|
|
|
progfile = args[0]
|
|
|
|
except:
|
2021-12-19 20:35:35 +00:00
|
|
|
print("Usage %s [-d <dev>] [-b <baudrate>] [-a <address>] <filename>" % argv[0])
|
|
|
|
print("\t-d : Use target on interface <dev> (default: %s)" % dev)
|
|
|
|
print("\t-b : Set device baudrate (default: %d)" % baud)
|
|
|
|
print("\t-a : Set programming address (default: 0x%X)" % addr)
|
|
|
|
print()
|
2011-02-04 07:25:12 +00:00
|
|
|
exit(-1)
|
|
|
|
|
|
|
|
prog = open(progfile, "rb").read()
|
|
|
|
boot = stm32_boot(dev, baud)
|
|
|
|
|
|
|
|
cmds = boot.get()
|
2022-01-25 23:53:25 +00:00
|
|
|
print("Target bootloader version: %d.%d\n" % (cmds[0] >> 4, cmds[0] % 0xf))
|
2011-02-04 07:25:12 +00:00
|
|
|
|
2021-12-19 20:35:35 +00:00
|
|
|
print("Removing device protection...")
|
2011-02-04 07:25:12 +00:00
|
|
|
boot.read_unprotect()
|
|
|
|
boot.write_unprotect()
|
2021-12-19 20:35:35 +00:00
|
|
|
print("Erasing target device...")
|
2011-02-04 07:25:12 +00:00
|
|
|
boot.eraseall()
|
|
|
|
addr = 0x8000000
|
|
|
|
while prog:
|
2021-12-19 20:35:35 +00:00
|
|
|
print("Programming address 0x%08X..0x%08X...\r" % (addr, addr + min(len(prog), 255)), end=' ')
|
|
|
|
stdout.flush()
|
2011-02-04 07:25:12 +00:00
|
|
|
boot.write(addr, prog[:256])
|
|
|
|
addr += 256
|
|
|
|
prog = prog[256:]
|
|
|
|
|
|
|
|
print
|
2021-12-19 20:35:35 +00:00
|
|
|
print("Enabling device protection...")
|
2011-02-04 07:25:12 +00:00
|
|
|
boot.write_protect(range(0,2))
|
|
|
|
#boot.read_protect()
|
|
|
|
|
2021-12-19 20:35:35 +00:00
|
|
|
print("All operations completed.")
|
2011-02-04 07:25:12 +00:00
|
|
|
print
|
|
|
|
|
|
|
|
|