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)
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.
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.
Inherited Members
- emdbg.debug.px4.device.Device
- Gpio
- Irq
- IrqNuttX
- architecture
- uptime
- max_interrupts
- vector_table
- vector_table_nuttx
- gpios
- coredump
- cpuid
- idcode
- flash_size
- line
- uid
- package
- devid
- rev
- emdbg.debug.px4.base.Base
- register_names
- registers
- read_register
- write_register
- write_registers
- fix_nuttx_sp
- lookup_static_symbol_in_function
- lookup_static_symbol_ptr
- lookup_global_symbol_ptr
- lookup_static_symbol_in_function_ptr
- value_ptr
- addr_ptr
- read_memory
- write_memory
- read_uint
- read_int
- read_string
- symtab_line
- block
- description_at
- integer_type
- uint32
- int32