From 5ae3d601016b9509f647ad9e761729174ecf5d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Mon, 12 Jul 2021 05:13:01 +0300 Subject: [PATCH] Debug: update PyCortexMDebug to latest and refactor (#574) * Debug: update PyCortexDebug to latest and refactor. * Debug: format sources. Dockerfile: add missing dependency. Make: switch to gdb-py. * Debug: port PyCortexMDebug to python2 * Docker: add missing debug dependencies * Debug: cleanup local include in svd_gdb.py --- .../{scripts/gdb.py => PyCortexMDebug.py} | 8 + debug/PyCortexMDebug/cmdebug/__init__.py | 0 debug/PyCortexMDebug/cmdebug/dwt_gdb.py | 20 +- debug/PyCortexMDebug/cmdebug/svd.py | 332 +++++++++++++----- debug/PyCortexMDebug/cmdebug/svd_gdb.py | 220 +++++++----- docker/Dockerfile | 7 +- make/rules.mk | 6 +- 7 files changed, 405 insertions(+), 188 deletions(-) rename debug/PyCortexMDebug/{scripts/gdb.py => PyCortexMDebug.py} (83%) mode change 100644 => 100755 debug/PyCortexMDebug/cmdebug/__init__.py mode change 100644 => 100755 debug/PyCortexMDebug/cmdebug/dwt_gdb.py mode change 100644 => 100755 debug/PyCortexMDebug/cmdebug/svd_gdb.py diff --git a/debug/PyCortexMDebug/scripts/gdb.py b/debug/PyCortexMDebug/PyCortexMDebug.py similarity index 83% rename from debug/PyCortexMDebug/scripts/gdb.py rename to debug/PyCortexMDebug/PyCortexMDebug.py index ca38822a0..6533535c3 100644 --- a/debug/PyCortexMDebug/scripts/gdb.py +++ b/debug/PyCortexMDebug/PyCortexMDebug.py @@ -18,6 +18,14 @@ You should have received a copy of the GNU General Public License along with PyCortexMDebug. If not, see . """ +from os import path +import sys + +directory, file = path.split(__file__) +directory = path.expanduser(directory) +directory = path.abspath(directory) + +sys.path.append(directory) from cmdebug.svd_gdb import LoadSVD from cmdebug.dwt_gdb import DWT diff --git a/debug/PyCortexMDebug/cmdebug/__init__.py b/debug/PyCortexMDebug/cmdebug/__init__.py old mode 100644 new mode 100755 diff --git a/debug/PyCortexMDebug/cmdebug/dwt_gdb.py b/debug/PyCortexMDebug/cmdebug/dwt_gdb.py old mode 100644 new mode 100755 index 9afa21e32..dd7ccd207 --- a/debug/PyCortexMDebug/cmdebug/dwt_gdb.py +++ b/debug/PyCortexMDebug/cmdebug/dwt_gdb.py @@ -38,12 +38,14 @@ class DWT(gdb.Command): def __init__(self): gdb.Command.__init__(self, "dwt", gdb.COMMAND_DATA) - def read(self, address, bits=32): + @staticmethod + def read(address, bits=32): """Read from memory (using print) and return an integer""" value = gdb.selected_inferior().read_memory(address, bits / 8) return struct.unpack_from(". """ - import lxml.objectify as objectify import sys -from copy import deepcopy from collections import OrderedDict import os +import pickle import traceback +import re +import warnings + + +class SmartDict: + """ + Dictionary for search by case-insensitive lookup and/or prefix lookup + """ + + def __init__(self): + self.od = OrderedDict() + self.casemap = {} + + def __getitem__(self, key): + if key in self.od: + return self.od[key] + + if key.lower() in self.casemap: + return self.od[self.casemap[key.lower()]] + + return self.od[self.prefix_match(key)] + + def is_ambiguous(self, key): + return ( + key not in self.od + and key not in self.casemap + and len(list(self.prefix_match_iter(key))) > 1 + ) + + def prefix_match_iter(self, key): + name, number = re.match(r"^(.*?)([0-9]*)$", key.lower()).groups() + for entry, od_key in self.casemap.items(): + if entry.startswith(name) and entry.endswith(number): + yield od_key + + def prefix_match(self, key): + for od_key in self.prefix_match_iter(key): + return od_key + return None + + def __setitem__(self, key, value): + if key in self.od: + warnings.warn("Duplicate entry %s", key) + elif key.lower() in self.casemap: + warnings.warn( + "Entry %s differs from duplicate %s only in cAsE", + key, + self.casemap[key.lower()], + ) + + self.casemap[key.lower()] = key + self.od[key] = value + + def __delitem__(self, key): + if ( + self.casemap[key.lower()] == key + ): # Check that we did not overwrite this entry + del self.casemap[key.lower()] + del self.od[key] + + def __contains__(self, key): + return key in self.od or key.lower() in self.casemap or self.prefix_match(key) + + def __iter__(self): + return iter(self.od) + + def __len__(self): + return len(self.od) + + def items(self): + return self.od.items() + + def keys(self): + return self.od.keys() + + def values(self): + return self.od.values() + + def __str__(self): + return str(self.od) class SVDNonFatalError(Exception): @@ -40,29 +120,52 @@ class SVDNonFatalError(Exception): class SVDFile: + """ + A parsed SVD file + """ + def __init__(self, fname): + """ + + Args: + fname: Filename for the SVD file + """ f = objectify.parse(os.path.expanduser(fname)) root = f.getroot() periph = root.peripherals.getchildren() - self.peripherals = OrderedDict() + self.peripherals = SmartDict() + self.base_address = 0 + # XML elements for p in periph: try: - self.peripherals[str(p.name)] = SVDPeripheral(p, self) + if p.tag == "peripheral": + self.peripherals[str(p.name)] = SVDPeripheral(p, self) + else: + # This is some other tag + pass except SVDNonFatalError as e: print(e) def add_register(parent, node): + """ + Add a register node to a peripheral + + Args: + parent: Parent SVDPeripheral object + node: XML file node fot of the register + """ + if hasattr(node, "dim"): dim = int(str(node.dim), 0) # dimension is not used, number of split indexes should be same incr = int(str(node.dimIncrement), 0) default_dim_index = ",".join((str(i) for i in range(dim))) dim_index = str(getattr(node, "dimIndex", default_dim_index)) - indexes = dim_index.split(",") + indices = dim_index.split(",") offset = 0 - for i in indexes: + for i in indices: name = str(node.name) % i reg = SVDPeripheralRegister(node, parent) reg.name = name @@ -71,21 +174,30 @@ def add_register(parent, node): offset += incr else: try: - parent.registers[str(node.name)] = SVDPeripheralRegister(node, parent) - except: - pass + reg = SVDPeripheralRegister(node, parent) + name = str(node.name) + if name not in parent.registers: + parent.registers[name] = reg + else: + if hasattr(node, "alternateGroup"): + print("Register %s has an alternate group", name) + except SVDNonFatalError as e: + print(e) def add_cluster(parent, node): + """ + Add a register cluster to a peripheral + """ if hasattr(node, "dim"): dim = int(str(node.dim), 0) - # dimension is not used, number of split indexes should be same + # dimension is not used, number of split indices should be same incr = int(str(node.dimIncrement), 0) default_dim_index = ",".join((str(i) for i in range(dim))) dim_index = str(getattr(node, "dimIndex", default_dim_index)) - indexes = dim_index.split(",") + indices = dim_index.split(",") offset = 0 - for i in indexes: + for i in indices: name = str(node.name) % i cluster = SVDRegisterCluster(node, parent) cluster.name = name @@ -101,37 +213,58 @@ def add_cluster(parent, node): class SVDRegisterCluster: + """ + Register cluster + """ + def __init__(self, svd_elem, parent): - self.parent = parent + """ + + Args: + svd_elem: XML element for the register cluster + parent: Parent SVDPeripheral object + """ + self.parent_base_address = parent.base_address + self.parent_name = parent.name self.address_offset = int(str(svd_elem.addressOffset), 0) - self.base_address = self.address_offset + parent.base_address + self.base_address = self.address_offset + self.parent_base_address # This doesn't inherit registers from anything children = svd_elem.getchildren() self.description = str(svd_elem.description) self.name = str(svd_elem.name) - self.registers = OrderedDict() - self.clusters = OrderedDict() + self.registers = SmartDict() + self.clusters = SmartDict() for r in children: if r.tag == "register": add_register(self, r) def refactor_parent(self, parent): - self.parent = parent - self.base_address = parent.base_address + self.address_offset - try: - values = self.registers.itervalues() - except AttributeError: - values = self.registers.values() + self.parent_base_address = parent.base_address + self.parent_name = parent.name + self.base_address = self.parent_base_address + self.address_offset + values = self.registers.values() for r in values: r.refactor_parent(self) - def __unicode__(self): + def __str__(self): return str(self.name) class SVDPeripheral: + """ + This is a peripheral as defined in the SVD file + """ + def __init__(self, svd_elem, parent): - self.parent = parent + """ + + Args: + svd_elem: XML element for the peripheral + parent: Parent SVDFile object + """ + self.parent_base_address = parent.base_address + + # Look for a base address, as it is required if not hasattr(svd_elem, "baseAddress"): raise SVDNonFatalError("Periph without base address") self.base_address = int(str(svd_elem.baseAddress), 0) @@ -139,78 +272,83 @@ class SVDPeripheral: derived_from = svd_elem.attrib["derivedFrom"] try: self.name = str(svd_elem.name) - except: + except AttributeError: self.name = parent.peripherals[derived_from].name try: self.description = str(svd_elem.description) - except: + except AttributeError: self.description = parent.peripherals[derived_from].description - self.registers = deepcopy(parent.peripherals[derived_from].registers) - self.clusters = deepcopy(parent.peripherals[derived_from].clusters) + + # pickle is faster than deepcopy by up to 50% on svd files with a + # lot of derivedFrom definitions + def copier(a): + return pickle.loads(pickle.dumps(a)) + + self.registers = copier(parent.peripherals[derived_from].registers) + self.clusters = copier(parent.peripherals[derived_from].clusters) self.refactor_parent(parent) else: # This doesn't inherit registers from anything - registers = svd_elem.registers.getchildren() self.description = str(svd_elem.description) self.name = str(svd_elem.name) - self.registers = OrderedDict() - self.clusters = OrderedDict() - for r in registers: - if r.tag == "cluster": - add_cluster(self, r) - else: - add_register(self, r) + self.registers = SmartDict() + self.clusters = SmartDict() + + if hasattr(svd_elem, "registers"): + registers = [ + r + for r in svd_elem.registers.getchildren() + if r.tag in ["cluster", "register"] + ] + for r in registers: + if r.tag == "cluster": + add_cluster(self, r) + elif r.tag == "register": + add_register(self, r) def refactor_parent(self, parent): - self.parent = parent - try: - values = self.registers.itervalues() - except AttributeError: - values = self.registers.values() + self.parent_base_address = parent.base_address + values = self.registers.values() for r in values: r.refactor_parent(self) - try: - for c in self.clusters.itervalues(): - c.refactor_parent(self) - except AttributeError: - for c in self.clusters.values(): - c.refactor_parent(self) - def __unicode__(self): + for c in self.clusters.values(): + c.refactor_parent(self) + + def __str__(self): return str(self.name) class SVDPeripheralRegister: + """ + A register within a peripheral + """ + def __init__(self, svd_elem, parent): - self.parent = parent + self.parent_base_address = parent.base_address self.name = str(svd_elem.name) self.description = str(svd_elem.description) self.offset = int(str(svd_elem.addressOffset), 0) - try: + if hasattr(svd_elem, "access"): self.access = str(svd_elem.access) - except: + else: self.access = "read-write" - try: + if hasattr(svd_elem, "size"): self.size = int(str(svd_elem.size), 0) - except: + else: self.size = 0x20 - self.fields = OrderedDict() + self.fields = SmartDict() if hasattr(svd_elem, "fields"): - fields = svd_elem.fields.getchildren() + # Filter fields to only consider those of tag "field" + fields = [f for f in svd_elem.fields.getchildren() if f.tag == "field"] for f in fields: self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self) def refactor_parent(self, parent): - self.parent = parent - try: - fields = self.fields.itervalues() - except AttributeError: - fields = self.fields.values() - for f in fields: - f.refactor_parent(self) + self.parent_base_address = parent.base_address def address(self): - return self.parent.base_address + self.offset + return self.parent_base_address + self.offset def readable(self): return self.access in ["read-only", "read-write", "read-writeOnce"] @@ -223,44 +361,58 @@ class SVDPeripheralRegister: "read-writeOnce", ] - def __unicode__(self): + def __str__(self): return str(self.name) class SVDPeripheralRegisterField: + """ + Field within a register + """ + def __init__(self, svd_elem, parent): - self.parent = parent self.name = str(svd_elem.name) self.description = str(getattr(svd_elem, "description", "")) - try: + # Try to extract a bit range (offset and width) from the available fields + if hasattr(svd_elem, "bitOffset") and hasattr(svd_elem, "bitWidth"): self.offset = int(str(svd_elem.bitOffset)) self.width = int(str(svd_elem.bitWidth)) - except: - try: - bitrange = list( - map(int, str(svd_elem.bitRange).strip()[1:-1].split(":")) - ) - self.offset = bitrange[1] - self.width = 1 + bitrange[0] - bitrange[1] - except: - lsb = int(str(svd_elem.lsb)) - msb = int(str(svd_elem.msb)) - self.offset = lsb - self.width = 1 + msb - lsb + elif hasattr(svd_elem, "bitRange"): + bitrange = list(map(int, str(svd_elem.bitRange).strip()[1:-1].split(":"))) + self.offset = bitrange[1] + self.width = 1 + bitrange[0] - bitrange[1] + else: + assert hasattr(svd_elem, "lsb") and hasattr( + svd_elem, "msb" + ), "Range not found for field {} in register {}".format(self.name, parent) + lsb = int(str(svd_elem.lsb)) + msb = int(str(svd_elem.msb)) + self.offset = lsb + self.width = 1 + msb - lsb + self.access = str(getattr(svd_elem, "access", parent.access)) self.enum = {} if hasattr(svd_elem, "enumeratedValues"): - for v in svd_elem.enumeratedValues.getchildren(): - if v.tag == "name": + values = [ + v + for v in svd_elem.enumeratedValues.getchildren() + if v.tag == "enumeratedValue" + ] + for v in values: + # Skip the "name" tag and any entries that don't have a value + if v.tag == "name" or not hasattr(v, "value"): continue # Some Kinetis parts have values with # instead of 0x... value = str(v.value).replace("#", "0x") - self.enum[int(value, 0)] = (str(v.name), str(v.description)) - - def refactor_parent(self, parent): - self.parent = parent + description = str(v.description) if hasattr(v, "description") else "" + try: + index = int(value, 0) + self.enum[int(value, 0)] = (str(v.name), description) + except ValueError: + # If the value couldn't be converted as a single integer, skip it + pass def readable(self): return self.access in ["read-only", "read-write", "read-writeOnce"] @@ -273,11 +425,15 @@ class SVDPeripheralRegisterField: "read-writeOnce", ] - def __unicode__(self): + def __str__(self): return str(self.name) -if __name__ == "__main__": +def _main(): + """ + Basic test to parse a file and do some things + """ + for f in sys.argv[1:]: print("Testing file: {}".format(f)) svd = SVDFile(f) @@ -286,3 +442,7 @@ if __name__ == "__main__": print("Registers in peripheral '{}':".format(key)) print(svd.peripherals[key].registers) print("Done testing file: {}".format(f)) + + +if __name__ == "__main__": + _main() diff --git a/debug/PyCortexMDebug/cmdebug/svd_gdb.py b/debug/PyCortexMDebug/cmdebug/svd_gdb.py old mode 100644 new mode 100755 index 7153f1c9f..3acd014f7 --- a/debug/PyCortexMDebug/cmdebug/svd_gdb.py +++ b/debug/PyCortexMDebug/cmdebug/svd_gdb.py @@ -16,7 +16,6 @@ You should have received a copy of the GNU General Public License along with PyCortexMDebug. If not, see . """ -import binascii import gdb import re import math @@ -24,10 +23,7 @@ import sys import struct import pkg_resources -sys.path.append(".") -from cmdebug.svd import SVDFile - -# from svd_test import * +from .svd import SVDFile BITS_TO_UNPACK_FORMAT = { 8: "B", @@ -83,7 +79,8 @@ class LoadSVD(gdb.Command): return [fname for fname in filenames if fname.lower().startswith(prefix)] return gdb.COMPLETE_NONE - def invoke(self, args, from_tty): + @staticmethod + def invoke(args, from_tty): args = gdb.string_to_argv(args) argc = len(args) if argc == 1: @@ -130,36 +127,39 @@ class SVD(gdb.Command): except AttributeError: regs_iter = registers.values() gdb.write("Registers in %s:\n" % container_name) - regList = [] + reg_list = [] for r in regs_iter: if r.readable(): - data = self.read(r.address(), r.size) - data = self.format(data, form, r.size) - if form == "a": - data += ( - " <" - + re.sub( - r"\s+", - " ", - gdb.execute( - "info symbol {}".format(data), True, True - ).strip(), + try: + data = self.read(r.address(), r.size) + data = self.format(data, form, r.size) + if form == "a": + data += ( + " <" + + re.sub( + r"\s+", + " ", + gdb.execute( + "info symbol {}".format(data), True, True + ).strip(), + ) + + ">" ) - + ">" - ) + except gdb.MemoryError: + data = "(error reading)" else: data = "(not readable)" desc = re.sub(r"\s+", " ", r.description) - regList.append((r.name, data, desc)) + reg_list.append((r.name, data, desc)) - column1Width = max(len(reg[0]) for reg in regList) + 2 # padding - column2Width = max(len(reg[1]) for reg in regList) - for reg in regList: + column1_width = max(len(reg[0]) for reg in reg_list) + 2 # padding + column2_width = max(len(reg[1]) for reg in reg_list) + for reg in reg_list: gdb.write( "\t{}:{}{}".format( reg[0], - "".ljust(column1Width - len(reg[0])), - reg[1].rjust(column2Width), + "".ljust(column1_width - len(reg[0])), + reg[1].rjust(column2_width), ) ) if reg[2] != reg[0]: @@ -173,7 +173,7 @@ class SVD(gdb.Command): data = 0 else: data = self.read(register.address(), register.size) - fieldList = [] + field_list = [] try: fields_iter = fields.itervalues() except AttributeError: @@ -193,16 +193,16 @@ class SVD(gdb.Command): val = self.format(val, form, f.width) else: val = "(not readable)" - fieldList.append((f.name, val, desc)) + field_list.append((f.name, val, desc)) - column1Width = max(len(field[0]) for field in fieldList) + 2 # padding - column2Width = max(len(field[1]) for field in fieldList) # padding - for field in fieldList: + column1_width = max(len(field[0]) for field in field_list) + 2 # padding + column2_width = max(len(field[1]) for field in field_list) # padding + for field in field_list: gdb.write( "\t{}:{}{}".format( field[0], - "".ljust(column1Width - len(field[0])), - field[1].rjust(column2Width), + "".ljust(column1_width - len(field[0])), + field[1].rjust(column2_width), ) ) if field[2] != field[0]: @@ -234,6 +234,10 @@ class SVD(gdb.Command): gdb.write("svd/[format_character] ...\n") gdb.write("\tFormat values using that character\n") gdb.write("\td(default):decimal, x: hex, o: octal, b: binary\n") + gdb.write("\n") + gdb.write( + "Both prefix matching and case-insensitive matching is supported for peripherals, registers, clusters and fields.\n" + ) return if not len(s[0]): @@ -242,7 +246,7 @@ class SVD(gdb.Command): peripherals = self.svd_file.peripherals.itervalues() except AttributeError: peripherals = self.svd_file.peripherals.values() - columnWidth = max(len(p.name) for p in peripherals) + 2 # padding + column_width = max(len(p.name) for p in peripherals) + 2 # padding try: peripherals = self.svd_file.peripherals.itervalues() except AttributeError: @@ -251,11 +255,19 @@ class SVD(gdb.Command): desc = re.sub(r"\s+", " ", p.description) gdb.write( "\t{}:{}{}\n".format( - p.name, "".ljust(columnWidth - len(p.name)), desc + p.name, "".ljust(column_width - len(p.name)), desc ) ) return + def warn_if_ambiguous(smart_dict, key): + if smart_dict.is_ambiguous(key): + gdb.write( + "Warning: {} could prefix match any of: {}\n".format( + key, ", ".join(smart_dict.prefix_match_iter(key)) + ) + ) + registers = None if len(s) >= 1: peripheral_name = s[0] @@ -263,29 +275,31 @@ class SVD(gdb.Command): gdb.write("Peripheral {} does not exist!\n".format(s[0])) return + warn_if_ambiguous(self.svd_file.peripherals, peripheral_name) + peripheral = self.svd_file.peripherals[peripheral_name] if len(s) == 1: - self._print_registers(s[0], form, peripheral.registers) + self._print_registers(peripheral.name, form, peripheral.registers) if len(peripheral.clusters) > 0: try: clusters_iter = peripheral.clusters.itervalues() except AttributeError: clusters_iter = peripheral.clusters.values() - gdb.write("Clusters in %s:\n" % peripheral_name) - regList = [] + gdb.write("Clusters in %s:\n" % peripheral.name) + reg_list = [] for r in clusters_iter: desc = re.sub(r"\s+", " ", r.description) - regList.append((r.name, "", desc)) + reg_list.append((r.name, "", desc)) - column1Width = max(len(reg[0]) for reg in regList) + 2 # padding - column2Width = max(len(reg[1]) for reg in regList) - for reg in regList: + column1_width = max(len(reg[0]) for reg in reg_list) + 2 # padding + column2_width = max(len(reg[1]) for reg in reg_list) + for reg in reg_list: gdb.write( "\t{}:{}{}".format( reg[0], - "".ljust(column1Width - len(reg[0])), - reg[1].rjust(column2Width), + "".ljust(column1_width - len(reg[0])), + reg[1].rjust(column2_width), ) ) if reg[2] != reg[0]: @@ -295,19 +309,23 @@ class SVD(gdb.Command): cluster = None if len(s) == 2: - container = " ".join(s[:2]) if s[1] in peripheral.clusters: - self._print_registers( - container, form, peripheral.clusters[s[1]].registers - ) + warn_if_ambiguous(peripheral.clusters, s[1]) + cluster = peripheral.clusters[s[1]] + container = peripheral.name + " > " + cluster.name + self._print_registers(container, form, cluster.registers) + elif s[1] in peripheral.registers: - self._print_register_fields( - container, form, self.svd_file.peripherals[s[0]].registers[s[1]] - ) + warn_if_ambiguous(peripheral.registers, s[1]) + register = peripheral.registers[s[1]] + container = peripheral.name + " > " + register.name + + self._print_register_fields(container, form, register) + else: gdb.write( "Register/cluster {} in peripheral {} does not exist!\n".format( - s[1], s[0] + s[1], peripheral.name ) ) return @@ -315,42 +333,55 @@ class SVD(gdb.Command): if len(s) == 3: if s[1] not in peripheral.clusters: gdb.write( - "Cluster {} in peripheral {} does not exist!\n".format(s[1], s[0]) - ) - elif s[2] not in peripheral.clusters[s[1]].registers: - gdb.write( - "Register {} in cluster {} in peripheral {} does not exist!\n".format( - s[2], s[1], s[0] + "Cluster {} in peripheral {} does not exist!\n".format( + s[1], peripheral.name ) ) - else: - container = " ".join(s[:3]) - cluster = peripheral.clusters[s[1]] - self._print_register_fields(container, form, cluster.registers[s[2]]) + return + warn_if_ambiguous(peripheral.clusters, s[1]) + + cluster = peripheral.clusters[s[1]] + if s[2] not in cluster.registers: + gdb.write( + "Register {} in cluster {} in peripheral {} does not exist!\n".format( + s[2], cluster.name, peripheral.name + ) + ) + return + warn_if_ambiguous(cluster.registers, s[2]) + + register = cluster.registers[s[2]] + container = " > ".join([peripheral.name, cluster.name, register.name]) + self._print_register_fields(container, form, register) return if len(s) == 4: - try: - reg = self.svd_file.peripherals[s[0]].registers[s[1]] - except KeyError: + if s[1] not in peripheral.registers: gdb.write( - "Register {} in peripheral {} does not exist!\n".format(s[1], s[0]) - ) - return - try: - field = reg.fields[s[2]] - except KeyError: - gdb.write( - "Field {} in register {} in peripheral {} does not exist!\n".format( - s[2], s[1], s[0] + "Register {} in peripheral {} does not exist!\n".format( + s[1], peripheral.name ) ) return + warn_if_ambiguous(peripheral.registers, s[1]) + + reg = peripheral.registers[s[1]] + + if s[2] not in reg.fields: + gdb.write( + "Field {} in register {} in peripheral {} does not exist!\n".format( + s[2], reg.name, peripheral.name + ) + ) + return + warn_if_ambiguous(reg.fields, s[2]) + + field = reg.fields[s[2]] if not field.writable() or not reg.writable(): gdb.write( "Field {} in register {} in peripheral {} is read-only!\n".format( - s[2], s[1], s[0] + field.name, reg.name, peripheral.name ) ) return @@ -359,9 +390,8 @@ class SVD(gdb.Command): val = int(s[3], 0) except ValueError: gdb.write( - "{} is not a valid number! You can prefix numbers with 0x for hex, 0b for binary, or any python int literal\n".format( - s[3] - ) + "{} is not a valid number! You can prefix numbers with 0x for hex, 0b for binary, or any python " + "int literal\n".format(s[3]) ) return @@ -378,7 +408,7 @@ class SVD(gdb.Command): else: data = self.read(reg.address(), reg.size) data &= ~(((1 << field.width) - 1) << field.offset) - data |= (val) << field.offset + data |= val << field.offset self.write(reg.address(), data, reg.size) return @@ -394,22 +424,26 @@ class SVD(gdb.Command): if len(s) > 1: s = s[1:] else: - return + return [] # completion after e.g. "svd/x" but before trailing space if len(s) == 1: - return filter( - lambda x: x.lower().startswith(s[0].lower()), - self.peripheral_list() + ["help"], - ) + return list(self.svd_file.peripherals.prefix_match_iter(s[0])) if len(s) == 2: reg = s[1].upper() if len(reg) and reg[0] == "&": reg = reg[1:] - filt = filter(lambda x: x.startswith(reg), self.register_list(s[0].upper())) - return filt - def read(self, address, bits=32): + if s[0] not in self.svd_file.peripherals: + return [] + + per = self.svd_file.peripherals[s[0]] + return list(per.registers.prefix_match_iter(s[1])) + + return [] + + @staticmethod + def read(address, bits=32): """Read from memory and return an integer""" value = gdb.selected_inferior().read_memory(address, bits / 8) unpack_format = "I" @@ -418,15 +452,17 @@ class SVD(gdb.Command): # gdb.write("{:x} {}\n".format(address, binascii.hexlify(value))) return struct.unpack_from("<" + unpack_format, value)[0] - def write(self, address, data, bits=32): + @staticmethod + def write(address, data, bits=32): """Write data to memory""" gdb.selected_inferior().write_memory(address, bytes(data), bits / 8) - def format(self, value, form, length=32): + @staticmethod + def format(value, form, length=32): """Format a number based on a format character and length""" # get current gdb radix setting radix = int( - re.search("\d+", gdb.execute("show output-radix", True, True)).group(0) + re.search(r"\d+", gdb.execute("show output-radix", True, True)).group(0) ) # override it if asked to @@ -454,7 +490,7 @@ class SVD(gdb.Command): try: keys = self.svd_file.peripherals.iterkeys() except AttributeError: - keys = elf.svd_file.peripherals.keys() + keys = self.svd_file.peripherals.keys() return list(keys) def register_list(self, peripheral): @@ -470,7 +506,7 @@ class SVD(gdb.Command): def field_list(self, peripheral, register): try: - periph = svd_file.peripherals[peripheral] + periph = self.svd_file.peripherals[peripheral] reg = periph.registers[register] try: regs = reg.fields.iterkeys() diff --git a/docker/Dockerfile b/docker/Dockerfile index 9c4f96f95..406d806e2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -20,7 +20,9 @@ RUN apt-get update && \ unzip \ build-essential \ python \ + python-dev \ python-pip \ + python-setuptools \ python3 \ imagemagick \ srecord \ @@ -29,6 +31,9 @@ RUN apt-get update && \ dfu-util \ && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +RUN pip install lxml + + SHELL ["/bin/bash", "-eo", "pipefail", "-c"] RUN wget --progress=dot:giga -O - "https://apt.llvm.org/llvm-snapshot.gpg.key" | apt-key add - && add-apt-repository "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-12 main" @@ -48,7 +53,7 @@ RUN wget --progress=dot:giga "https://developer.arm.com/-/media/Files/downloads/ cd gcc-arm-none-eabi-10-2020-q4-major/bin/ && \ for file in * ; do ln -s "${PWD}/${file}" "/usr/bin/${file}" ; done && \ cd / && arm-none-eabi-gcc -v && arm-none-eabi-gdb -v - + # install hex2dfu # hadolint ignore=DL3003 diff --git a/make/rules.mk b/make/rules.mk index 6edc91529..627f14c6c 100644 --- a/make/rules.mk +++ b/make/rules.mk @@ -72,11 +72,11 @@ flash: $(OBJ_DIR)/flash upload: $(OBJ_DIR)/upload debug: flash - arm-none-eabi-gdb \ + arm-none-eabi-gdb-py \ -ex 'target extended-remote | openocd -c "gdb_port pipe" $(OPENOCD_OPTS)' \ -ex "set confirm off" \ -ex "source ../debug/FreeRTOS/FreeRTOS.py" \ - -ex "source ../debug/PyCortexMDebug/scripts/gdb.py" \ + -ex "source ../debug/PyCortexMDebug/PyCortexMDebug.py" \ -ex "svd_load $(SVD_FILE)" \ -ex "compare-sections" \ $(OBJ_DIR)/$(PROJECT).elf; \ @@ -86,7 +86,7 @@ openocd: bm_debug: flash set -m; blackmagic & echo $$! > $(OBJ_DIR)/agent.PID - arm-none-eabi-gdb \ + arm-none-eabi-gdb-py \ -ex "target extended-remote 127.0.0.1:2000" \ -ex "set confirm off" \ -ex "monitor debug_bmp enable"\