Skip to content

Commit a7f5782

Browse files
committed
[api] Move GPIO code from modm to here
1 parent 068b715 commit a7f5782

File tree

9 files changed

+365
-40
lines changed

9 files changed

+365
-40
lines changed

modm_devices/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@
1010
from . import device_identifier
1111
from . import device
1212
from . import parser
13+
from . import stm32
1314

1415
from .pkg import naturalkey
1516
from .exception import ParserException
1617

17-
__all__ = ['exception', 'device_file', 'device_identifier', 'device', 'parser', 'pkg']
18+
__all__ = [
19+
'exception',
20+
'device_file',
21+
'device_identifier',
22+
'device',
23+
'parser',
24+
'pkg'
25+
]
1826

1927
__version__ = "0.2.8"

modm_devices/cache.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
4+
import functools
5+
6+
class cached_property(object):
7+
def __init__(self, func):
8+
self.__doc__ = getattr(func, "__doc__")
9+
self.func = func
10+
11+
def __get__(self, obj, cls):
12+
if obj is None:
13+
return self
14+
value = obj.__dict__[self.func.__name__] = self.func(obj)
15+
return value
16+
17+
cached_function = functools.lru_cache(None)

modm_devices/device.py

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -10,63 +10,68 @@
1010

1111
from .exception import ParserException
1212
from .device_identifier import DeviceIdentifier
13-
13+
from .driver import Driver
14+
from .cache import *
15+
import fnmatch
1416

1517
class Device:
1618
def __init__(self,
1719
identifier: DeviceIdentifier,
1820
device_file):
1921
self._identifier = identifier.copy()
20-
self.naming_schema = identifier.naming_schema
2122
self.partname = identifier.string
22-
self.device_file = device_file
23-
23+
self._device_file = device_file
2424
self.__properties = None
2525

2626
@property
2727
def _properties(self):
2828
if self.__properties is None:
29-
self.__properties = self.device_file.get_properties(self._identifier)
29+
self.__properties = self._device_file.get_properties(self._identifier)
3030
return self.__properties
3131

32-
@property
33-
def identifier(self):
34-
return self._identifier
35-
36-
def get_all_drivers(self, name):
37-
parts = name.split(":")
32+
def _find_drivers(self, *patterns):
3833
results = []
34+
for pattern in patterns:
35+
parts = pattern.split(":")
3936

40-
if len(parts) == 1:
41-
results = [d for d in self._properties["driver"] if d["name"] == parts[0]]
42-
elif len(parts) == 2:
43-
find_all = (parts[1][-1] == '*')
44-
for driver in self._properties["driver"]:
45-
if driver["name"] == parts[0] and \
46-
((find_all and driver["type"].startswith(parts[1][:-1])) or
47-
(not find_all and driver["type"] == parts[1])):
48-
results.append(driver)
49-
else:
50-
raise ParserException("Invalid driver name '{}'. "
51-
"The name must contain no or one ':' to "
52-
"separate type and name.".format(name))
37+
if len(parts) == 1:
38+
results.extend(d for d in self._properties["driver"]
39+
if fnmatch.fnmatch(d["name"], parts[0]))
40+
elif len(parts) == 2:
41+
results.extend(d for d in self._properties["driver"]
42+
if (fnmatch.fnmatch(d["name"], parts[0]) and
43+
fnmatch.fnmatch(d["type"], parts[1])))
44+
else:
45+
raise ParserException("Invalid driver pattern '{}'. "
46+
"The name must contain no or one ':' to "
47+
"separate `name:type` pattern.".format(parts))
5348

5449
return results
5550

56-
def get_driver(self, name):
57-
results = self.get_all_drivers(name)
51+
def _find_first_driver(self, *patterns):
52+
results = self._find_drivers(*patterns)
5853
return results[0] if len(results) else None
5954

60-
def has_driver(self, name, type: list = []):
61-
if len(type) == 0:
62-
return self.get_driver(name) is not None
55+
@property
56+
def did(self):
57+
return self._identifier
58+
59+
def driver(self, name):
60+
return Driver(self, self._find_first_driver(name))
61+
62+
def drivers(self, *names):
63+
return [Driver(self, d) for d in self._find_drivers(*names)]
6364

64-
if ':' in name:
65-
raise ParserException("Invalid driver name '{}'. "
66-
"The name must contain no ':' when using the "
67-
"compatible argument.".format(name))
65+
def has_driver(self, *names):
66+
return len(self._find_drivers(*names))
6867

69-
return any(self.get_driver(name + ':' + c) is not None for c in type)
68+
# Deprecated stuff
69+
def get_driver(self, pattern):
70+
return self._find_first_driver(pattern)
7071

71-
def __str__(self):
72-
return self.partname
72+
def get_all_drivers(self, *patterns):
73+
return self._find_drivers(*patterns)
74+
75+
@property
76+
def identifier(self):
77+
return self.did

modm_devices/device_file.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from collections import defaultdict
1212

1313
from .device import Device
14+
from .stm32.device import Stm32Device
1415
from .device_identifier import DeviceIdentifier
1516
from .device_identifier import MultiDeviceIdentifier
1617
from .access import read_only
@@ -47,10 +48,14 @@ def get_devices(self):
4748
valid_devices = [node.text for node in device_node.iterfind(self._VALID_DEVICE)]
4849
devices = identifiers
4950
if len(invalid_devices):
50-
devices = [did for did in devices if did.string not in invalid_devices]
51+
devices = (did for did in devices if did.string not in invalid_devices)
5152
if len(valid_devices):
52-
devices = [did for did in devices if did.string in valid_devices]
53-
return [Device(did, self) for did in devices]
53+
devices = (did for did in devices if did.string in valid_devices)
54+
def build_device(did, device_file):
55+
if did.platform == "stm32":
56+
return Stm32Device(did, device_file)
57+
return Device(did, device_file)
58+
return [build_device(did, self) for did in devices]
5459

5560
@staticmethod
5661
def is_valid(node, identifier: DeviceIdentifier):

modm_devices/driver.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2020, Niklas Hauser
5+
# All rights reserved.
6+
7+
from collections import defaultdict
8+
from .cache import *
9+
10+
class Instance:
11+
def __init__(self, driver, instance):
12+
self._instance = instance
13+
self.driver = driver
14+
self.name = self._instance["name"]
15+
self.number = int(self.name) if self.name.isdigit() else self.name
16+
17+
def features(self, default=[]):
18+
feats = self.driver.features()
19+
feats.extend(self._instance.get("feature", []))
20+
return feats if len(feats) else default
21+
22+
def __str__(self):
23+
return self.name
24+
25+
26+
class Driver:
27+
def __init__(self, device, driver):
28+
self._driver = driver
29+
self.device = device
30+
self.name = self._driver["name"]
31+
self.type = self._driver["type"]
32+
33+
def instances(self, default=[]):
34+
if "instance" in self._driver:
35+
return [Instance(self, i) for i in self._driver["instance"]]
36+
return default
37+
38+
def features(self, default=[]):
39+
if "feature" in self._driver:
40+
return list(self._driver["feature"])
41+
return default
42+
43+
def __str__(self):
44+
return self.name

modm_devices/stm32/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import device

modm_devices/stm32/core.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2020, Niklas Hauser
5+
# All rights reserved.
6+
7+
from ..cache import cached_property
8+
from ..driver import Driver
9+
from collections import defaultdict
10+
11+
class DriverCore(Driver):
12+
def __init__(self, device):
13+
Driver.__init__(self, device, device._find_first_driver("core"))
14+
15+
16+
def vectors(self, filterfn=None):
17+
vecs = (v["name"] for v in self._driver["vector"])
18+
if filterfn is not None:
19+
vecs = filter(filterfn, vecs)
20+
return vecs
21+
22+
23+
def instance_irq_map(self, name):
24+
"""
25+
:return: a map from int(instance) to str(name) interrupt starting with {name}.
26+
"""
27+
vector_map = defaultdict(list)
28+
for vector in self.vectors(lambda v: v.startswith(name)):
29+
vrange = sorted(int(d) for d in vector[len(name):].split("_") if d.isdigit())
30+
if len(vrange) == 2:
31+
vrange = list(range(vrange[0], vrange[1]+1))
32+
for num in vrange:
33+
# if num in vector_map:
34+
# raise ValueError("Instance '{}' already in '{}' map!".format(str(num), name))
35+
vector_map[num].append(vector)
36+
return vector_map
37+
38+
39+
def shared_irqs(self, name):
40+
"""
41+
:return: a map from str(name) to range(instances) >= 2 for interrupts starting with {name}.
42+
"""
43+
vector_range = {}
44+
for vector in self.vectors(lambda v: v.startswith(name)):
45+
vrange = sorted(int(d) for d in vector[len(name):].split("_") if d.isdigit())
46+
if len(vrange) <= 1:
47+
continue;
48+
if len(vrange) == 2:
49+
vrange = list(range(vrange[0], vrange[1]+1))
50+
if vector in vector_range:
51+
raise ValueError("Vector '{}' already in '{}' map!".format(str(vector), name))
52+
vector_range[vector] = vrange
53+
return vector_range
54+

modm_devices/stm32/device.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2016, Fabian Greif
5+
# Copyright (c) 2016, Niklas Hauser
6+
# All rights reserved.
7+
8+
from .gpio import DriverGpio
9+
from .core import DriverCore
10+
from ..device import Device
11+
from ..cache import cached_property
12+
from ..access import copy_keys
13+
14+
15+
class Stm32Device(Device):
16+
def __init__(self, identifier, device_file):
17+
Device.__init__(self, identifier, device_file)
18+
19+
@cached_property
20+
def gpio(self):
21+
return DriverGpio(self)
22+
23+
@cached_property
24+
def core(self):
25+
return DriverCore(self)
26+
27+
@cached_property
28+
def peripherals(self):
29+
all_peripherals = []
30+
for s in self.gpio.signals_all:
31+
d = copy_keys(s, "driver", "instance")
32+
if len(d): all_peripherals.append(d);
33+
34+
# Signals are not enough, since there are peripherals that don't have signals.
35+
# Example: STM32F401RE < 64pins: SPI4 cannot be connected to any pins.
36+
for d in self._properties["driver"]:
37+
driver = d["name"]
38+
if driver in ["gpio", "core"]:
39+
continue
40+
elif "instance" in d:
41+
all_peripherals.extend( {"driver": driver, "instance": int(i["name"])} for i in d["instance"] )
42+
else:
43+
all_peripherals.append( {"driver": driver} )
44+
45+
for r in self.gpio._driver.get("remap", {}):
46+
d = copy_keys(r, "driver", "instance")
47+
if len(d): all_peripherals.append(d);
48+
49+
return all_peripherals

0 commit comments

Comments
 (0)