From 69d790fcf6f2a1c62ad1898031b4c8c0571bad05 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Fri, 4 Feb 2011 20:25:12 +1300 Subject: [PATCH] Added programming scripts. --- scripts/README | 9 + scripts/bootprog.py | 188 ++++++++++++++++++++ scripts/dfu.py | 192 +++++++++++++++++++++ scripts/gdb.py | 301 +++++++++++++++++++++++++++++++++ scripts/hexprog.py | 175 +++++++++++++++++++ scripts/stm32_mem.py | 109 ++++++++++++ scripts/stubs/Makefile | 12 ++ scripts/stubs/stm32_opterase.S | 53 ++++++ scripts/stubs/stm32_optprog.S | 52 ++++++ 9 files changed, 1091 insertions(+) create mode 100644 scripts/README create mode 100755 scripts/bootprog.py create mode 100644 scripts/dfu.py create mode 100644 scripts/gdb.py create mode 100755 scripts/hexprog.py create mode 100755 scripts/stm32_mem.py create mode 100644 scripts/stubs/Makefile create mode 100644 scripts/stubs/stm32_opterase.S create mode 100644 scripts/stubs/stm32_optprog.S diff --git a/scripts/README b/scripts/README new file mode 100644 index 00000000..a539475d --- /dev/null +++ b/scripts/README @@ -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. + diff --git a/scripts/bootprog.py b/scripts/bootprog.py new file mode 100755 index 00000000..ea11f8ed --- /dev/null +++ b/scripts/bootprog.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 +# +# 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 . + +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 ] [-b ] [-a
] " % argv[0] + print "\t-d : Use target on interface (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 + + diff --git a/scripts/dfu.py b/scripts/dfu.py new file mode 100644 index 00000000..2e4ad66a --- /dev/null +++ b/scripts/dfu.py @@ -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 +# +# 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 . + + +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) + + + diff --git a/scripts/gdb.py b/scripts/gdb.py new file mode 100644 index 00000000..fc3572c9 --- /dev/null +++ b/scripts/gdb.py @@ -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 +# +# 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 . + + +# 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) + + diff --git a/scripts/hexprog.py b/scripts/hexprog.py new file mode 100755 index 00000000..811c6a26 --- /dev/null +++ b/scripts/hexprog.py @@ -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 +# +# 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 . + +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 ] [-b ] [-t ] " % argv[0]) + print("\t-s : Use SW-DP instead of JTAG-DP") + print("\t-d : Use target on interface (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") + diff --git a/scripts/stm32_mem.py b/scripts/stm32_mem.py new file mode 100755 index 00000000..ff2986e4 --- /dev/null +++ b/scripts/stm32_mem.py @@ -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 +# +# 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 . + +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("