emdbg.debug.px4.svd

  1# Copyright (c) 2023, Auterion AG
  2# SPDX-License-Identifier: BSD-3-Clause
  3
  4from __future__ import annotations
  5import io
  6from pathlib import Path
  7from collections import defaultdict
  8from contextlib import redirect_stdout
  9
 10from . import utils
 11from .device import Device
 12
 13
 14class PeripheralWatcher(Device):
 15    """Visualize the changes of a peripheral register map."""
 16
 17    def __init__(self, gdb, filename: Path = None):
 18        from cmsis_svd.parser import SVDParser
 19        super().__init__(gdb)
 20        if filename is None: filename = self._SVD_FILE
 21        self.device = SVDParser.for_xml_file(filename).get_device()
 22        self._watched = {}
 23
 24    def _find(self, name):
 25        if isinstance(name, str):
 26            rname = None
 27            if "." in name:
 28                name, rname = name.split(".")
 29            peripheral = next((p for p in self.device.peripherals if p.name == name), None)
 30            if peripheral is None:
 31                raise ValueError(f"Unknown peripheral instance '{name}'! "
 32                                 f"Available peripherals are {','.join(p.name for p in self.device.peripherals)}")
 33            if rname:
 34                register = next((r for r in peripheral.registers if r.name == rname), None)
 35                if register is None:
 36                    raise ValueError(f"Unknown register instance '{name}.{rname}'! "
 37                                     f"Available registers are {','.join(r.name for r in peripheral.registers)}")
 38                return [(peripheral, register)]
 39            return [(peripheral, register) for register in peripheral.registers]
 40        return name if isinstance(name, list) else [name]
 41
 42    def address(self, name):
 43        """
 44        :return: A dictionary of register to address mapping.
 45        """
 46        return {(p,r): (p.base_address + r.address_offset,
 47                        p.base_address + r.address_offset + r.size//8)
 48                for p, r in self._find(name)}
 49
 50    def watch(self, name: str) -> str:
 51        """
 52        Add a peripheral to the watch list.
 53
 54        :param name: name of peripheral instance.
 55        :raises ValueError: if instance name is unknown.
 56
 57        :return: difference report compared to reset values as defined in SVD.
 58        """
 59        registers = self._find(name)
 60        self.update(registers)
 61        report = self.report(registers)
 62        self.update(registers)
 63        return report
 64
 65    def unwatch(self, name: str = None):
 66        """
 67        Remove a peripheral from the watch list.
 68
 69        :param name: name of peripheral instance, or all watched peripheral if `None`.
 70        :raises ValueError: if instance name is unknown.
 71        """
 72        if name is not None:
 73            for register in self._find(name):
 74                if register in self._watched:
 75                    self._watched.pop(register)
 76        else:
 77            self._watched = {}
 78
 79    def update(self, name: str = None):
 80        """
 81        Update all cached register values to the current values.
 82        Note: This performs a read from the device of all watched registers.
 83
 84        :param name: name of peripheral instance, or all watched peripheral if `None`.
 85        :raises ValueError: if instance name is unknown.
 86        """
 87        if name is not None:
 88            for peripheral, register in self._find(name):
 89                new = (peripheral, register) not in self._watched
 90                addr = peripheral.base_address + register.address_offset
 91                value = register.reset_value if new else self.read_uint(addr, register.size // 8)
 92                self._watched[(peripheral, register)] = value
 93        else:
 94            for register in self._watched:
 95                self.update(register)
 96
 97    def reset(self, name: str = None):
 98        """
 99        Update all cached registers to their reset values as defined in the
100        CMSIS-SVD file.
101
102        :param name: name of peripheral instance, or all watched peripheral if `None`.
103        :raises ValueError: if instance name is unknown.
104        """
105        if name is not None:
106            for register in self._find(name):
107                self._watched.pop(register)
108                self.update(register)
109        else:
110            registers = list(self._watched)
111            for register in registers:
112                self.reset(register)
113
114    def report(self, name: str = None) -> str:
115        """
116        Compare the cached registers with the on-device registers and compute
117        the difference.
118
119        :param name: name of peripheral instance, or all watched peripheral if `None`.
120        :raises ValueError: if instance name is unknown.
121        :return: The difference report as a string with ANSI formatting.
122        """
123        if name is None:
124            # return sum(map(self.report, self._watched.keys()))
125            report = ""
126            for register in self._watched:
127                report += self.report(register)
128            return report
129
130        from arm_gdb.common import RegisterDef, FieldBitfieldEnum, FieldBitfield
131
132        report = []
133        addr_map = {}
134        for peripheral, register in self._find(name):
135            value = self._watched[(peripheral, register)]
136            addr = peripheral.base_address + register.address_offset
137            new_value = self.read_uint(addr, register.size // 8)
138
139            if difference := new_value ^ value:
140                fields = []
141                for field in sorted(register.fields, key=lambda f: f.bit_offset):
142                    # Check if difference is within mask of this bit field
143                    if (difference & (((1 << field.bit_width) - 1) << field.bit_offset)):
144                        fields.append(FieldBitfield(
145                            field.name,
146                            field.bit_offset,
147                            field.bit_width,
148                            field.description
149                        ))
150                reg = RegisterDef(
151                    peripheral.name + "." + register.name,
152                    register.description,
153                    addr,
154                    register.size // 8,
155                    fields
156                )
157
158                # dump previous register values
159                class CachedInferior:
160                    def read_memory(self, addr, size):
161                        class MemoryView:
162                            def tobytes(self):
163                                return value.to_bytes(size, "little")
164                        return MemoryView()
165                with redirect_stdout(io.StringIO()) as buffer:
166                    reg.dump(CachedInferior(), include_descr=True, base=1, all=True)
167                for line in buffer.getvalue().splitlines():
168                    report.append(f"\033[2m- {line}\033[0m")
169
170                # dump current register values
171                inf = self._gdb.selected_inferior()
172                with redirect_stdout(io.StringIO()) as buffer:
173                    reg.dump(inf, include_descr=True, base=1, all=True)
174                for line in buffer.getvalue().splitlines():
175                    report.append(f"+ {line}")
176        return "\n".join(report)
class PeripheralWatcher(emdbg.debug.px4.device.Device):
 15class PeripheralWatcher(Device):
 16    """Visualize the changes of a peripheral register map."""
 17
 18    def __init__(self, gdb, filename: Path = None):
 19        from cmsis_svd.parser import SVDParser
 20        super().__init__(gdb)
 21        if filename is None: filename = self._SVD_FILE
 22        self.device = SVDParser.for_xml_file(filename).get_device()
 23        self._watched = {}
 24
 25    def _find(self, name):
 26        if isinstance(name, str):
 27            rname = None
 28            if "." in name:
 29                name, rname = name.split(".")
 30            peripheral = next((p for p in self.device.peripherals if p.name == name), None)
 31            if peripheral is None:
 32                raise ValueError(f"Unknown peripheral instance '{name}'! "
 33                                 f"Available peripherals are {','.join(p.name for p in self.device.peripherals)}")
 34            if rname:
 35                register = next((r for r in peripheral.registers if r.name == rname), None)
 36                if register is None:
 37                    raise ValueError(f"Unknown register instance '{name}.{rname}'! "
 38                                     f"Available registers are {','.join(r.name for r in peripheral.registers)}")
 39                return [(peripheral, register)]
 40            return [(peripheral, register) for register in peripheral.registers]
 41        return name if isinstance(name, list) else [name]
 42
 43    def address(self, name):
 44        """
 45        :return: A dictionary of register to address mapping.
 46        """
 47        return {(p,r): (p.base_address + r.address_offset,
 48                        p.base_address + r.address_offset + r.size//8)
 49                for p, r in self._find(name)}
 50
 51    def watch(self, name: str) -> str:
 52        """
 53        Add a peripheral to the watch list.
 54
 55        :param name: name of peripheral instance.
 56        :raises ValueError: if instance name is unknown.
 57
 58        :return: difference report compared to reset values as defined in SVD.
 59        """
 60        registers = self._find(name)
 61        self.update(registers)
 62        report = self.report(registers)
 63        self.update(registers)
 64        return report
 65
 66    def unwatch(self, name: str = None):
 67        """
 68        Remove a peripheral from the watch list.
 69
 70        :param name: name of peripheral instance, or all watched peripheral if `None`.
 71        :raises ValueError: if instance name is unknown.
 72        """
 73        if name is not None:
 74            for register in self._find(name):
 75                if register in self._watched:
 76                    self._watched.pop(register)
 77        else:
 78            self._watched = {}
 79
 80    def update(self, name: str = None):
 81        """
 82        Update all cached register values to the current values.
 83        Note: This performs a read from the device of all watched registers.
 84
 85        :param name: name of peripheral instance, or all watched peripheral if `None`.
 86        :raises ValueError: if instance name is unknown.
 87        """
 88        if name is not None:
 89            for peripheral, register in self._find(name):
 90                new = (peripheral, register) not in self._watched
 91                addr = peripheral.base_address + register.address_offset
 92                value = register.reset_value if new else self.read_uint(addr, register.size // 8)
 93                self._watched[(peripheral, register)] = value
 94        else:
 95            for register in self._watched:
 96                self.update(register)
 97
 98    def reset(self, name: str = None):
 99        """
100        Update all cached registers to their reset values as defined in the
101        CMSIS-SVD file.
102
103        :param name: name of peripheral instance, or all watched peripheral if `None`.
104        :raises ValueError: if instance name is unknown.
105        """
106        if name is not None:
107            for register in self._find(name):
108                self._watched.pop(register)
109                self.update(register)
110        else:
111            registers = list(self._watched)
112            for register in registers:
113                self.reset(register)
114
115    def report(self, name: str = None) -> str:
116        """
117        Compare the cached registers with the on-device registers and compute
118        the difference.
119
120        :param name: name of peripheral instance, or all watched peripheral if `None`.
121        :raises ValueError: if instance name is unknown.
122        :return: The difference report as a string with ANSI formatting.
123        """
124        if name is None:
125            # return sum(map(self.report, self._watched.keys()))
126            report = ""
127            for register in self._watched:
128                report += self.report(register)
129            return report
130
131        from arm_gdb.common import RegisterDef, FieldBitfieldEnum, FieldBitfield
132
133        report = []
134        addr_map = {}
135        for peripheral, register in self._find(name):
136            value = self._watched[(peripheral, register)]
137            addr = peripheral.base_address + register.address_offset
138            new_value = self.read_uint(addr, register.size // 8)
139
140            if difference := new_value ^ value:
141                fields = []
142                for field in sorted(register.fields, key=lambda f: f.bit_offset):
143                    # Check if difference is within mask of this bit field
144                    if (difference & (((1 << field.bit_width) - 1) << field.bit_offset)):
145                        fields.append(FieldBitfield(
146                            field.name,
147                            field.bit_offset,
148                            field.bit_width,
149                            field.description
150                        ))
151                reg = RegisterDef(
152                    peripheral.name + "." + register.name,
153                    register.description,
154                    addr,
155                    register.size // 8,
156                    fields
157                )
158
159                # dump previous register values
160                class CachedInferior:
161                    def read_memory(self, addr, size):
162                        class MemoryView:
163                            def tobytes(self):
164                                return value.to_bytes(size, "little")
165                        return MemoryView()
166                with redirect_stdout(io.StringIO()) as buffer:
167                    reg.dump(CachedInferior(), include_descr=True, base=1, all=True)
168                for line in buffer.getvalue().splitlines():
169                    report.append(f"\033[2m- {line}\033[0m")
170
171                # dump current register values
172                inf = self._gdb.selected_inferior()
173                with redirect_stdout(io.StringIO()) as buffer:
174                    reg.dump(inf, include_descr=True, base=1, all=True)
175                for line in buffer.getvalue().splitlines():
176                    report.append(f"+ {line}")
177        return "\n".join(report)

Visualize the changes of a peripheral register map.

PeripheralWatcher(gdb, filename: pathlib.Path = None)
18    def __init__(self, gdb, filename: Path = None):
19        from cmsis_svd.parser import SVDParser
20        super().__init__(gdb)
21        if filename is None: filename = self._SVD_FILE
22        self.device = SVDParser.for_xml_file(filename).get_device()
23        self._watched = {}
device
def address(self, name):
43    def address(self, name):
44        """
45        :return: A dictionary of register to address mapping.
46        """
47        return {(p,r): (p.base_address + r.address_offset,
48                        p.base_address + r.address_offset + r.size//8)
49                for p, r in self._find(name)}
Returns

A dictionary of register to address mapping.

def watch(self, name: str) -> str:
51    def watch(self, name: str) -> str:
52        """
53        Add a peripheral to the watch list.
54
55        :param name: name of peripheral instance.
56        :raises ValueError: if instance name is unknown.
57
58        :return: difference report compared to reset values as defined in SVD.
59        """
60        registers = self._find(name)
61        self.update(registers)
62        report = self.report(registers)
63        self.update(registers)
64        return report

Add a peripheral to the watch list.

Parameters
  • name: name of peripheral instance.
Raises
  • ValueError: if instance name is unknown.
Returns

difference report compared to reset values as defined in SVD.

def unwatch(self, name: str = None):
66    def unwatch(self, name: str = None):
67        """
68        Remove a peripheral from the watch list.
69
70        :param name: name of peripheral instance, or all watched peripheral if `None`.
71        :raises ValueError: if instance name is unknown.
72        """
73        if name is not None:
74            for register in self._find(name):
75                if register in self._watched:
76                    self._watched.pop(register)
77        else:
78            self._watched = {}

Remove a peripheral from the watch list.

Parameters
  • name: name of peripheral instance, or all watched peripheral if None.
Raises
  • ValueError: if instance name is unknown.
def update(self, name: str = None):
80    def update(self, name: str = None):
81        """
82        Update all cached register values to the current values.
83        Note: This performs a read from the device of all watched registers.
84
85        :param name: name of peripheral instance, or all watched peripheral if `None`.
86        :raises ValueError: if instance name is unknown.
87        """
88        if name is not None:
89            for peripheral, register in self._find(name):
90                new = (peripheral, register) not in self._watched
91                addr = peripheral.base_address + register.address_offset
92                value = register.reset_value if new else self.read_uint(addr, register.size // 8)
93                self._watched[(peripheral, register)] = value
94        else:
95            for register in self._watched:
96                self.update(register)

Update all cached register values to the current values. Note: This performs a read from the device of all watched registers.

Parameters
  • name: name of peripheral instance, or all watched peripheral if None.
Raises
  • ValueError: if instance name is unknown.
def reset(self, name: str = None):
 98    def reset(self, name: str = None):
 99        """
100        Update all cached registers to their reset values as defined in the
101        CMSIS-SVD file.
102
103        :param name: name of peripheral instance, or all watched peripheral if `None`.
104        :raises ValueError: if instance name is unknown.
105        """
106        if name is not None:
107            for register in self._find(name):
108                self._watched.pop(register)
109                self.update(register)
110        else:
111            registers = list(self._watched)
112            for register in registers:
113                self.reset(register)

Update all cached registers to their reset values as defined in the CMSIS-SVD file.

Parameters
  • name: name of peripheral instance, or all watched peripheral if None.
Raises
  • ValueError: if instance name is unknown.
def report(self, name: str = None) -> str:
115    def report(self, name: str = None) -> str:
116        """
117        Compare the cached registers with the on-device registers and compute
118        the difference.
119
120        :param name: name of peripheral instance, or all watched peripheral if `None`.
121        :raises ValueError: if instance name is unknown.
122        :return: The difference report as a string with ANSI formatting.
123        """
124        if name is None:
125            # return sum(map(self.report, self._watched.keys()))
126            report = ""
127            for register in self._watched:
128                report += self.report(register)
129            return report
130
131        from arm_gdb.common import RegisterDef, FieldBitfieldEnum, FieldBitfield
132
133        report = []
134        addr_map = {}
135        for peripheral, register in self._find(name):
136            value = self._watched[(peripheral, register)]
137            addr = peripheral.base_address + register.address_offset
138            new_value = self.read_uint(addr, register.size // 8)
139
140            if difference := new_value ^ value:
141                fields = []
142                for field in sorted(register.fields, key=lambda f: f.bit_offset):
143                    # Check if difference is within mask of this bit field
144                    if (difference & (((1 << field.bit_width) - 1) << field.bit_offset)):
145                        fields.append(FieldBitfield(
146                            field.name,
147                            field.bit_offset,
148                            field.bit_width,
149                            field.description
150                        ))
151                reg = RegisterDef(
152                    peripheral.name + "." + register.name,
153                    register.description,
154                    addr,
155                    register.size // 8,
156                    fields
157                )
158
159                # dump previous register values
160                class CachedInferior:
161                    def read_memory(self, addr, size):
162                        class MemoryView:
163                            def tobytes(self):
164                                return value.to_bytes(size, "little")
165                        return MemoryView()
166                with redirect_stdout(io.StringIO()) as buffer:
167                    reg.dump(CachedInferior(), include_descr=True, base=1, all=True)
168                for line in buffer.getvalue().splitlines():
169                    report.append(f"\033[2m- {line}\033[0m")
170
171                # dump current register values
172                inf = self._gdb.selected_inferior()
173                with redirect_stdout(io.StringIO()) as buffer:
174                    reg.dump(inf, include_descr=True, base=1, all=True)
175                for line in buffer.getvalue().splitlines():
176                    report.append(f"+ {line}")
177        return "\n".join(report)

Compare the cached registers with the on-device registers and compute the difference.

Parameters
  • name: name of peripheral instance, or all watched peripheral if None.
Raises
  • ValueError: if instance name is unknown.
Returns

The difference report as a string with ANSI formatting.