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
This commit is contained in:
あく 2021-07-12 05:13:01 +03:00 committed by GitHub
parent c3fda0c8c3
commit 5ae3d60101
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 405 additions and 188 deletions

View File

@ -18,6 +18,14 @@ You should have received a copy of the GNU General Public License
along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>. along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
""" """
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.svd_gdb import LoadSVD
from cmdebug.dwt_gdb import DWT from cmdebug.dwt_gdb import DWT

0
debug/PyCortexMDebug/cmdebug/__init__.py Normal file → Executable file
View File

20
debug/PyCortexMDebug/cmdebug/dwt_gdb.py Normal file → Executable file
View File

@ -38,12 +38,14 @@ class DWT(gdb.Command):
def __init__(self): def __init__(self):
gdb.Command.__init__(self, "dwt", gdb.COMMAND_DATA) 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""" """Read from memory (using print) and return an integer"""
value = gdb.selected_inferior().read_memory(address, bits / 8) value = gdb.selected_inferior().read_memory(address, bits / 8)
return struct.unpack_from("<i", value)[0] return struct.unpack_from("<i", value)[0]
def write(self, address, value, bits=32): @staticmethod
def write(address, value, bits=32):
"""Set a value in memory""" """Set a value in memory"""
gdb.selected_inferior().write_memory(address, bytes(value), bits / 8) gdb.selected_inferior().write_memory(address, bytes(value), bits / 8)
@ -53,7 +55,7 @@ class DWT(gdb.Command):
self.write(DWT_CTRL, 0) self.write(DWT_CTRL, 0)
self.is_init = True self.is_init = True
s = map(lambda x: x.lower(), str(args).split(" ")) s = list(map(lambda x: x.lower(), str(args).split(" ")))
# Check for empty command # Check for empty command
if s[0] in ["", "help"]: if s[0] in ["", "help"]:
self.print_help() self.print_help()
@ -100,7 +102,8 @@ class DWT(gdb.Command):
gdb.write(args) gdb.write(args)
self.print_help() self.print_help()
def complete(self, text, word): @staticmethod
def complete(text, word):
text = str(text).lower() text = str(text).lower()
s = text.split(" ") s = text.split(" ")
@ -135,7 +138,8 @@ class DWT(gdb.Command):
def cpicnt_reset(self, value=0): def cpicnt_reset(self, value=0):
self.write(DWT_CPICNT, value & 0xFF) self.write(DWT_CPICNT, value & 0xFF)
def print_help(self): @staticmethod
def print_help():
gdb.write("Usage:\n") gdb.write("Usage:\n")
gdb.write("=========\n") gdb.write("=========\n")
gdb.write("dwt:\n") gdb.write("dwt:\n")
@ -149,4 +153,8 @@ class DWT(gdb.Command):
gdb.write("dwt cyccnt\n") gdb.write("dwt cyccnt\n")
gdb.write("\tDisplay the cycle count\n") gdb.write("\tDisplay the cycle count\n")
gdb.write("\td(default):decimal, x: hex, o: octal, b: binary\n") gdb.write("\td(default):decimal, x: hex, o: octal, b: binary\n")
return () return
# Registers our class to GDB when sourced:
DWT()

View File

@ -1,3 +1,4 @@
#!/usr/bin/env python
""" """
This file is part of PyCortexMDebug This file is part of PyCortexMDebug
@ -15,13 +16,92 @@ You should have received a copy of the GNU General Public License
along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>. along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
""" """
import lxml.objectify as objectify import lxml.objectify as objectify
import sys import sys
from copy import deepcopy
from collections import OrderedDict from collections import OrderedDict
import os import os
import pickle
import traceback 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): class SVDNonFatalError(Exception):
@ -40,29 +120,52 @@ class SVDNonFatalError(Exception):
class SVDFile: class SVDFile:
"""
A parsed SVD file
"""
def __init__(self, fname): def __init__(self, fname):
"""
Args:
fname: Filename for the SVD file
"""
f = objectify.parse(os.path.expanduser(fname)) f = objectify.parse(os.path.expanduser(fname))
root = f.getroot() root = f.getroot()
periph = root.peripherals.getchildren() periph = root.peripherals.getchildren()
self.peripherals = OrderedDict() self.peripherals = SmartDict()
self.base_address = 0
# XML elements # XML elements
for p in periph: for p in periph:
try: 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: except SVDNonFatalError as e:
print(e) print(e)
def add_register(parent, node): 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"): if hasattr(node, "dim"):
dim = int(str(node.dim), 0) dim = int(str(node.dim), 0)
# dimension is not used, number of split indexes should be same # dimension is not used, number of split indexes should be same
incr = int(str(node.dimIncrement), 0) incr = int(str(node.dimIncrement), 0)
default_dim_index = ",".join((str(i) for i in range(dim))) default_dim_index = ",".join((str(i) for i in range(dim)))
dim_index = str(getattr(node, "dimIndex", default_dim_index)) dim_index = str(getattr(node, "dimIndex", default_dim_index))
indexes = dim_index.split(",") indices = dim_index.split(",")
offset = 0 offset = 0
for i in indexes: for i in indices:
name = str(node.name) % i name = str(node.name) % i
reg = SVDPeripheralRegister(node, parent) reg = SVDPeripheralRegister(node, parent)
reg.name = name reg.name = name
@ -71,21 +174,30 @@ def add_register(parent, node):
offset += incr offset += incr
else: else:
try: try:
parent.registers[str(node.name)] = SVDPeripheralRegister(node, parent) reg = SVDPeripheralRegister(node, parent)
except: name = str(node.name)
pass 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): def add_cluster(parent, node):
"""
Add a register cluster to a peripheral
"""
if hasattr(node, "dim"): if hasattr(node, "dim"):
dim = int(str(node.dim), 0) 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) incr = int(str(node.dimIncrement), 0)
default_dim_index = ",".join((str(i) for i in range(dim))) default_dim_index = ",".join((str(i) for i in range(dim)))
dim_index = str(getattr(node, "dimIndex", default_dim_index)) dim_index = str(getattr(node, "dimIndex", default_dim_index))
indexes = dim_index.split(",") indices = dim_index.split(",")
offset = 0 offset = 0
for i in indexes: for i in indices:
name = str(node.name) % i name = str(node.name) % i
cluster = SVDRegisterCluster(node, parent) cluster = SVDRegisterCluster(node, parent)
cluster.name = name cluster.name = name
@ -101,37 +213,58 @@ def add_cluster(parent, node):
class SVDRegisterCluster: class SVDRegisterCluster:
"""
Register cluster
"""
def __init__(self, svd_elem, parent): 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.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 # This doesn't inherit registers from anything
children = svd_elem.getchildren() children = svd_elem.getchildren()
self.description = str(svd_elem.description) self.description = str(svd_elem.description)
self.name = str(svd_elem.name) self.name = str(svd_elem.name)
self.registers = OrderedDict() self.registers = SmartDict()
self.clusters = OrderedDict() self.clusters = SmartDict()
for r in children: for r in children:
if r.tag == "register": if r.tag == "register":
add_register(self, r) add_register(self, r)
def refactor_parent(self, parent): def refactor_parent(self, parent):
self.parent = parent self.parent_base_address = parent.base_address
self.base_address = parent.base_address + self.address_offset self.parent_name = parent.name
try: self.base_address = self.parent_base_address + self.address_offset
values = self.registers.itervalues() values = self.registers.values()
except AttributeError:
values = self.registers.values()
for r in values: for r in values:
r.refactor_parent(self) r.refactor_parent(self)
def __unicode__(self): def __str__(self):
return str(self.name) return str(self.name)
class SVDPeripheral: class SVDPeripheral:
"""
This is a peripheral as defined in the SVD file
"""
def __init__(self, svd_elem, parent): 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"): if not hasattr(svd_elem, "baseAddress"):
raise SVDNonFatalError("Periph without base address") raise SVDNonFatalError("Periph without base address")
self.base_address = int(str(svd_elem.baseAddress), 0) self.base_address = int(str(svd_elem.baseAddress), 0)
@ -139,78 +272,83 @@ class SVDPeripheral:
derived_from = svd_elem.attrib["derivedFrom"] derived_from = svd_elem.attrib["derivedFrom"]
try: try:
self.name = str(svd_elem.name) self.name = str(svd_elem.name)
except: except AttributeError:
self.name = parent.peripherals[derived_from].name self.name = parent.peripherals[derived_from].name
try: try:
self.description = str(svd_elem.description) self.description = str(svd_elem.description)
except: except AttributeError:
self.description = parent.peripherals[derived_from].description 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) self.refactor_parent(parent)
else: else:
# This doesn't inherit registers from anything # This doesn't inherit registers from anything
registers = svd_elem.registers.getchildren()
self.description = str(svd_elem.description) self.description = str(svd_elem.description)
self.name = str(svd_elem.name) self.name = str(svd_elem.name)
self.registers = OrderedDict() self.registers = SmartDict()
self.clusters = OrderedDict() self.clusters = SmartDict()
for r in registers:
if r.tag == "cluster": if hasattr(svd_elem, "registers"):
add_cluster(self, r) registers = [
else: r
add_register(self, 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): def refactor_parent(self, parent):
self.parent = parent self.parent_base_address = parent.base_address
try: values = self.registers.values()
values = self.registers.itervalues()
except AttributeError:
values = self.registers.values()
for r in values: for r in values:
r.refactor_parent(self) 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) return str(self.name)
class SVDPeripheralRegister: class SVDPeripheralRegister:
"""
A register within a peripheral
"""
def __init__(self, svd_elem, parent): def __init__(self, svd_elem, parent):
self.parent = parent self.parent_base_address = parent.base_address
self.name = str(svd_elem.name) self.name = str(svd_elem.name)
self.description = str(svd_elem.description) self.description = str(svd_elem.description)
self.offset = int(str(svd_elem.addressOffset), 0) self.offset = int(str(svd_elem.addressOffset), 0)
try: if hasattr(svd_elem, "access"):
self.access = str(svd_elem.access) self.access = str(svd_elem.access)
except: else:
self.access = "read-write" self.access = "read-write"
try: if hasattr(svd_elem, "size"):
self.size = int(str(svd_elem.size), 0) self.size = int(str(svd_elem.size), 0)
except: else:
self.size = 0x20 self.size = 0x20
self.fields = OrderedDict() self.fields = SmartDict()
if hasattr(svd_elem, "fields"): 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: for f in fields:
self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self) self.fields[str(f.name)] = SVDPeripheralRegisterField(f, self)
def refactor_parent(self, parent): def refactor_parent(self, parent):
self.parent = parent self.parent_base_address = parent.base_address
try:
fields = self.fields.itervalues()
except AttributeError:
fields = self.fields.values()
for f in fields:
f.refactor_parent(self)
def address(self): def address(self):
return self.parent.base_address + self.offset return self.parent_base_address + self.offset
def readable(self): def readable(self):
return self.access in ["read-only", "read-write", "read-writeOnce"] return self.access in ["read-only", "read-write", "read-writeOnce"]
@ -223,44 +361,58 @@ class SVDPeripheralRegister:
"read-writeOnce", "read-writeOnce",
] ]
def __unicode__(self): def __str__(self):
return str(self.name) return str(self.name)
class SVDPeripheralRegisterField: class SVDPeripheralRegisterField:
"""
Field within a register
"""
def __init__(self, svd_elem, parent): def __init__(self, svd_elem, parent):
self.parent = parent
self.name = str(svd_elem.name) self.name = str(svd_elem.name)
self.description = str(getattr(svd_elem, "description", "")) 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.offset = int(str(svd_elem.bitOffset))
self.width = int(str(svd_elem.bitWidth)) self.width = int(str(svd_elem.bitWidth))
except: elif hasattr(svd_elem, "bitRange"):
try: bitrange = list(map(int, str(svd_elem.bitRange).strip()[1:-1].split(":")))
bitrange = list( self.offset = bitrange[1]
map(int, str(svd_elem.bitRange).strip()[1:-1].split(":")) self.width = 1 + bitrange[0] - bitrange[1]
) else:
self.offset = bitrange[1] assert hasattr(svd_elem, "lsb") and hasattr(
self.width = 1 + bitrange[0] - bitrange[1] svd_elem, "msb"
except: ), "Range not found for field {} in register {}".format(self.name, parent)
lsb = int(str(svd_elem.lsb)) lsb = int(str(svd_elem.lsb))
msb = int(str(svd_elem.msb)) msb = int(str(svd_elem.msb))
self.offset = lsb self.offset = lsb
self.width = 1 + msb - lsb self.width = 1 + msb - lsb
self.access = str(getattr(svd_elem, "access", parent.access)) self.access = str(getattr(svd_elem, "access", parent.access))
self.enum = {} self.enum = {}
if hasattr(svd_elem, "enumeratedValues"): if hasattr(svd_elem, "enumeratedValues"):
for v in svd_elem.enumeratedValues.getchildren(): values = [
if v.tag == "name": 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 continue
# Some Kinetis parts have values with # instead of 0x... # Some Kinetis parts have values with # instead of 0x...
value = str(v.value).replace("#", "0x") value = str(v.value).replace("#", "0x")
self.enum[int(value, 0)] = (str(v.name), str(v.description)) description = str(v.description) if hasattr(v, "description") else ""
try:
def refactor_parent(self, parent): index = int(value, 0)
self.parent = parent 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): def readable(self):
return self.access in ["read-only", "read-write", "read-writeOnce"] return self.access in ["read-only", "read-write", "read-writeOnce"]
@ -273,11 +425,15 @@ class SVDPeripheralRegisterField:
"read-writeOnce", "read-writeOnce",
] ]
def __unicode__(self): def __str__(self):
return str(self.name) 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:]: for f in sys.argv[1:]:
print("Testing file: {}".format(f)) print("Testing file: {}".format(f))
svd = SVDFile(f) svd = SVDFile(f)
@ -286,3 +442,7 @@ if __name__ == "__main__":
print("Registers in peripheral '{}':".format(key)) print("Registers in peripheral '{}':".format(key))
print(svd.peripherals[key].registers) print(svd.peripherals[key].registers)
print("Done testing file: {}".format(f)) print("Done testing file: {}".format(f))
if __name__ == "__main__":
_main()

220
debug/PyCortexMDebug/cmdebug/svd_gdb.py Normal file → Executable file
View File

@ -16,7 +16,6 @@ You should have received a copy of the GNU General Public License
along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>. along with PyCortexMDebug. If not, see <http://www.gnu.org/licenses/>.
""" """
import binascii
import gdb import gdb
import re import re
import math import math
@ -24,10 +23,7 @@ import sys
import struct import struct
import pkg_resources import pkg_resources
sys.path.append(".") from .svd import SVDFile
from cmdebug.svd import SVDFile
# from svd_test import *
BITS_TO_UNPACK_FORMAT = { BITS_TO_UNPACK_FORMAT = {
8: "B", 8: "B",
@ -83,7 +79,8 @@ class LoadSVD(gdb.Command):
return [fname for fname in filenames if fname.lower().startswith(prefix)] return [fname for fname in filenames if fname.lower().startswith(prefix)]
return gdb.COMPLETE_NONE return gdb.COMPLETE_NONE
def invoke(self, args, from_tty): @staticmethod
def invoke(args, from_tty):
args = gdb.string_to_argv(args) args = gdb.string_to_argv(args)
argc = len(args) argc = len(args)
if argc == 1: if argc == 1:
@ -130,36 +127,39 @@ class SVD(gdb.Command):
except AttributeError: except AttributeError:
regs_iter = registers.values() regs_iter = registers.values()
gdb.write("Registers in %s:\n" % container_name) gdb.write("Registers in %s:\n" % container_name)
regList = [] reg_list = []
for r in regs_iter: for r in regs_iter:
if r.readable(): if r.readable():
data = self.read(r.address(), r.size) try:
data = self.format(data, form, r.size) data = self.read(r.address(), r.size)
if form == "a": data = self.format(data, form, r.size)
data += ( if form == "a":
" <" data += (
+ re.sub( " <"
r"\s+", + re.sub(
" ", r"\s+",
gdb.execute( " ",
"info symbol {}".format(data), True, True gdb.execute(
).strip(), "info symbol {}".format(data), True, True
).strip(),
)
+ ">"
) )
+ ">" except gdb.MemoryError:
) data = "(error reading)"
else: else:
data = "(not readable)" data = "(not readable)"
desc = re.sub(r"\s+", " ", r.description) 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 column1_width = max(len(reg[0]) for reg in reg_list) + 2 # padding
column2Width = max(len(reg[1]) for reg in regList) column2_width = max(len(reg[1]) for reg in reg_list)
for reg in regList: for reg in reg_list:
gdb.write( gdb.write(
"\t{}:{}{}".format( "\t{}:{}{}".format(
reg[0], reg[0],
"".ljust(column1Width - len(reg[0])), "".ljust(column1_width - len(reg[0])),
reg[1].rjust(column2Width), reg[1].rjust(column2_width),
) )
) )
if reg[2] != reg[0]: if reg[2] != reg[0]:
@ -173,7 +173,7 @@ class SVD(gdb.Command):
data = 0 data = 0
else: else:
data = self.read(register.address(), register.size) data = self.read(register.address(), register.size)
fieldList = [] field_list = []
try: try:
fields_iter = fields.itervalues() fields_iter = fields.itervalues()
except AttributeError: except AttributeError:
@ -193,16 +193,16 @@ class SVD(gdb.Command):
val = self.format(val, form, f.width) val = self.format(val, form, f.width)
else: else:
val = "(not readable)" 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 column1_width = max(len(field[0]) for field in field_list) + 2 # padding
column2Width = max(len(field[1]) for field in fieldList) # padding column2_width = max(len(field[1]) for field in field_list) # padding
for field in fieldList: for field in field_list:
gdb.write( gdb.write(
"\t{}:{}{}".format( "\t{}:{}{}".format(
field[0], field[0],
"".ljust(column1Width - len(field[0])), "".ljust(column1_width - len(field[0])),
field[1].rjust(column2Width), field[1].rjust(column2_width),
) )
) )
if field[2] != field[0]: if field[2] != field[0]:
@ -234,6 +234,10 @@ class SVD(gdb.Command):
gdb.write("svd/[format_character] ...\n") gdb.write("svd/[format_character] ...\n")
gdb.write("\tFormat values using that character\n") gdb.write("\tFormat values using that character\n")
gdb.write("\td(default):decimal, x: hex, o: octal, b: binary\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 return
if not len(s[0]): if not len(s[0]):
@ -242,7 +246,7 @@ class SVD(gdb.Command):
peripherals = self.svd_file.peripherals.itervalues() peripherals = self.svd_file.peripherals.itervalues()
except AttributeError: except AttributeError:
peripherals = self.svd_file.peripherals.values() 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: try:
peripherals = self.svd_file.peripherals.itervalues() peripherals = self.svd_file.peripherals.itervalues()
except AttributeError: except AttributeError:
@ -251,11 +255,19 @@ class SVD(gdb.Command):
desc = re.sub(r"\s+", " ", p.description) desc = re.sub(r"\s+", " ", p.description)
gdb.write( gdb.write(
"\t{}:{}{}\n".format( "\t{}:{}{}\n".format(
p.name, "".ljust(columnWidth - len(p.name)), desc p.name, "".ljust(column_width - len(p.name)), desc
) )
) )
return 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 registers = None
if len(s) >= 1: if len(s) >= 1:
peripheral_name = s[0] peripheral_name = s[0]
@ -263,29 +275,31 @@ class SVD(gdb.Command):
gdb.write("Peripheral {} does not exist!\n".format(s[0])) gdb.write("Peripheral {} does not exist!\n".format(s[0]))
return return
warn_if_ambiguous(self.svd_file.peripherals, peripheral_name)
peripheral = self.svd_file.peripherals[peripheral_name] peripheral = self.svd_file.peripherals[peripheral_name]
if len(s) == 1: 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: if len(peripheral.clusters) > 0:
try: try:
clusters_iter = peripheral.clusters.itervalues() clusters_iter = peripheral.clusters.itervalues()
except AttributeError: except AttributeError:
clusters_iter = peripheral.clusters.values() clusters_iter = peripheral.clusters.values()
gdb.write("Clusters in %s:\n" % peripheral_name) gdb.write("Clusters in %s:\n" % peripheral.name)
regList = [] reg_list = []
for r in clusters_iter: for r in clusters_iter:
desc = re.sub(r"\s+", " ", r.description) 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 column1_width = max(len(reg[0]) for reg in reg_list) + 2 # padding
column2Width = max(len(reg[1]) for reg in regList) column2_width = max(len(reg[1]) for reg in reg_list)
for reg in regList: for reg in reg_list:
gdb.write( gdb.write(
"\t{}:{}{}".format( "\t{}:{}{}".format(
reg[0], reg[0],
"".ljust(column1Width - len(reg[0])), "".ljust(column1_width - len(reg[0])),
reg[1].rjust(column2Width), reg[1].rjust(column2_width),
) )
) )
if reg[2] != reg[0]: if reg[2] != reg[0]:
@ -295,19 +309,23 @@ class SVD(gdb.Command):
cluster = None cluster = None
if len(s) == 2: if len(s) == 2:
container = " ".join(s[:2])
if s[1] in peripheral.clusters: if s[1] in peripheral.clusters:
self._print_registers( warn_if_ambiguous(peripheral.clusters, s[1])
container, form, peripheral.clusters[s[1]].registers cluster = peripheral.clusters[s[1]]
) container = peripheral.name + " > " + cluster.name
self._print_registers(container, form, cluster.registers)
elif s[1] in peripheral.registers: elif s[1] in peripheral.registers:
self._print_register_fields( warn_if_ambiguous(peripheral.registers, s[1])
container, form, self.svd_file.peripherals[s[0]].registers[s[1]] register = peripheral.registers[s[1]]
) container = peripheral.name + " > " + register.name
self._print_register_fields(container, form, register)
else: else:
gdb.write( gdb.write(
"Register/cluster {} in peripheral {} does not exist!\n".format( "Register/cluster {} in peripheral {} does not exist!\n".format(
s[1], s[0] s[1], peripheral.name
) )
) )
return return
@ -315,42 +333,55 @@ class SVD(gdb.Command):
if len(s) == 3: if len(s) == 3:
if s[1] not in peripheral.clusters: if s[1] not in peripheral.clusters:
gdb.write( gdb.write(
"Cluster {} in peripheral {} does not exist!\n".format(s[1], s[0]) "Cluster {} in peripheral {} does not exist!\n".format(
) s[1], peripheral.name
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]
) )
) )
else: return
container = " ".join(s[:3]) warn_if_ambiguous(peripheral.clusters, s[1])
cluster = peripheral.clusters[s[1]]
self._print_register_fields(container, form, cluster.registers[s[2]]) 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 return
if len(s) == 4: if len(s) == 4:
try: if s[1] not in peripheral.registers:
reg = self.svd_file.peripherals[s[0]].registers[s[1]]
except KeyError:
gdb.write( gdb.write(
"Register {} in peripheral {} does not exist!\n".format(s[1], s[0]) "Register {} in peripheral {} does not exist!\n".format(
) s[1], peripheral.name
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]
) )
) )
return 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(): if not field.writable() or not reg.writable():
gdb.write( gdb.write(
"Field {} in register {} in peripheral {} is read-only!\n".format( "Field {} in register {} in peripheral {} is read-only!\n".format(
s[2], s[1], s[0] field.name, reg.name, peripheral.name
) )
) )
return return
@ -359,9 +390,8 @@ class SVD(gdb.Command):
val = int(s[3], 0) val = int(s[3], 0)
except ValueError: except ValueError:
gdb.write( 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( "{} is not a valid number! You can prefix numbers with 0x for hex, 0b for binary, or any python "
s[3] "int literal\n".format(s[3])
)
) )
return return
@ -378,7 +408,7 @@ class SVD(gdb.Command):
else: else:
data = self.read(reg.address(), reg.size) data = self.read(reg.address(), reg.size)
data &= ~(((1 << field.width) - 1) << field.offset) data &= ~(((1 << field.width) - 1) << field.offset)
data |= (val) << field.offset data |= val << field.offset
self.write(reg.address(), data, reg.size) self.write(reg.address(), data, reg.size)
return return
@ -394,22 +424,26 @@ class SVD(gdb.Command):
if len(s) > 1: if len(s) > 1:
s = s[1:] s = s[1:]
else: else:
return return [] # completion after e.g. "svd/x" but before trailing space
if len(s) == 1: if len(s) == 1:
return filter( return list(self.svd_file.peripherals.prefix_match_iter(s[0]))
lambda x: x.lower().startswith(s[0].lower()),
self.peripheral_list() + ["help"],
)
if len(s) == 2: if len(s) == 2:
reg = s[1].upper() reg = s[1].upper()
if len(reg) and reg[0] == "&": if len(reg) and reg[0] == "&":
reg = reg[1:] 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""" """Read from memory and return an integer"""
value = gdb.selected_inferior().read_memory(address, bits / 8) value = gdb.selected_inferior().read_memory(address, bits / 8)
unpack_format = "I" unpack_format = "I"
@ -418,15 +452,17 @@ class SVD(gdb.Command):
# gdb.write("{:x} {}\n".format(address, binascii.hexlify(value))) # gdb.write("{:x} {}\n".format(address, binascii.hexlify(value)))
return struct.unpack_from("<" + unpack_format, value)[0] 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""" """Write data to memory"""
gdb.selected_inferior().write_memory(address, bytes(data), bits / 8) 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""" """Format a number based on a format character and length"""
# get current gdb radix setting # get current gdb radix setting
radix = int( 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 # override it if asked to
@ -454,7 +490,7 @@ class SVD(gdb.Command):
try: try:
keys = self.svd_file.peripherals.iterkeys() keys = self.svd_file.peripherals.iterkeys()
except AttributeError: except AttributeError:
keys = elf.svd_file.peripherals.keys() keys = self.svd_file.peripherals.keys()
return list(keys) return list(keys)
def register_list(self, peripheral): def register_list(self, peripheral):
@ -470,7 +506,7 @@ class SVD(gdb.Command):
def field_list(self, peripheral, register): def field_list(self, peripheral, register):
try: try:
periph = svd_file.peripherals[peripheral] periph = self.svd_file.peripherals[peripheral]
reg = periph.registers[register] reg = periph.registers[register]
try: try:
regs = reg.fields.iterkeys() regs = reg.fields.iterkeys()

View File

@ -20,7 +20,9 @@ RUN apt-get update && \
unzip \ unzip \
build-essential \ build-essential \
python \ python \
python-dev \
python-pip \ python-pip \
python-setuptools \
python3 \ python3 \
imagemagick \ imagemagick \
srecord \ srecord \
@ -29,6 +31,9 @@ RUN apt-get update && \
dfu-util \ dfu-util \
&& apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN pip install lxml
SHELL ["/bin/bash", "-eo", "pipefail", "-c"] 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" 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/ && \ cd gcc-arm-none-eabi-10-2020-q4-major/bin/ && \
for file in * ; do ln -s "${PWD}/${file}" "/usr/bin/${file}" ; done && \ for file in * ; do ln -s "${PWD}/${file}" "/usr/bin/${file}" ; done && \
cd / && arm-none-eabi-gcc -v && arm-none-eabi-gdb -v cd / && arm-none-eabi-gcc -v && arm-none-eabi-gdb -v
# install hex2dfu # install hex2dfu
# hadolint ignore=DL3003 # hadolint ignore=DL3003

View File

@ -72,11 +72,11 @@ flash: $(OBJ_DIR)/flash
upload: $(OBJ_DIR)/upload upload: $(OBJ_DIR)/upload
debug: flash debug: flash
arm-none-eabi-gdb \ arm-none-eabi-gdb-py \
-ex 'target extended-remote | openocd -c "gdb_port pipe" $(OPENOCD_OPTS)' \ -ex 'target extended-remote | openocd -c "gdb_port pipe" $(OPENOCD_OPTS)' \
-ex "set confirm off" \ -ex "set confirm off" \
-ex "source ../debug/FreeRTOS/FreeRTOS.py" \ -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 "svd_load $(SVD_FILE)" \
-ex "compare-sections" \ -ex "compare-sections" \
$(OBJ_DIR)/$(PROJECT).elf; \ $(OBJ_DIR)/$(PROJECT).elf; \
@ -86,7 +86,7 @@ openocd:
bm_debug: flash bm_debug: flash
set -m; blackmagic & echo $$! > $(OBJ_DIR)/agent.PID 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 "target extended-remote 127.0.0.1:2000" \
-ex "set confirm off" \ -ex "set confirm off" \
-ex "monitor debug_bmp enable"\ -ex "monitor debug_bmp enable"\