diff --git a/.travis.yml b/.travis.yml
index acb909b4..323eca95 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,6 +19,7 @@ script:
- (cd tools/generator; make generate-avr)
- (cd tools/generator; make generate-stm32)
- (cd tools/generator; make generate-sam)
+ - (cd tools/generator; make generate-nrf)
- tools/device/scripts/stats --count
- tools/device/scripts/stats --name >/dev/null
- tools/device/scripts/stats --ram >/dev/null
diff --git a/README.md b/README.md
index 4f4ad5c3..0fea2987 100644
--- a/README.md
+++ b/README.md
@@ -13,18 +13,19 @@ These tools and this data set is maintained and curated by
It is licensed under the MPLv2 license.
The CI checks daily for new data: [![](https://travis-ci.org/modm-io/modm-devices.svg?branch=develop)](https://travis-ci.org/modm-io/modm-devices)
-Currently data for 3240 devices is available.
+Currently data for 3247 devices is available.
Please open an issue or better yet a pull request for additional support.
| Family | Devices | Family | Devices | Family | Devices |
|:--------------|:--------|:--------------|:--------|:--------------|:--------|
| AT90 | 12 | ATMEGA | 370 | ATTINY | 148 |
-| SAMD | 209 | SAML | 47 | STM32F0 | 168 |
-| STM32F1 | 194 | STM32F2 | 71 | STM32F3 | 143 |
-| STM32F4 | 340 | STM32F7 | 174 | STM32G0 | 117 |
-| STM32G4 | 263 | STM32H7 | 153 | STM32L0 | 307 |
-| STM32L1 | 140 | STM32L4 | 353 | STM32WB | 31 |
+| NRF52 | 7 | SAMD | 209 | SAML | 47 |
+| STM32F0 | 168 | STM32F1 | 194 | STM32F2 | 71 |
+| STM32F3 | 143 | STM32F4 | 340 | STM32F7 | 174 |
+| STM32G0 | 117 | STM32G4 | 263 | STM32H7 | 153 |
+| STM32L0 | 307 | STM32L1 | 140 | STM32L4 | 353 |
+| STM32WB | 31 |
diff --git a/devices/nrf/nrf52810.xml b/devices/nrf/nrf52810.xml
new file mode 100644
index 00000000..956965c9
--- /dev/null
+++ b/devices/nrf/nrf52810.xml
@@ -0,0 +1,169 @@
+
+
+
+
+ {platform}{family}{series}-{package}{function}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/devices/nrf/nrf52811.xml b/devices/nrf/nrf52811.xml
new file mode 100644
index 00000000..e6cbeea4
--- /dev/null
+++ b/devices/nrf/nrf52811.xml
@@ -0,0 +1,182 @@
+
+
+
+
+ {platform}{family}{series}-{package}{function}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/devices/nrf/nrf52820.xml b/devices/nrf/nrf52820.xml
new file mode 100644
index 00000000..3ed75754
--- /dev/null
+++ b/devices/nrf/nrf52820.xml
@@ -0,0 +1,189 @@
+
+
+
+
+ {platform}{family}{series}-{package}{function}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/devices/nrf/nrf52832.xml b/devices/nrf/nrf52832.xml
new file mode 100644
index 00000000..23da70c8
--- /dev/null
+++ b/devices/nrf/nrf52832.xml
@@ -0,0 +1,228 @@
+
+
+
+
+ {platform}{family}{series}-{package}{function}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/devices/nrf/nrf52833.xml b/devices/nrf/nrf52833.xml
new file mode 100644
index 00000000..3fef5258
--- /dev/null
+++ b/devices/nrf/nrf52833.xml
@@ -0,0 +1,284 @@
+
+
+
+
+ {platform}{family}{series}-{package}{function}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/devices/nrf/nrf52840.xml b/devices/nrf/nrf52840.xml
new file mode 100644
index 00000000..dfd73e9a
--- /dev/null
+++ b/devices/nrf/nrf52840.xml
@@ -0,0 +1,296 @@
+
+
+
+
+ {platform}{family}{series}-{package}{function}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/generator/Makefile b/tools/generator/Makefile
index 69b5de56..bde282a7 100644
--- a/tools/generator/Makefile
+++ b/tools/generator/Makefile
@@ -15,7 +15,7 @@ raw-device-data/%-devices:
(cd raw-data-extractor && python3 extract-$(@:raw-device-data/%-devices=%).py)
.PHONY: extract
-extract: raw-device-data/avr-devices raw-device-data/stm32-devices raw-device-data/sam-devices
+extract: raw-device-data/avr-devices raw-device-data/stm32-devices raw-device-data/sam-devices raw-device-data/nrf-devices
.PHONY: clean_extract
clean_extract:
@@ -41,6 +41,17 @@ generate-sam%: raw-device-data/sam-devices ext/cmsis-5-partial
generate-sam: generate-saml21 generate-samd21 generate-samd51
+
+# NRF device files
+.PHONY: generate-nrf%
+generate-nrf%: raw-device-data/nrf-devices ext/cmsis-5-partial
+ @rm -f ../../devices/nrf/$(@:generate-%=%)*
+ ./nrf_generator.py $(@:generate-%=%)
+
+.PHONY: generate-nrf
+generate-nrf: generate-nrf52810 generate-nrf52811 generate-nrf52820 generate-nrf52832 generate-nrf52833 generate-nrf52840
+
+
# STM32 device files
.PHONY: generate-stm32%
generate-stm32%: raw-device-data/sam-devices ext/cmsis-5-partial ext/cmsis-header-stm32
diff --git a/tools/generator/dfg/nrf/__init__.py b/tools/generator/dfg/nrf/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tools/generator/dfg/nrf/nrf_device_tree.py b/tools/generator/dfg/nrf/nrf_device_tree.py
new file mode 100644
index 00000000..58790ffa
--- /dev/null
+++ b/tools/generator/dfg/nrf/nrf_device_tree.py
@@ -0,0 +1,300 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013-2016, Niklas Hauser
+# Copyright (c) 2016, Fabian Greif
+# Copyright (c) 2020, Hannes Ellinger
+# All rights reserved.
+
+import math
+import logging
+import re
+import os
+
+from ..device_tree import DeviceTree
+from ..input.xml import XMLReader
+
+from .nrf_identifier import NRFIdentifier
+
+LOGGER = logging.getLogger('dfg.nrf.reader')
+
+class NRFDeviceTree:
+ """ NRFDeviceTree
+ This NRF specific part description file reader knows the structure and
+ translates the data into a platform independent format.
+ """
+
+ @staticmethod
+ def _properties_from_file(ld_filename):
+ xml_filename = re.sub('\_\w{4}.ld', '.svd', ld_filename)
+ device_file = XMLReader(xml_filename)
+ p = {}
+
+ partname = ld_filename.split('/')[-1]
+ partname = partname.split('.')[0]
+ partname = partname.replace('_', '-')
+
+ did = NRFIdentifier.from_string(partname.lower())
+ p['id'] = did
+
+ LOGGER.info("Parsing '%s'", did.string)
+
+ # information about the core and architecture
+ core = device_file.query("//device/cpu/name")[0].text.lower().replace("cm", "cortex-m")
+ if device_file.query("//device/cpu/fpuPresent")[0].text == '1':
+ core += "f"
+ p["core"] = core
+
+
+ # find the values for flash and ram
+ memlines = []
+ with open(ld_filename, 'r') as linkerfile:
+ status = 0
+ for line in linkerfile:
+ if status == 0 and "MEMORY" in line:
+ status = 1
+ elif status == 1 and "{" in line:
+ status = 2
+ elif status == 2:
+ if "}" in line:
+ status = 3
+ else:
+ memlines.append(line)
+
+ memories = []
+
+ for memline in memlines:
+ matchString = r" (?P\w*) \((?P\w*)\) : ORIGIN = (?P0x\d*), LENGTH = (?P0x\d*)"
+ match = re.search(matchString, memline)
+ memories.append({
+ "name": match.group("name").lower(),
+ "access": match.group("access").lower(),
+ "size": str(int(match.group("size").lower(), 16)),
+ "start": match.group("start").lower()})
+
+ p["memories"] = memories
+
+
+ # Signals
+ signals = {}
+ raw_signals = device_file.query("//peripherals/peripheral/registers/cluster")
+ for s in raw_signals:
+ if s.find('name').text == "PSEL":
+
+ # find parent peripheral
+ parent_peripheral_instance = s.getparent().getparent().find('name').text.lower()
+ signals[parent_peripheral_instance] = []
+
+ # find all signals of peripheral
+ signal_elements = s.findall('register/name')
+ for signal_element in signal_elements:
+ signal_name = signal_element.text.lower()
+ signals[parent_peripheral_instance].append(signal_name)
+
+
+ # drivers and gpios
+ raw_modules = device_file.query("//peripherals/peripheral")
+ modules = []
+ ports = {}
+ gpios = []
+ for m in raw_modules:
+ modulename = m.find('name').text
+ moduledesc = m.find('description').text
+
+ if "GPIO Port" in moduledesc:
+ # omit the leading P of the port names, also of the derived ports
+ portnumber = modulename[1:]
+ if m.get('derivedFrom') is not None:
+ portsize = ports[m.get('derivedFrom')[1:]]
+ else:
+ portsize = int(m.find('size').text, base=0)
+
+ ports[portnumber] = portsize
+ for i in range(portsize):
+ gpios.append((portnumber, str(i)))
+
+ else:
+ matchString = r"(?P.*\D)(?P\d*$)"
+ match = re.search(matchString, modulename)
+ modules.append({'module': match.group("module").lower(), 'instance': modulename.lower()})
+
+ # copy available signals to all derived peripherals
+ if m.get('derivedFrom') is not None:
+ if m.get('derivedFrom').lower() in signals:
+ LOGGER.debug(modulename.lower() + " is derived from " + m.get('derivedFrom').lower())
+ signals[modulename.lower()] = signals[m.get('derivedFrom').lower()]
+ p['modules'] = sorted(list(set([(m['module'], m['instance']) for m in modules])))
+ p['gpios'] = gpios
+ p['signals'] = []
+ for instance in signals:
+ for signal in signals[instance]:
+ matchString = r"(?P.*\D)(?P\d*$)"
+ match = re.search(matchString, instance)
+ if not "[%s]" in signal: # TODO take care of multichannel signals like OUT[%s] of PWM peripheral
+ p['signals'].append({'driver': match.group("module").lower(), 'instance': instance, 'name': signal})
+
+
+ interrupts = []
+ raw_interrupt = device_file.query("//peripherals/peripheral/interrupt")
+ for i in raw_interrupt:
+ interruptname = i.find('name').text
+ interruptnum = i.find('value').text
+ interrupts.append({'position': interruptnum, 'name': interruptname})
+
+ # Unique interrupts
+ p['interrupts'] = []
+ for interrupt in interrupts:
+ if interrupt not in p['interrupts']:
+ p['interrupts'].append(interrupt)
+
+ LOGGER.debug("Found GPIOs: [%s]", ", ".join([p.upper() + "." + i for p,i in p['gpios']]))
+ LOGGER.debug("Available Modules are:\n" + NRFDeviceTree._modulesToString(p['modules']))
+ LOGGER.debug("Found Signals:")
+ for sig in p['signals']:
+ LOGGER.debug(" %s", sig)
+ LOGGER.debug("Found Interrupts:")
+ for intr in p['interrupts']:
+ LOGGER.debug(" %s", intr)
+
+ return p
+
+ @staticmethod
+ def _modulesToString(modules):
+ string = ""
+ mods = sorted(modules)
+ char = mods[0][0][0:1]
+ for module, instance in mods:
+ if not instance.startswith(char):
+ string += "\n"
+ string += instance + " \t"
+ char = instance[0][0:1]
+ return string
+
+ @staticmethod
+ def _device_tree_from_properties(p):
+ tree = DeviceTree('device')
+ tree.ids.append(p['id'])
+
+ def topLevelOrder(e):
+ order = ['attribute-flash', 'attribute-ram', 'attribute-eeprom', 'attribute-core', 'attribute-mcu', 'header', 'attribute-define']
+ if e.name in order:
+ if e.name in ['attribute-flash', 'attribute-eeprom', 'attribute-ram']:
+ return (order.index(e.name), int(e['value']))
+ else:
+ return (order.index(e.name), e['value'])
+ return (len(order), -1)
+ # tree.addSortKey(topLevelOrder)
+
+ # NRFDeviceTree.addDeviceAttributesToNode(p, tree, 'attribute-flash')
+ # NRFDeviceTree.addDeviceAttributesToNode(p, tree, 'attribute-ram')
+ # NRFDeviceTree.addDeviceAttributesToNode(p, tree, 'attribute-eeprom')
+ # NRFDeviceTree.addDeviceAttributesToNode(p, tree, 'attribute-mcu')
+
+ def driverOrder(e):
+ if e.name == 'driver':
+ if e['name'] == 'core':
+ # place the core at the very beginning
+ return ('aaaaaaa', e['type'])
+ if e['name'] == 'gpio':
+ # place the gpio at the very end
+ return ('zzzzzzz', e['type'])
+ # sort remaining drivers by type and compatible strings
+ return (e['name'], e['type'])
+ return ("", "")
+ tree.addSortKey(driverOrder)
+
+ # Core
+ core_child = tree.addChild('driver')
+ core_child.setAttributes('name', 'core', 'type', p['core'])
+ core_child.addSortKey(lambda e: (int(e['position']), e['name']) if e.name == 'vector' else (-1, ""))
+ core_child.addSortKey(lambda e: (e['name'], int(e['size'])) if e.name == 'memory' else ("", -1))
+
+ for section in p["memories"]:
+ memory_section = core_child.addChild("memory")
+ memory_section.setAttributes(["name", "access", "start", "size"], section)
+ # sort the node children by start address and size
+ core_child.addSortKey(lambda e: (int(e["start"], 16), int(e["size"])) if e.name == "memory" else (-1, -1))
+
+ # for memory in ['flash', 'ram', 'lpram', 'eeprom']:
+ # if memory not in p: continue;
+ # memory_section = core_child.addChild('memory')
+ # memory_section.setAttribute('name', memory)
+ # memory_section.setAttribute('size', p[memory])
+
+ for vector in p['interrupts']:
+ if int(vector['position']) < 0: continue;
+ vector_section = core_child.addChild('vector')
+ vector_section.setAttributes(['position', 'name'], vector)
+
+ modules = {}
+ for m, i in p['modules']:
+ # filter out non-peripherals: fuses, micro-trace buffer
+ if m in ['fuses', 'mtb', 'systemcontrol', 'systick', 'hmatrixb', 'hmatrix']: continue;
+ if m not in modules:
+ modules[m] = [i]
+ else:
+ modules[m].append(i)
+
+
+ compatible = p['id']['platform'] + p['id']['family']
+ # add all other modules
+ for name, instances in modules.items():
+ driver = tree.addChild('driver')
+ dtype = name
+
+ driver.setAttributes('name', dtype, 'type', compatible)
+ # Add all instances to this driver
+ if any(i != dtype for i in instances):
+ driver.addSortKey(lambda e: e['value'])
+ for i in instances:
+ inst = driver.addChild('instance')
+ inst.setValue(i[len(dtype):])
+
+ # GPIO driver
+ gpio_driver = tree.addChild('driver')
+ gpio_driver.setAttributes('name', 'gpio', 'type', compatible)
+ # gpio_driver.addSortKey(lambda e : (e['port'], int(e['pin'])))
+
+ # add all signals
+ for s in p['signals']:
+ driver, instance, name = s['driver'], s['instance'], s['name']
+ # add the af node
+ gpio_signal = {'driver': driver}
+ if instance != driver:
+ gpio_signal['instance'] = instance.replace(driver, '')
+ if name != driver and name != 'int':
+ if 'index' in s: name += s['index'];
+ gpio_signal['name'] = name
+ elif 'index' in s:
+ gpio_signal['name'] = s['index']
+ if "name" not in gpio_signal:
+ LOGGER.error("%s has no name!", s)
+ continue
+
+ af = gpio_driver.addChild('signal')
+ af.setAttributes(['driver', 'instance', 'name'], gpio_signal)
+
+ # add all GPIOs
+ for port, pin in p['gpios']:
+ pin_driver = gpio_driver.addChild('gpio')
+ pin_driver.setAttributes('port', port, 'pin', pin)
+ pin_driver.addSortKey(lambda e: (e['driver'],
+ e['instance'] if e['instance'] is not None else '',
+ e['name'] if e['name'] is not None else ''))
+
+ return tree
+
+ @staticmethod
+ def addDeviceAttributesToNode(p, node, name):
+ pname = name.split('-')[-1]
+ if pname not in p: return;
+ props = p[pname]
+ if not isinstance(props, list):
+ props = [props]
+ for prop in props:
+ child = node.addChild(name)
+ child.setValue(prop)
+
+ @staticmethod
+ def from_file(filename):
+ p = NRFDeviceTree._properties_from_file(filename)
+ if p is None: return None;
+ return NRFDeviceTree._device_tree_from_properties(p)
diff --git a/tools/generator/dfg/nrf/nrf_groups.py b/tools/generator/dfg/nrf/nrf_groups.py
new file mode 100644
index 00000000..dcca1bab
--- /dev/null
+++ b/tools/generator/dfg/nrf/nrf_groups.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020 Hannes Ellinger
+# All rights reserved.
+
+nrf_groups = \
+[
+ # NRF5 devices
+ {
+ 'family': ['51'],
+ 'series': ['822']
+ },{
+ 'family': ['52'],
+ 'series': ['810', '811', '820', '832', '833', '840']
+ },
+]
diff --git a/tools/generator/dfg/nrf/nrf_identifier.py b/tools/generator/dfg/nrf/nrf_identifier.py
new file mode 100644
index 00000000..64a4c303
--- /dev/null
+++ b/tools/generator/dfg/nrf/nrf_identifier.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 , Kevin Läufer
+# Copyright (c) 2013-2014, Niklas Hauser
+# Copyright (c) 2016, Fabian Greif
+# Copyright (c) 2020, Hannes Ellinger
+# All rights reserved.
+
+import re
+import logging
+
+from modm_devices.device_identifier import DeviceIdentifier
+
+LOGGER = logging.getLogger("dfg.nrf.identifier")
+
+class NRFIdentifier:
+ """ NRFIdentifier
+ A class to parse NRF device strings, e.g. NRF52840-QIAA.
+ Device names are organized as follows:
+ NRF 52 840 - QI AA
+ {platform}{family}{series}-{package}{function}
+ """
+
+ @staticmethod
+ def from_string(string):
+ string = string.lower()
+
+ if string.startswith("nrf"):
+ matchString = r"nrf(?P[0-9]{2})(?P[0-9]{3})-(?P\w{2})(?P\w{2})"
+ match = re.search(matchString, string)
+ if match:
+ i = DeviceIdentifier("{platform}{family}{series}-{package}{function}")
+ i.set("platform", "nrf")
+ i.set("family", match.group("family").lower())
+ i.set("series", match.group("series").lower())
+ i.set("package", match.group("package").lower())
+ i.set("function", match.group("function").lower())
+ return i
+
+
+ LOGGER.error("Parse Error: unknown platform. Device string: '%s'", string)
+ exit(1)
diff --git a/tools/generator/nrf_generator.py b/tools/generator/nrf_generator.py
new file mode 100755
index 00000000..bf6e8c1c
--- /dev/null
+++ b/tools/generator/nrf_generator.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013-2016, Niklas Hauser
+# Copyright (c) 2016, Fabian Greif
+# All rights reserved.
+# TESTING: exec(open("./nrf_generator.py").read())
+
+import os
+import sys
+import glob
+import logging
+
+import dfg.logger
+
+from dfg.merger import DeviceMerger
+from dfg.nrf.nrf_device_tree import NRFDeviceTree
+from dfg.nrf.nrf_groups import nrf_groups
+from dfg.output.device_file import DeviceFileWriter
+from modm_devices.parser import DeviceParser
+from deepdiff import DeepDiff
+
+LOGGER = logging.getLogger('dfg.nrf')
+
+if __name__ == "__main__":
+ loglevel = 'INFO'
+ devs = []
+ device_depth = 1e6
+ simulate = False
+
+ for arg in sys.argv[1:]:
+ if arg.startswith('-n'):
+ simulate = True
+ continue
+ if arg.startswith('--log='):
+ loglevel = arg.replace('--log=', '')
+ continue
+ if arg.startswith('--depth='):
+ device_depth = int(arg.replace('--depth=', '')) - 1
+ continue
+ devs.append(arg)
+
+ if not len(devs):
+ devs.append('nrf52810')
+ devs.append('nrf52811')
+ devs.append('nrf52820')
+ devs.append('nrf52832')
+ devs.append('nrf52833')
+ devs.append('nrf52840')
+
+ dfg.logger.configure_logger(loglevel)
+
+ devices = {}
+ for dev in devs:
+ ld_path = os.path.join(os.path.dirname(__file__), 'raw-device-data', 'nrf-devices', 'nrf', (dev.lower() + '_*.ld'))
+ files = glob.glob(ld_path)
+ for filename in files:
+ device = NRFDeviceTree.from_file(filename)
+ if device is None: continue;
+ devices[device.ids.string] = device
+ if device_depth > 0:
+ device_depth -= 1
+ else:
+ exit(1)
+
+ mergedDevices = DeviceMerger.merge(nrf_groups, [d.copy() for d in devices.values()])
+
+ def filename(ids):
+ p = {}
+ for k in ids.keys():
+ v = []
+ for b in ids.getAttribute(k):
+ if b == "": b = 'n'
+ v.append(b)
+ if k in ['type', 'pin']: v.sort()
+ if len(v) > 0:
+ p[k] = "_".join(v)
+ fmt = "{platform}{family}{series}"
+ return fmt.format(**p)
+
+ folder = os.path.join(os.path.dirname(__file__), '..', '..', 'devices', 'nrf')
+ parser = DeviceParser()
+ parsed_devices = {}
+ for dev in mergedDevices:
+ # dump the merged device file into the devices folder
+ path = DeviceFileWriter.write(dev, folder, filename)
+ # immediately parse this file
+ device_file = parser.parse(path)
+ for device in device_file.get_devices():
+ # and extract all the devices from it
+ parsed_devices[device.partname] = device
+
+ tmp_folder = os.path.join(os.path.dirname(__file__), 'single')
+ os.makedirs(tmp_folder, exist_ok=True)
+ for pname, pdevice in parsed_devices.items():
+ # these are the properties from the merged device
+ pprops = pdevice.properties
+ # dump the associated single device
+ rpath = DeviceFileWriter.write(devices[pname], tmp_folder, lambda ids: ids.string)
+ # parse it again
+ rdevice_file = parser.parse(rpath)
+ rdevice = rdevice_file.get_devices()
+ assert(len(rdevice) == 1)
+ # these are the properties of the single device
+ rprops = rdevice[0].properties
+ ddiff = DeepDiff(rprops, pprops, ignore_order=True)
+ # assert that there is no difference between the two
+ assert(len(ddiff) == 0)
diff --git a/tools/generator/raw-data-extractor/extract-nrf.py b/tools/generator/raw-data-extractor/extract-nrf.py
new file mode 100644
index 00000000..7a1c8df6
--- /dev/null
+++ b/tools/generator/raw-data-extractor/extract-nrf.py
@@ -0,0 +1,36 @@
+from pathlib import Path
+import urllib.request
+import zipfile
+import shutil
+import io
+import glob
+import os
+
+packurl = "https://www.nordicsemi.com/-/media/Software-and-other-downloads/Desktop-software/nRF-MDK/sw/8-33-0/nRF_MDK_8_33_0_GCC_BSDLicense.zip"
+
+shutil.rmtree("../raw-device-data/nrf-devices", ignore_errors=True)
+Path("../raw-device-data/nrf-devices/nrf").mkdir(exist_ok=True, parents=True)
+
+if __name__ == "__main__":
+ dest = "../raw-device-data/nrf-devices/nrf"
+ print("Downloading...")
+ with urllib.request.urlopen(packurl) as content:
+ z = zipfile.ZipFile(io.BytesIO(content.read()))
+ print("Extracting...")
+ # remove subfolders, some packs have several chips per pack
+ for zi in z.infolist():
+ if zi.filename.endswith(".svd") or zi.filename.endswith(".ld"):
+ zi.filename = os.path.basename(zi.filename)
+ print(zi.filename)
+ z.extract(zi, dest)
+
+ # dirty hack because af inconsistent part names in .svd files
+ os.rename(dest + '/nrf51.svd', dest + '/nrf51822.svd')
+ os.rename(dest + '/nrf52.svd', dest + '/nrf52832.svd')
+
+ for f in glob.glob(dest + '/nrf51_*.ld'):
+ os.remove(f)
+ for f in glob.glob(dest + '/nrf52_*.ld'):
+ os.remove(f)
+ for f in glob.glob(dest + '/nrf_common.ld'):
+ os.remove(f)