Skip to content

Commit

Permalink
feature/compare_elements (#1)
Browse files Browse the repository at this point in the history
Reviewed-on: https://code.fueldner.net/opensource/pysvd/pulls/1

Add compare feature for elements.
Co-authored-by: Benjamin Füldner <[email protected]>
Co-committed-by: Benjamin Füldner <[email protected]>
  • Loading branch information
bfueldner committed Feb 3, 2022
1 parent 1767501 commit 8c3d375
Show file tree
Hide file tree
Showing 6 changed files with 466 additions and 0 deletions.
5 changes: 5 additions & 0 deletions pysvd/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ def parse(self, node):
super().parse(node)

self.name = pysvd.parser.Text(pysvd.node.Element(node, 'name', True))
displayName = pysvd.parser.Text(pysvd.node.Element(node, 'displayName'))
if displayName is not None:
self.displayName = displayName
description = pysvd.parser.Text(pysvd.node.Element(node, 'description'))
if description is not None:
self.description = description
Expand All @@ -125,6 +128,8 @@ def parse(self, node):
def set_index(self, value):
value = str(value)
self.name = self.name.replace('%s', value)
if hasattr(self, 'displayName') and self.displayName is not None:
self.displayName = self.displayName.replace('%s', value)
if hasattr(self, 'description') and self.description is not None:
self.description = self.description.replace('%s', value)

Expand Down
159 changes: 159 additions & 0 deletions pysvd/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@
import pysvd


def compare_attribute(lhs, rhs, attibute):
"""Compare attibute of objects.
"""
if hasattr(lhs, attibute) != hasattr(rhs, attibute):
return False

if hasattr(lhs, attibute):
return getattr(lhs, attibute) == getattr(rhs, attibute)

return True

# /device
# http://www.keil.com/pack/doc/cmsis/svd/html/elem_device.html
class Device(pysvd.classes.Base):
Expand Down Expand Up @@ -190,6 +201,49 @@ def __init__(self, parent, node):

super().__init__(parent, node)

def __eq__(self, other):
"""Compare element and all attributes.
"""
if not isinstance(other, Peripheral):
return NotImplemented

if not self.equal_struct(other):
return False

return compare_attribute(self, other, 'name') and \
compare_attribute(self, other, 'description') and \
compare_attribute(self, other, 'version') and \
compare_attribute(self, other, 'alternatePeripheral') and \
compare_attribute(self, other, 'groupName') and \
compare_attribute(self, other, 'prependToName') and \
compare_attribute(self, other, 'appendToName') and \
compare_attribute(self, other, 'headerStructName') and \
compare_attribute(self, other, 'disableCondition') and \
compare_attribute(self, other, 'baseAddress') and \
compare_attribute(self, other, 'addressBlock') and \
compare_attribute(self, other, 'interrupt')

def equal_struct(self, other):
"""Compare only child elements.
"""
if not isinstance(other, Peripheral):
return NotImplemented

if len(self.registers) != len(other.registers):
return False

for (lhs, rhs) in zip(self.registers, other.registers):
if lhs != rhs:
return False

if len(self.clusters) != len(other.clusters):
return False

for (lhs, rhs) in zip(self.clusters, other.clusters):
if lhs != rhs:
return False
return True

def set_offset(self, value):
self.baseAddress += value

Expand Down Expand Up @@ -348,6 +402,44 @@ def __init__(self, parent, node):

super().__init__(parent, node)

def __eq__(self, other):
"""Compare element and all attributes.
"""
if not isinstance(other, Register):
return NotImplemented

if not self.equal_struct(other):
return False

return compare_attribute(self, other, 'name') and \
compare_attribute(self, other, 'displayName') and \
compare_attribute(self, other, 'description') and \
compare_attribute(self, other, 'alternateGroup') and \
compare_attribute(self, other, 'alternateRegister') and \
compare_attribute(self, other, 'addressOffset') and \
compare_attribute(self, other, 'size') and \
compare_attribute(self, other, 'access') and \
compare_attribute(self, other, 'protection') and \
compare_attribute(self, other, 'resetValue') and \
compare_attribute(self, other, 'resetMask') and \
compare_attribute(self, other, 'dataType') and \
compare_attribute(self, other, 'modifiedWriteValues') and \
compare_attribute(self, other, 'readAction')

def equal_struct(self, other):
"""Compare only child elements.
"""
if not isinstance(other, Register):
return NotImplemented

if len(self.fields) != len(other.fields):
return False

for (lhs, rhs) in zip(self.fields, other.fields):
if lhs != rhs:
return False
return True

def set_offset(self, value):
self.addressOffset += value

Expand Down Expand Up @@ -447,6 +539,41 @@ class Field(pysvd.classes.Dim):
def __init__(self, parent, node):
super().__init__(parent, node)

def __eq__(self, other):
if not isinstance(other, Field):
return NotImplemented

if not self.equal_struct(other):
return False

return compare_attribute(self, other, 'name') and \
compare_attribute(self, other, 'description') and \
compare_attribute(self, other, 'bitOffset') and \
compare_attribute(self, other, 'bitWidth') and \
compare_attribute(self, other, 'access') and \
compare_attribute(self, other, 'modifiedWriteValues') and \
compare_attribute(self, other, 'readAction') and \
compare_attribute(self, other, 'writeConstraint')

def equal_struct(self, other):
"""Check structure elements, but not overridable attributes.
"""
if not isinstance(other, Field):
return NotImplemented

return compare_attribute(self, other, 'enumeratedValues')

x = """
if len(self.enumeratedValues) != len(other.enumeratedValues):
return False
for (lhs, rhs) in zip(self.enumeratedValues, other.enumeratedValues):
if lhs != rhs:
return False
return True
"""


def set_offset(self, value):
self.bitOffset += value

Expand Down Expand Up @@ -521,6 +648,29 @@ def __init__(self, parent, node):

super().__init__(parent, node)

def __eq__(self, other):
if not isinstance(other, EnumeratedValues):
return NotImplemented

if not self.equal_struct(other):
return False

return compare_attribute(self, other, 'name') and \
compare_attribute(self, other, 'headerEnumName') and \
compare_attribute(self, other, 'usage')

def equal_struct(self, other):
if not isinstance(other, EnumeratedValues):
return NotImplemented

if len(self.enumeratedValues) != len(other.enumeratedValues):
return False

for (lhs, rhs) in zip(self.enumeratedValues, other.enumeratedValues):
if lhs != rhs:
return False
return True

def parse(self, node):
super().parse(node)

Expand All @@ -545,6 +695,15 @@ class EnumeratedValue(pysvd.classes.Parent):
def __init__(self, parent, node):
super().__init__(parent, node)

def __eq__(self, other):
if not isinstance(other, EnumeratedValue):
return NotImplemented

return compare_attribute(self, other, 'name') and \
compare_attribute(self, other, 'value') and \
compare_attribute(self, other, 'description') and \
compare_attribute(self, other, 'isDefault')

def parse(self, node):
super().parse(node)

Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ pytest-codestyle
pytest-flakes
pytest-cov
coveralls
naturalsort
colorama
133 changes: 133 additions & 0 deletions scripts/svd2register.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
# coding: utf-8
"""pysvd example project.
Read SVD file and generate C-style register access structs.
"""

import argparse
import xml.etree.ElementTree as ET
import pysvd
from enum import Enum

access = {
pysvd.type.access.read_write: '__IO',
pysvd.type.access.read_only: '__I',
pysvd.type.access.write_only: '__O',
}

data_type = {
8: 'uint8_t',
16: 'uint16_t',
32: 'uint32_t',
}

def format_registers(output, prefix, registers):
data = []
for register in registers:
data.append(('`{1} <{0}.{1}_>`_'.format(prefix, register.name), '0x{:02X}'.format(register.addressOffset)))
if len(data):
output.write(table(('Register', 'Offset'), data))

for register in registers:
output.write('.. _{}.{}:\n\n'.format(prefix, register.name))

output.write(underline(register.description, section.subsubsection))

output.write(rst_list_name.format('Name', register.name))
output.write(rst_list_name.format('Size', register.size))
output.write(rst_list_name.format('Offset', '0x{:02X}'.format(register.addressOffset)))
output.write(rst_list_name.format('Reset', '0x{:{fill}{width}X}'.format(register.resetValue, fill='0', width=register.size // 4)))
output.write(rst_list_name.format('Access', register.access))
output.write('\n')

# Table

# Bit description
for field in register.fields:
if field.bitWidth == 1:
output.write('- Bit {} ({}) - {}\n'.format(field.bitOffset, field.access, field.name))
else:
output.write('- Bits {}:{} ({}) - {}\n'.format(
field.bitOffset + field.bitWidth - 1, field.bitOffset, field.access, field.name))
output.write(' {}\n\n'.format(field.description))

if hasattr(field, 'enumeratedValues'):
for enumeratedValue in field.enumeratedValues.enumeratedValues:
output.write(' - {} - {}\n'.format(enumeratedValue.value, enumeratedValue.name))
if hasattr(enumeratedValue, 'description'):
output.write(' {}\n'.format(enumeratedValue.description))
output.write('\n')


def write_register(register, output):
output.write(" union\n {\n")
output.write(" {1} {2} {0.name};\n".format(register, access[register.access], data_type[register.size]))


def write_peripheral(peripheral, output):
output.write("/* {0.name} - {0.description} */\n\n".format(peripheral))

index = 1;
offset = 0
output.write("struct __attribute__((packed)) {0.name}_t\n{{\n".format(peripheral))
for register in peripheral.registers:
if register.addressOffset != offset:
output.write(" __I {0} _{1};\n".format(data_type[register.addressOffset - offset]), index)
index += 1
write_register(register, output)
offset = register.addressOffset + register.size // 8
output.write("};\n")
output.write("static_assert(sizeof({0}_t) == {1}, \"Size of {0}_t mismatch\");\n\n".format(peripheral.name, offset))

def main():
parser = argparse.ArgumentParser(description='SVD to C-style register access structs')
parser.add_argument('--svd', metavar='FILE', type=str, help='System view description (SVD) file', required=True)
parser.add_argument('--output', '-o', metavar='FILE', type=str, help='C output file', required=True)
parser.add_argument('--version', action='version', version=pysvd.__version__)
args = parser.parse_args()

node = ET.parse(args.svd).getroot()
device = pysvd.element.Device(node)

output = open(args.output, "w")

# Header
output.write("/**\n" \
" * @file\n" \
" * @version {0.version}\n" \
" * @brief Register access structs for {0.vendor} {0.name}\n" \
" * @note This file is autogenerated using pysvd {1.__version__}\n" \
" */\n\n".format(device, pysvd)
)
output.write("#pragma once\n\n")

output.write("#define __I volatile const /*!< Read only permission */\n" \
"#define __O volatile /*!< Write only permission */\n" \
"#define __IO volatile /*!< Read/write permission */\n\n"
)

# Interrupts
interrupts = []
for peripheral in device.peripherals:
interrupts += peripheral.interrupts

interrupt_count = 0
interrupt_name = ""
output.write("enum IRQn\n{\n")
for interrupt in sorted(interrupts, key=lambda interrupt: interrupt.value):
if interrupt_name != interrupt.name:
output.write(" {0.name:20}= {0.value},\n".format(interrupt))
interrupt_name = interrupt.name
interrupt_count += 1
output.write("};\n\n")

# Peripherals
data = []
for peripheral in sorted(device.peripherals, key=lambda peripheral: peripheral.name):
if peripheral.name.startswith('ADC'):
write_peripheral(peripheral, output)
output.close()

if __name__ == '__main__':
main()
Loading

0 comments on commit 8c3d375

Please sign in to comment.