Added programming scripts.
This commit is contained in:
parent
406617a2a4
commit
69d790fcf6
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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")
|
||||
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue