modm_data.owl.stmicro

 1# Copyright 2022, Niklas Hauser
 2# SPDX-License-Identifier: MPL-2.0
 3
 4from .device import owls, owl_devices, owl_device, load_owl_device
 5from .identifier import did_from_string
 6from .ontology import create_ontology
 7from .model import (
 8    owl_from_datasheet,
 9    owl_from_reference_manual,
10    owl_from_doc,
11    owl_from_did,
12    owl_from_cubemx,
13    owl_from_header,
14)
15
16__all__ = [
17    "owls",
18    "owl_devices",
19    "owl_device",
20    "load_owl_device",
21    "did_from_string",
22    "create_ontology",
23    "owl_from_datasheet",
24    "owl_from_reference_manual",
25    "owl_from_doc",
26    "owl_from_did",
27    "owl_from_cubemx",
28    "owl_from_header",
29]
def owls():
15def owls():
16    global _OWL_FILES
17    if _OWL_FILES is None:
18        _OWL_FILES = cache_path("stmicro-owl/").glob("*.owl")
19        _OWL_FILES = [ds.stem for ds in _OWL_FILES]
20    return _OWL_FILES
def owl_devices():
23def owl_devices():
24    global _OWL_MAPPING, _OWL_MAPPING_FILE
25    if _OWL_MAPPING is None:
26        if not _OWL_MAPPING_FILE.exists():
27            _OWL_MAPPING = {}
28            for ds in owls():
29                if ds.startswith("DS"):
30                    owl.store.load(ds)
31                    for name in set(i.name for i in owl.Device.instances()):
32                        _OWL_MAPPING[name] = ds
33
34            _OWL_MAPPING_FILE.parent.mkdir(parents=True, exist_ok=True)
35            with _OWL_MAPPING_FILE.open("w", encoding="utf-8") as fh:
36                json.dump(_OWL_MAPPING, fh, indent=4)
37        else:
38            with _OWL_MAPPING_FILE.open("r", encoding="utf-8") as fh:
39                _OWL_MAPPING = json.load(fh)
40    return _OWL_MAPPING
def owl_device(device):
43def owl_device(device):
44    return owl_devices().get(device.string)
def load_owl_device(device) -> bool:
47def load_owl_device(device) -> bool:
48    if (dev := owl_devices().get(device.string)) is not None:
49        owl.store.load(dev)
50        return True
51    return False
def did_from_string(string) -> modm_data.owl.DeviceIdentifier:
 8def did_from_string(string) -> DeviceIdentifier:
 9    string = string.lower()
10
11    if string.startswith("stm32"):
12        schema = "{platform}{family}{name}{pin}{size}{package}{temperature}{variant}"
13        if "@" in string:
14            schema += "@{core}"
15        i = DeviceIdentifier(schema)
16        i.set("platform", "stm32")
17        i.set("family", string[5:7])
18        i.set("name", string[7:9])
19        i.set("pin", string[9])
20        i.set("size", string[10])
21        i.set("package", string[11])
22        i.set("temperature", string[12])
23        if "@" in string:
24            string, core = string.split("@")
25            i.set("core", core)
26        if len(string) >= 14:
27            i.set("variant", string[13])
28        else:
29            i.set("variant", "")
30        return i
31
32    raise ValueError(f"Unknown identifier '{string}'!")
def create_ontology(did):
 20def create_ontology(did):
 21    store = Store("stmicro", did.string)
 22    ontology = store.ontology
 23
 24    # --------------------------- DEVICE IDENTIFIER ---------------------------
 25    class DeviceIdentifier(owl.Thing):
 26        namespace = ontology
 27        comment = "The unique identifier (part number) of the device."
 28
 29    class hasDeviceSchema(owl.DataProperty, owl.FunctionalProperty):
 30        namespace = ontology
 31        comment = "How to format the device identifier."
 32        domain = [DeviceIdentifier]
 33        range = [str]
 34
 35    class hasDevicePlatform(owl.DataProperty, owl.FunctionalProperty):
 36        namespace = ontology
 37        domain = [DeviceIdentifier]
 38        range = [str]
 39
 40    class hasDeviceFamily(owl.DataProperty, owl.FunctionalProperty):
 41        namespace = ontology
 42        domain = [DeviceIdentifier]
 43        range = [str]
 44
 45    class hasDeviceName(owl.DataProperty, owl.FunctionalProperty):
 46        namespace = ontology
 47        domain = [DeviceIdentifier]
 48        range = [str]
 49
 50    class hasDevicePin(owl.DataProperty, owl.FunctionalProperty):
 51        namespace = ontology
 52        domain = [DeviceIdentifier]
 53        range = [str]
 54
 55    class hasDeviceSize(owl.DataProperty, owl.FunctionalProperty):
 56        namespace = ontology
 57        domain = [DeviceIdentifier]
 58        range = [str]
 59
 60    class hasDevicePackage(owl.DataProperty, owl.FunctionalProperty):
 61        namespace = ontology
 62        domain = [DeviceIdentifier]
 63        range = [str]
 64
 65    class hasDeviceTemperature(owl.DataProperty, owl.FunctionalProperty):
 66        namespace = ontology
 67        domain = [DeviceIdentifier]
 68        range = [str]
 69
 70    class hasDeviceVariant(owl.DataProperty, owl.FunctionalProperty):
 71        namespace = ontology
 72        domain = [DeviceIdentifier]
 73        range = [str]
 74
 75    class hasDeviceCore(owl.DataProperty, owl.FunctionalProperty):
 76        namespace = ontology
 77        domain = [DeviceIdentifier]
 78        range = [str]
 79
 80    # ------------------------------- MEMORIES --------------------------------
 81    class Memory(owl.Thing):
 82        namespace = ontology
 83        comment = "Internal memory."
 84
 85    class hasMemoryStartAddress(owl.DataProperty, owl.FunctionalProperty):
 86        namespace = ontology
 87        domain = [Memory]
 88        range = [int]
 89
 90    class hasMemorySize(owl.DataProperty, owl.FunctionalProperty):
 91        namespace = ontology
 92        domain = [Memory]
 93        range = [int]
 94
 95    class hasMemoryAccess(owl.DataProperty, owl.FunctionalProperty):
 96        namespace = ontology
 97        domain = [Memory]
 98        range = [str]
 99
100    class hasMemory(DeviceIdentifier >> Memory):
101        namespace = ontology
102
103    # ------------------------------ INTERRUPTS -------------------------------
104    class InterruptVector(owl.Thing):
105        namespace = ontology
106        comment = "Interrupt vector in the table."
107
108    class hasInterruptVectorPosition(owl.DataProperty, owl.FunctionalProperty):
109        namespace = ontology
110        domain = [InterruptVector]
111        range = [int]
112
113    class hasInterruptVector(owl.ObjectProperty):
114        namespace = ontology
115        domain = [DeviceIdentifier]
116        range = [InterruptVector]
117
118    # --------------------------------- PINS ----------------------------------
119    class Package(owl.Thing):
120        namespace = ontology
121        comment = "A device package identifier"
122        domain = [DeviceIdentifier]
123
124    class hasPackagePinCount(owl.DataProperty, owl.FunctionalProperty):
125        namespace = ontology
126        domain = [Package]
127        range = [int]
128
129    class hasPackage(DeviceIdentifier >> Package):
130        namespace = ontology
131
132    class Pin(owl.Thing):
133        namespace = ontology
134        comment = "A pin on a package."
135
136    class hasPinType(owl.DataProperty, owl.FunctionalProperty):
137        namespace = ontology
138        domain = [Pin]
139        range = [str]
140
141    class hasPinNumber(owl.DataProperty, owl.FunctionalProperty):
142        namespace = ontology
143        domain = [Pin]
144        range = [int]
145
146    class hasPort(owl.DataProperty, owl.FunctionalProperty):
147        namespace = ontology
148        domain = [Pin]
149        range = [str]
150
151    class hasPin(owl.ObjectProperty):
152        namespace = ontology
153        domain = [Package]
154        range = [Pin]
155
156    class pinPosition(owl.AnnotationProperty):
157        namespace = ontology
158        comment = "The pin position attached to the [Package, hasPin, Pin] relation."
159
160    # -------------------------------- SIGNALS --------------------------------
161    class Signal(owl.Thing):
162        namespace = ontology
163        comment = "Connects a pin with a peripheral function."
164
165    class AlternateFunction(Signal):
166        namespace = ontology
167        comment = "Connects to a digital peripheral function via multiplexer."
168
169    class AdditionalFunction(Signal):
170        namespace = ontology
171        comment = "Connects to an analog/special peripheral function."
172
173    class hasSignal(owl.ObjectProperty):
174        namespace = ontology
175        domain = [Pin]
176        range = [Signal]
177
178    class alternateFunction(owl.AnnotationProperty):
179        namespace = ontology
180        comment = "The AF number attached to the [Pin, hasSignal, AlternateFunction] relation."
181
182    # ------------------------------ PERIPHERALS ------------------------------
183    class Peripheral(owl.Thing):
184        namespace = ontology
185        comment = "Internal peripheral."
186
187    class hasPeripheralInstance(owl.DataProperty, owl.FunctionalProperty):
188        namespace = ontology
189        domain = [Peripheral]
190        range = [int]
191
192    class hasPeripheralType(owl.DataProperty, owl.FunctionalProperty):
193        namespace = ontology
194        domain = [Peripheral]
195        range = [str]
196
197    class hasPeripheral(owl.ObjectProperty):
198        namespace = ontology
199        domain = [DeviceIdentifier, Signal]
200        range = [Peripheral]
201
202    # ----------------------------- FLASH LATENCY -----------------------------
203    class FlashWaitState(owl.Thing):
204        namespace = ontology
205        comment = "Flash Latency for minimum frequency."
206
207    class hasWaitState(owl.DataProperty, owl.FunctionalProperty):
208        namespace = ontology
209        domain = [FlashWaitState]
210        range = [int]
211
212    class hasMaxFrequency(owl.DataProperty, owl.FunctionalProperty):
213        namespace = ontology
214        domain = [FlashWaitState]
215        range = [int]
216
217    class hasMinOperatingVoltage(owl.DataProperty, owl.FunctionalProperty):
218        namespace = ontology
219        domain = [FlashWaitState]
220        range = [float]
221
222    class hasFlashWaitState(owl.ObjectProperty):
223        namespace = ontology
224        domain = [DeviceIdentifier]
225        range = [FlashWaitState]
226
227    # -------------------------------- GENERAL --------------------------------
228    class hasName(owl.DataProperty, owl.FunctionalProperty):
229        namespace = ontology
230        domain = [Memory, Signal, Peripheral, Package, Pin]
231        range = [str]
232
233    return KeyDict(locals())
def owl_from_datasheet(onto, ds):
 9def owl_from_datasheet(onto, ds):
10    pass
def owl_from_reference_manual(onto, rm):
13def owl_from_reference_manual(onto, rm):
14    odid = onto.DeviceIdentifier(onto.did.string.lower())
15
16    dfilter = find_device_filter(onto.did, rm.flash_latencies)
17    table_data = rm.flash_latencies[dfilter]
18    print(dfilter, table_data)
19    for min_voltage, frequencies in table_data.items():
20        # print(min_voltage, frequencies)
21        for wait_state, max_frequency in enumerate(frequencies):
22            ofreq = onto.FlashWaitState(f"WaitState_{wait_state}_{min_voltage}mV")
23            ofreq.hasWaitState = wait_state
24            ofreq.hasMaxFrequency = int(max_frequency * 1e6)
25            ofreq.hasMinOperatingVoltage = min_voltage / 1000.0
26            odid.hasFlashWaitState.append(ofreq)
def owl_from_doc(onto, doc):
29def owl_from_doc(onto, doc):
30    if doc.name.startswith("DS"):
31        owl_from_datasheet(onto, doc)
32    elif doc.name.startswith("RM"):
33        owl_from_reference_manual(onto, doc)
def owl_from_did(onto):
36def owl_from_did(onto):
37    # Add device identifiers
38    odid = onto.DeviceIdentifier(onto.did.string.lower())
39    odid.hasDeviceSchema = onto.did.naming_schema
40    odid.hasDevicePlatform = onto.did.platform
41    odid.hasDeviceFamily = onto.did.family
42    odid.hasDeviceName = onto.did.name
43    odid.hasDevicePin = onto.did.pin
44    odid.hasDeviceSize = onto.did.size
45    odid.hasDevicePackage = onto.did.package
46    odid.hasDeviceTemperature = onto.did.temperature
47    odid.hasDeviceVariant = onto.did.variant
48    if onto.did.get("core", False):
49        odid.hasDeviceCore = onto.did.core
def owl_from_cubemx(onto, data):
 52def owl_from_cubemx(onto, data):
 53    odid = onto.DeviceIdentifier(onto.did.string.lower())
 54
 55    # Add internal memories
 56    for memory in data["memories"]:
 57        omem = onto.Memory("Memory_" + memory["name"].upper())
 58        odid.hasMemory.append(omem)
 59        omem.hasName = memory["name"].upper()
 60        omem.hasMemoryStartAddress = int(memory["start"], 16)
 61        omem.hasMemorySize = int(memory["size"])
 62        omem.hasMemoryAccess = memory["access"]
 63
 64    # Add the peripherals and their type
 65    for pbase, name, version, ptype, features, stype in data["modules"]:
 66        oper = onto.Peripheral("Peripheral_" + name.upper())
 67        odid.hasPeripheral.append(oper)
 68        oper.hasName = name.upper()
 69        if pbase != name:
 70            oper.hasPeripheralInstance = int(name.replace(pbase, ""))
 71        oper.hasPeripheralType = pbase + ptype.replace("stm32", "")
 72
 73    # Add package
 74    opack = onto.Package("Package_" + data["package"])
 75    opack.hasName = str(data["package"])
 76    odid.hasPackage.append(opack)
 77    opack.hasPackagePinCount = int(data["pin-count"])
 78
 79    # Add pinout for package
 80    io_pins = {}
 81    for pin in data["pinout"]:
 82        opin = onto.Pin("Pin_" + pin["name"])
 83        opin.hasName = pin["name"]
 84        opin.hasPinType = pin["type"]
 85        if pin["type"] == "I/O" and (number := re.search(r"P\w(\d+)", pin["name"])):
 86            opin.hasPort = pin["name"][1]
 87            opin.hasPinNumber = int(number.group(1))
 88            io_pins[(opin.hasPort.lower(), opin.hasPinNumber)] = opin
 89        opack.hasPin.append(opin)
 90        onto.pinPosition[opack, onto.hasPin, opin].append(pin["position"])
 91
 92    # Add alternate and additional functions to pins
 93    for port, number, signals in data["gpios"]:
 94        opin = io_pins[(port, int(number))]
 95        for signal in signals:
 96            peripheral = (signal["driver"] or "").upper() + (signal["instance"] or "")
 97            name = signal["name"].upper()
 98            af = signal["af"]
 99            signame = "Signal_" + peripheral + "_" + name
100            osig = onto.AlternateFunction(signame) if af else onto.AdditionalFunction(signame)
101            osig.hasPeripheral.append(onto.Peripheral("Peripheral_" + peripheral))
102            osig.hasName = name
103            opin.hasSignal.append(osig)
104            if af:
105                onto.alternateFunction[opin, onto.hasSignal, osig].append(int(af))
def owl_from_header(onto, header):
108def owl_from_header(onto, header):
109    odid = onto.DeviceIdentifier(onto.did.string.lower())
110
111    # Add interrupt vector table
112    for interrupt in header.interrupt_table:
113        if interrupt["position"] >= 0:
114            oint = onto.InterruptVector("InterruptVector_" + interrupt["name"])
115            oint.hasInterruptVectorPosition = interrupt["position"]
116            odid.hasInterruptVector.append(oint)