Added programming scripts.

This commit is contained in:
Gareth McMullin 2011-02-04 20:25:12 +13:00
parent 406617a2a4
commit 69d790fcf6
9 changed files with 1091 additions and 0 deletions

9
scripts/README Normal file
View File

@ -0,0 +1,9 @@
This directory contains some useful scripts for working
on the Black Magic Debug project.
bootprog.py - Production programmer using the STM32 SystemMemory bootloader.
hexprog.py - Write an Intel hex file to a target using the GDB protocol.
stm32_mem.py - Access STM32 Flash memory using USB DFU class interface.
stubs/ - Source code for the microcode strings included in hexprog.py.

188
scripts/bootprog.py Executable file
View File

@ -0,0 +1,188 @@
#!/usr/bin/python
#
# bootprog.py: STM32 SystemMemory Production Programmer -- version 1.1
# Copyright (C) 2009 Black Sphere Technologies
# 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):
self.serial = serial.Serial(port, baud, 8, 'E', 1,
timeout=1)
# Turn on target device in SystemMemory boot mode
self.serial.setDTR(1)
sleep(0.1);
self._sync()
def _sync(self):
# Send sync byte
#print "sending sync byte"
self.serial.write("\x7F")
self._checkack()
def _sendcmd(self, cmd):
if type(cmd) == int:
cmd = chr(cmd)
cmd += chr(ord(cmd) ^ 0xff)
#print "sendcmd:", repr(cmd)
self.serial.write(cmd)
def _send(self, data):
csum = 0
for c in data: csum ^= ord(c)
data = data + chr(csum)
#print "sending:", repr(data)
self.serial.write(data)
def _checkack(self):
ACK = "\x79"
b = self.serial.read(1)
if b != ACK: raise Exception("Invalid ack: %r" % b)
#print "got ack!"
def get(self):
self._sendcmd("\x00")
self._checkack()
num = ord(self.serial.read(1))
data = self.serial.read(num+1)
self._checkack()
return data
def eraseall(self):
# Send erase cmd
self._sendcmd("\x43")
self._checkack()
# Global erase
self._sendcmd("\xff")
self._checkack()
def read(self, addr, len):
# Send read cmd
self._sendcmd("\x11")
self._checkack()
# Send address
self._send(struct.pack(">L", addr))
self._checkack()
# Send length
self._sendcmd(chr(len-1))
self._checkack()
return self.serial.read(len)
def write(self, addr, data):
# Send write cmd
self._sendcmd("\x31")
self._checkack()
# Send address
self._send(struct.pack(">L", addr))
self._checkack()
# Send data
self._send(chr(len(data)-1) + data)
self._checkack()
def write_protect(self, sectors):
# Send WP cmd
self._sendcmd("\x63")
self._checkack()
# Send sector list
self._send(chr(len(sectors)-1) + "".join(chr(i) for i in sectors))
self._checkack()
# Resync after system reset
self._sync()
def write_unprotect(self):
self._sendcmd("\x73")
self._checkack()
self._checkack()
self._sync()
def read_protect(self):
self._sendcmd("\x82")
self._checkack()
self._checkack()
self._sync()
def read_unprotect(self):
self._sendcmd("\x92")
self._checkack()
self._checkack()
self._sync()
if __name__ == "__main__":
from sys import stdout, argv, platform
from getopt import getopt
if platform == "linux2":
print "\x1b\x5b\x48\x1b\x5b\x32\x4a" # clear terminal screen
print "STM32 SystemMemory Production Programmer -- version 1.1"
print "Copyright (C) 2009 Black Sphere Technologies, All rights reserved."
print
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:
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
exit(-1)
prog = open(progfile, "rb").read()
boot = stm32_boot(dev, baud)
cmds = boot.get()
print "Target bootloader version: %d.%d\n" % (ord(cmds[0]) >> 4, ord(cmds[0]) % 0xf)
print "Removing device protection..."
boot.read_unprotect()
boot.write_unprotect()
print "Erasing target device..."
boot.eraseall()
addr = 0x8000000
while prog:
print ("Programming address 0x%08X..0x%08X...\r" % (addr, addr + min(len(prog), 255))),
stdout.flush();
boot.write(addr, prog[:256])
addr += 256
prog = prog[256:]
print
print "Enabling device protection..."
boot.write_protect(range(0,2))
#boot.read_protect()
print "All operations completed."
print

192
scripts/dfu.py Normal file
View File

@ -0,0 +1,192 @@
#!/usr/bin/env python
#
# dfu.py: Access USB DFU class devices
# Copyright (C) 2009 Black Sphere Technologies
# 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 usb
DFU_DETACH_TIMEOUT = 1000
# DFU Requests
DFU_DETACH = 0x00
DFU_DNLOAD = 0x01
DFU_UPLOAD = 0x02
DFU_GETSTATUS = 0x03
DFU_CLRSTATUS = 0x04
DFU_GETSTATE = 0x05
DFU_ABORT = 0x06
# DFU States
STATE_APP_IDLE = 0x00
STATE_APP_DETACH = 0x01
STATE_DFU_IDLE = 0x02
STATE_DFU_DOWNLOAD_SYNC = 0x03
STATE_DFU_DOWNLOAD_BUSY = 0x04
STATE_DFU_DOWNLOAD_IDLE = 0x05
STATE_DFU_MANIFEST_SYNC = 0x06
STATE_DFU_MANIFEST = 0x07
STATE_DFU_MANIFEST_WAIT_RESET = 0x08
STATE_DFU_UPLOAD_IDLE = 0x09
STATE_DFU_ERROR = 0x0a
DFU_STATUS_OK = 0x00
# DFU Status cides
DFU_STATUS_ERROR_TARGET = 0x01
DFU_STATUS_ERROR_FILE = 0x02
DFU_STATUS_ERROR_WRITE = 0x03
DFU_STATUS_ERROR_ERASE = 0x04
DFU_STATUS_ERROR_CHECK_ERASED = 0x05
DFU_STATUS_ERROR_PROG = 0x06
DFU_STATUS_ERROR_VERIFY = 0x07
DFU_STATUS_ERROR_ADDRESS = 0x08
DFU_STATUS_ERROR_NOTDONE = 0x09
DFU_STATUS_ERROR_FIRMWARE = 0x0a
DFU_STATUS_ERROR_VENDOR = 0x0b
DFU_STATUS_ERROR_USBR = 0x0c
DFU_STATUS_ERROR_POR = 0x0d
DFU_STATUS_ERROR_UNKNOWN = 0x0e
DFU_STATUS_ERROR_STALLEDPKT = 0x0f
class dfu_status(object):
def __init__(self, buf):
self.bStatus = buf[0]
self.bwPollTimeout = buf[1] + (buf[2]<<8) + (buf[3]<<16)
self.bState = buf[4]
self.iString = buf[5]
class dfu_device(object):
def __init__(self, dev, conf, iface):
self.dev = dev
self.conf = conf
self.iface = iface
self.handle = self.dev.open()
try:
self.handle.setConfiguration(conf)
except: pass
self.handle.claimInterface(iface)
self.handle.setAltInterface(iface)
if type(self.iface) is usb.Interface:
self.index = self.iface.interfaceNumber
else: self.index = self.iface
def detach(self, wTimeout=255):
self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS |
usb.RECIP_INTERFACE, DFU_DETACH,
None, value=wTimeout, index=self.index)
def download(self, wBlockNum, data):
self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS |
usb.RECIP_INTERFACE, DFU_DNLOAD,
data, value=wBlockNum, index=self.index)
def upload(self, wBlockNum, length):
return self.handle.controlMsg(usb.ENDPOINT_IN |
usb.TYPE_CLASS | usb.RECIP_INTERFACE, DFU_UPLOAD,
length, value=wBlockNum, index=self.index)
def get_status(self):
buf = self.handle.controlMsg(usb.ENDPOINT_IN |
usb.TYPE_CLASS | usb.RECIP_INTERFACE, DFU_GETSTATUS,
6, index=self.index)
return dfu_status(buf)
def clear_status(self):
self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS |
usb.RECIP_INTERFACE, DFU_CLRSTATUS,
"", index=0)
def get_state(self):
buf = self.handle.controlMsg(usb.ENDPOINT_IN |
usb.TYPE_CLASS | usb.RECIP_INTERFACE, DFU_GETSTATE,
1, index=self.index)
return buf[0]
def abort(self):
self.handle.controlMsg(usb.ENDPOINT_OUT | usb.TYPE_CLASS |
usb.RECIP_INTERFACE, DFU_ABORT,
None, index=self.index)
def make_idle(self):
retries = 3
while retries:
try:
status = self.get_status()
except:
self.clear_status()
continue
retries -= 1
if status.bState == STATE_DFU_IDLE:
return True
if ((status.bState == STATE_DFU_DOWNLOAD_SYNC) or
(status.bState == STATE_DFU_DOWNLOAD_IDLE) or
(status.bState == STATE_DFU_MANIFEST_SYNC) or
(status.bState == STATE_DFU_UPLOAD_IDLE) or
(status.bState == STATE_DFU_DOWNLOAD_BUSY) or
(status.bState == STATE_DFU_MANIFEST)):
self.abort()
continue
if status.bState == STATE_DFU_ERROR:
self.clear_status()
continue
if status.bState == STATE_APP_IDLE:
self.detach(DFU_DETACH_TIMEOUT)
continue
if ((status.bState == STATE_APP_DETACH) or
(status.bState == STATE_DFU_MANIFEST_WAIT_RESET)):
usb.reset(self.handle)
return False
raise Exception
def finddevs():
devs = []
for bus in usb.busses():
for dev in bus.devices:
for conf in dev.configurations:
for ifaces in conf.interfaces:
for iface in ifaces:
if ((iface.interfaceClass == 0xFE) and
(iface.interfaceSubClass == 0x01)):
devs.append((dev, conf, iface))
return devs
if __name__ == "__main__":
devs = finddevs()
if not devs:
print "No devices found!"
exit(-1)
for dfu in devs:
handle = dfu[0].open()
man = handle.getString(dfu[0].iManufacturer, 30)
product = handle.getString(dfu[0].iProduct, 30)
iname = handle.getString(dfu[2].iInterface, 30)
print "Device %s: ID %04x:%04x %s - %s" % (dfu[0].filename,
dfu[0].idVendor, dfu[0].idProduct, man, product)

301
scripts/gdb.py Normal file
View File

@ -0,0 +1,301 @@
#!/usr/bin/python
#
# gdb.py: Python module for low level GDB protocol implementation
# Copyright (C) 2009 Black Sphere Technologies
# 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/>.
# Used to parse XML memory map from target
from xml.dom.minidom import parseString
import struct
import time
def hexify(s):
"""Convert a binary string into hex representation"""
ret = ''
for c in s:
ret += "%02X" % ord(c)
return ret
def unhexify(s):
"""Convert a hex string into binary representation"""
ret = ''
for i in range(0, len(s), 2):
ret += chr(int(s[i:i+2], 16))
return ret
class FakeSocket:
"""Emulate socket functions send and recv on a file object"""
def __init__(self, file):
self.file = file
def send(self, data):
self.file.write(data)
def recv(self, bufsize):
return self.file.read(bufsize)
class Target:
def __init__(self, sock):
if "send" in dir(sock):
self.sock = sock
else:
self.sock = FakeSocket(sock)
def getpacket(self):
"""Return the first correctly received packet from GDB target"""
while True:
while self.sock.recv(1) != '$': pass
csum = 0
packet = ''
while True:
c = self.sock.recv(1)
if c == '#': break
if c == '$':
packet = ''
csum = 0
continue
if c == '}':
c = self.sock.recv(1)
csum += ord(c) + ord('}')
packet += chr(ord(c) ^ 0x20)
continue
packet += c
csum += ord(c)
if (csum & 0xFF) == int(self.sock.recv(2),16): break
self.sock.send('-')
self.sock.send('+')
return packet
def putpacket(self, packet):
"""Send packet to GDB target and wait for acknowledge"""
while True:
self.sock.send('$')
csum = 0
for c in packet:
if (c == '$') or (c == '#') or (c == '}'):
self.sock.send('}')
self.sock.send(chr(ord(c) ^ 0x20))
csum += (ord(c) ^ 0x20) + ord('}')
else:
self.sock.send(c)
csum += ord(c)
self.sock.send('#')
self.sock.send("%02X" % (csum & 0xFF))
if self.sock.recv(1) == '+': break
def monitor(self, cmd):
"""Send gdb "monitor" command to target"""
ret = []
self.putpacket("qRcmd," + hexify(cmd))
while True:
s = self.getpacket()
if s == '': return None
if s == 'OK': return ret
if s.startswith('O'): ret.append(unhexify(s[1:]))
else:
raise Exception('Invalid GDB stub response')
def attach(self, pid):
"""Attach to target process (gdb "attach" command)"""
self.putpacket("vAttach;%08X" % pid)
reply = self.getpacket()
if (len(reply) == 0) or (reply[0] == 'E'):
raise Exception('Failed to attach to remote pid %d' % pid)
def detach(self):
"""Detach from target process (gdb "detach" command)"""
self.putpacket("D")
if self.getpacket() != 'OK':
raise Exception("Failed to detach from remote process")
def reset(self):
"""Reset the target system"""
self.putpacket("r")
def read_mem(self, addr, length):
"""Read length bytes from target at address addr"""
self.putpacket("m%08X,%08X" % (addr, length))
reply = self.getpacket()
if (len(reply) == 0) or (reply[0] == 'E'):
raise Exception('Error reading memory at 0x%08X' % addr)
try:
data = unhexify(reply)
except Excpetion:
raise Exception('Invalid response to memory read packet: %r' % reply)
return data
def write_mem(self, addr, data):
"""Write data to target at address addr"""
self.putpacket("X%08X,%08X:%s" % (addr, len(data), data))
if self.getpacket() != 'OK':
raise Exception('Error writing to memory at 0x%08X' % addr)
def read_regs(self):
"""Read target core registers"""
self.putpacket("g")
reply = self.getpacket()
if (len(reply) == 0) or (reply[0] == 'E'):
raise Exception('Error reading memory at 0x%08X' % addr)
try:
data = unhexify(reply)
except Excpetion:
raise Exception('Invalid response to memory read packet: %r' % reply)
return struct.unpack("16L", data)
def write_regs(self, *regs):
"""Write target core registers"""
data = struct.pack("%dL" % len(regs), *regs)
self.putpacket("G" + hexify(data))
if self.getpacket() != 'OK':
raise Exception('Error writing to target core registers')
def memmap_read(self):
"""Read the XML memory map from target"""
offset = 0
ret = ''
while True:
self.putpacket("qXfer:memory-map:read::%08X,%08X" % (offset, 512))
reply = self.getpacket()
if (reply[0] == 'm') or (reply[0] == 'l'):
offset += len(reply) - 1
ret += reply[1:]
else:
raise Exception("Invalid GDB stub response")
if reply[0] == 'l': return ret
def resume(self):
"""Resume target execution"""
self.putpacket("c")
def interrupt(self):
"""Interrupt target execution"""
self.sock.send("\x03")
def run_stub(self, stub, address, *args):
"""Execute a binary stub at address, passing args in core registers."""
self.reset() # Ensure processor is in sane state
time.sleep(0.1)
self.write_mem(address, stub)
regs = list(self.read_regs())
regs[:len(args)] = args
regs[15] = address
self.write_regs(*regs)
self.resume()
reply = self.getpacket()
while not reply:
reply = self.getpacket()
if not reply.startswith("T05"):
raise Exception("Invalid stop response: %r" % reply)
class FlashMemory:
def __init__(self, target, offset, length, blocksize):
self.target = target
self.offset = offset
self.length = length
self.blocksize = blocksize
self.blocks = list(None for i in range(length / blocksize))
def prog(self, offset, data):
assert ((offset >= self.offset) and
(offset + len(data) <= self.offset + self.length))
while data:
index = (offset - self.offset) / self.blocksize
bloffset = (offset - self.offset) % self.blocksize
bldata = data[:self.blocksize-bloffset]
data = data[len(bldata):]; offset += len(bldata)
if self.blocks[index] is None: # Initialize a clear block
self.blocks[index] = "".join(chr(0xff) for i in range(self.blocksize))
self.blocks[index] = (self.blocks[index][:bloffset] + bldata +
self.blocks[index][bloffset+len(bldata):])
def commit(self, progress_cb=None):
totalblocks = 0
for b in self.blocks:
if b is not None: totalblocks += 1
block = 0
for i in range(len(self.blocks)):
block += 1
if callable(progress_cb):
progress_cb(block*100/totalblocks)
# Erase the block
data = self.blocks[i]
addr = self.offset + self.blocksize * i
if data is None: continue
#print "Erasing flash at 0x%X" % (self.offset + self.blocksize*i)
self.target.putpacket("vFlashErase:%08X,%08X" %
(self.offset + self.blocksize*i, self.blocksize))
if self.target.getpacket() != 'OK':
raise Exception("Failed to erase flash")
while data:
d = data[0:980]
data = data[len(d):]
#print "Writing %d bytes at 0x%X" % (len(d), addr)
self.target.putpacket("vFlashWrite:%08X:%s" % (addr, d))
addr += len(d)
if self.target.getpacket() != 'OK':
raise Exception("Failed to write flash")
self.target.putpacket("vFlashDone")
if self.target.getpacket() != 'OK':
raise Exception("Failed to commit")
self.blocks = list(None for i in range(self.length / self.blocksize))
def flash_probe(self):
self.mem = []
xmldom = parseString(self.memmap_read())
for memrange in xmldom.getElementsByTagName("memory"):
if memrange.getAttribute("type") != "flash": continue
offset = eval(memrange.getAttribute("start"))
length = eval(memrange.getAttribute("length"))
for property in memrange.getElementsByTagName("property"):
if property.getAttribute("name") == "blocksize":
blocksize = eval(property.firstChild.data)
break
mem = Target.FlashMemory(self, offset, length, blocksize)
self.mem.append(mem)
xmldom.unlink()
return self.mem
def flash_write_prepare(self, address, data):
for m in self.mem:
if (address >= m.offset) and (address + len(data) < m.offset + m.length):
m.prog(address, data)
def flash_commit(self, progress_cb=None):
for m in self.mem:
m.commit(progress_cb)

175
scripts/hexprog.py Executable file
View File

@ -0,0 +1,175 @@
#!/usr/bin/python
# hexprog.py: Python application to flash a target with an Intel hex file
# Copyright (C) 2009 Black Sphere Technologies
# 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 gdb
import struct
import time
# Microcode sequence to erase option bytes
stub_opterase = '\nH\x0bIA`\x0bIA`\tI\x81`\tI\x81`\x01iA\xf0 \x01\x01aA\xf0@\x01\x01a\xc4hO\xf0\x01\x064B\xfa\xd1\x00\xbe\x00 \x02@#\x01gE\xab\x89\xef\xcd'
# Microcode sequence to program option bytes
stub_optprog = '\tJ\nKS`\nKS`\x08K\x93`\x08K\x93`\x13iC\xf0\x10\x03\x13a\x01\x80\xd4hO\xf0\x01\x064B\xfa\xd1\x00\xbe\x00 \x02@#\x01gE\xab\x89\xef\xcd'
def flash_write_hex(target, hexfile, progress_cb=None):
target.flash_probe()
f = open(hexfile)
addrhi = 0
for line in f:
if line[0] != ':': raise Exception("Error in hex file")
reclen = int(line[1:3], 16)
addrlo = int(line[3:7], 16)
rectype = int(line[7:9], 16);
if sum(ord(x) for x in gdb.unhexify(line[1:11+reclen*2])) & 0xff != 0:
raise Exception("Checksum error in hex file")
if rectype == 0: # Data record
addr = (addrhi << 16) + addrlo
data = gdb.unhexify(line[9:9+reclen*2])
target.flash_write_prepare(addr, data)
pass
elif rectype == 4: # High address record
addrhi = int(line[9:13], 16)
pass
elif rectype == 5: # Entry record
pass
elif rectype == 1: # End of file record
break
else:
raise Exception("Invalid record in hex file")
try:
target.flash_commit(progress_cb)
except:
print "Flash write failed! Is device protected?\n"
exit(-1)
if __name__ == "__main__":
from serial import Serial, SerialException
from sys import argv, platform, stdout
from getopt import getopt
if platform == "linux2":
print ("\x1b\x5b\x48\x1b\x5b\x32\x4a") # clear terminal screen
print("Black Magic Probe -- Target Production Programming Tool -- version 1.0")
print("Copyright (C) 2009 Black Sphere Technologies, All rights reserved.")
print("")
dev = "COM1" if platform == "win32" else "/dev/ttyACM0"
baud = 115200
scan = "jtag_scan"
targetno = 0
unprot = False; prot = False
try:
opts, args = getopt(argv[1:], "sd:b:t:rR")
for opt in opts:
if opt[0] == "-s": scan = "swdp_scan"
elif opt[0] == "-b": baud = int(opt[1])
elif opt[0] == "-d": dev = opt[1]
elif opt[0] == "-t": targetno = int(opt[1])
elif opt[0] == "-r": unprot = True
elif opt[0] == "-R": prot = True
else: raise Exception()
hexfile = args[0]
except:
print("Usage %s [-s] [-d <dev>] [-b <baudrate>] [-t <n>] <filename>" % argv[0])
print("\t-s : Use SW-DP instead of JTAG-DP")
print("\t-d : Use target on interface <dev> (default: %s)" % dev)
print("\t-b : Set device baudrate (default: %d)" % baud)
print("\t-t : Connect to target #n (default: %d)" % targetno)
print("\t-r : Clear flash read protection before programming")
print("\t-R : Enable flash read protection after programming (requires power-on reset)")
print("")
exit(-1)
try:
target = gdb.Target(Serial(dev, baud, timeout=3))
except SerialException, e:
print("FATAL: Failed to open serial device!\n%s\n" % e[0])
exit(-1)
try:
r = target.monitor("version")
for s in r: print s,
except SerialException, e:
print("FATAL: Serial communication failure!\n%s\n" % e[0])
exit(-1)
except: pass
print "Target device scan:"
targetlist = None
r = target.monitor(scan)
for s in r:
print s,
print
r = target.monitor("targets")
for s in r:
if s.startswith("No. Att Driver"): targetlist = []
try:
if type(targetlist) is list:
targetlist.append(int(s[:2]))
except: pass
#if not targetlist:
# print("FATAL: No usable targets found!\n")
# exit(-1)
if targetlist and (targetno not in targetlist):
print("WARNING: Selected target %d not available, using %d" % (targetno, targetlist[0]))
targetno = targetlist[0]
print("Attaching to target %d." % targetno)
target.attach(targetno)
time.sleep(0.1)
if unprot:
print("Removing device protection.")
# Save option bytes for later
#optbytes = struct.unpack("8H", target.read_mem(0x1FFFF800, 16))
# Remove protection
target.run_stub(stub_opterase, 0x20000000)
target.run_stub(stub_optprog, 0x20000000, 0x1FFFF800, 0x5aa5)
target.reset()
time.sleep(0.1)
for m in target.flash_probe():
print("FLASH memory -- Offset: 0x%X BlockSize:0x%X\n" % (m.offset, m.blocksize))
def progress(percent):
print ("Progress: %d%%\r" % percent),
stdout.flush()
print("Programming target")
flash_write_hex(target, hexfile, progress)
print("Resetting target")
target.reset()
if prot:
print("Enabling device protection.")
target.run_stub(stub_opterase, 0x20000000)
target.run_stub(stub_optprog, 0x20000000, 0x1FFFF800, 0x00ff)
target.reset()
target.detach()
print("\nAll operations complete!\n")

109
scripts/stm32_mem.py Executable file
View File

@ -0,0 +1,109 @@
#!/usr/bin/python
#
# stm32_mem.py: STM32 memory access using USB DFU class
# Copyright (C) 2009 Black Sphere Technologies
# 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/>.
from time import sleep
import struct
from sys import stdout, argv
import usb
import dfu
CMD_GETCOMMANDS = 0x00
CMD_SETADDRESSPOINTER = 0x21
CMD_ERASE = 0x41
def stm32_erase(dev, addr):
erase_cmd = struct.pack("<BL", CMD_ERASE, addr)
dev.download(0, erase_cmd)
while True:
status = dev.get_status()
if status.bState == dfu.STATE_DFU_DOWNLOAD_BUSY:
sleep(status.bwPollTimeout / 1000.0)
if status.bState == dfu.STATE_DFU_DOWNLOAD_IDLE:
break
def stm32_write(dev, data):
dev.download(2, data)
while True:
status = dev.get_status()
if status.bState == dfu.STATE_DFU_DOWNLOAD_BUSY:
sleep(status.bwPollTimeout / 1000.0)
if status.bState == dfu.STATE_DFU_DOWNLOAD_IDLE:
break
def stm32_manifest(dev):
dev.download(0, "")
while True:
try:
status = dev.get_status()
except:
return
sleep(status.bwPollTimeout / 1000.0)
if status.bState == dfu.STATE_DFU_MANIFEST:
break
if __name__ == "__main__":
print
print "USB Device Firmware Upgrade - Host Utility -- version 1.1"
print "Copyright (C) 2009 Black Sphere Technologies"
print "All rights reserved"
print
devs = dfu.finddevs()
if not devs:
print "No devices found!"
exit(-1)
for dev in devs:
dfudev = dfu.dfu_device(*dev)
man = dfudev.handle.getString(dfudev.dev.iManufacturer, 30)
product = dfudev.handle.getString(dfudev.dev.iProduct, 30)
if man == "Black Sphere Technologies": break
if man == "STMicroelectronics": break
print "Device %s: ID %04x:%04x %s - %s" % (dfudev.dev.filename,
dfudev.dev.idVendor, dfudev.dev.idProduct, man, product)
try:
state = dfudev.get_state()
except:
print "Failed to read device state! Assuming APP_IDLE"
state = dfu.STATE_APP_IDLE
if state == dfu.STATE_APP_IDLE:
dfudev.detach()
print "Run again to upgrade firmware."
exit(0)
dfudev.make_idle()
bin = open(argv[1], "rb").read()
addr = 0x8002000
while bin:
print ("Programming memory at 0x%08X\r" % addr),
stdout.flush()
stm32_erase(dfudev, addr)
stm32_write(dfudev, bin[:1024])
bin = bin[1024:]
addr += 1024
stm32_manifest(dfudev)
print "\nAll operations complete!\n"

12
scripts/stubs/Makefile Normal file
View File

@ -0,0 +1,12 @@
CROSS_COMPILE = stm32-
CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
all: stm32_opterase.bin stm32_optprog.bin
%.bin: %.S
$(CC) -nostdlib -Wl,-Ttext,0x20000000 $<
$(OBJCOPY) -O binary a.out $@
clean:
-rm *.bin

View File

@ -0,0 +1,53 @@
@; Assembler sequence to erase option bytes on STM32
@; Takes no parameters, ends with BKPT instruction
.global _start
.equ FLASHBASE, 0x40022000
.equ KEY1, 0x45670123
.equ KEY2, 0xCDEF89AB
.equ FLASH_KEY, 0x04
.equ FLASH_OPTKEY, 0x08
.equ FLASH_CR, 0x10
.equ FLASH_SR, 0x0C
.equ OPTER, 0x20
.equ STRT, 0x40
.equ BSY, 0x01
.syntax unified
_start:
@; Load FLASH controller base address
ldr r0, =FLASHBASE
@; Do unlocking sequence
ldr r1, =KEY1
str r1, [r0, #FLASH_KEY]
ldr r1, =KEY2
str r1, [r0, #FLASH_KEY]
@; Same for option bytes
ldr r1, =KEY1
str r1, [r0, #FLASH_OPTKEY]
ldr r1, =KEY2
str r1, [r0, #FLASH_OPTKEY]
@; Set OPTER bit in FLASH_CR
ldr r1, [r0, #FLASH_CR]
orr r1, r1, #OPTER
str r1, [r0, #FLASH_CR]
@; Set STRT bit in FLASH_CR
orr r1, r1, #STRT
str r1, [r0, #FLASH_CR]
_wait: @; Wait for BSY bit to clear
ldr r4, [r0, #FLASH_SR]
mov r6, #BSY
tst r4, r6
bne _wait
bkpt

View File

@ -0,0 +1,52 @@
@; Assembler sequence to program option bytes on STM32
@; Takes option address in r0 and value in r1.
@; Ends with BKPT instruction
.global _start
.equ FLASHBASE, 0x40022000
.equ KEY1, 0x45670123
.equ KEY2, 0xCDEF89AB
.equ FLASH_KEY, 0x04
.equ FLASH_OPTKEY, 0x08
.equ FLASH_CR, 0x10
.equ FLASH_SR, 0x0C
.equ OPTPG, 0x10
.equ BSY, 0x01
.syntax unified
_start:
@; Load FLASH controller base address
ldr r2, =FLASHBASE
@; Do unlocking sequence
ldr r3, =KEY1
str r3, [r2, #FLASH_KEY]
ldr r3, =KEY2
str r3, [r2, #FLASH_KEY]
@; Same for option bytes
ldr r3, =KEY1
str r3, [r2, #FLASH_OPTKEY]
ldr r3, =KEY2
str r3, [r2, #FLASH_OPTKEY]
@; Set OPTPG bit in FLASH_CR
ldr r3, [r2, #FLASH_CR]
orr r3, r3, #OPTPG
str r3, [r2, #FLASH_CR]
@; Write data at address
strh r1, [r0]
_wait: @; Wait for BSY bit to clear
ldr r4, [r2, #FLASH_SR]
mov r6, #BSY
tst r4, r6
bne _wait
bkpt