modm_data.html2svd.stmicro.reference

  1# Copyright 2022, Niklas Hauser
  2# SPDX-License-Identifier: MPL-2.0
  3
  4import re
  5from functools import cached_property
  6from collections import defaultdict
  7from anytree import RenderTree
  8
  9from ...html.stmicro.helper import split_device_filter
 10from ...svd import *
 11from ...header2svd.stmicro.tree import _normalize_order
 12from ...cubemx import cubemx_device_list
 13from ...html import replace as html_replace
 14
 15
 16def _deduplicate_bit_fields(bit_fields):
 17    named_fields = defaultdict(set)
 18    for field in sorted(bit_fields, key=lambda f: f.position):
 19        named_fields[field.name].add(field.position)
 20
 21    new_fields = []
 22    for name, positions in named_fields.items():
 23        position = min(positions)
 24        width = max(positions) + 1 - position
 25        new_fields.append(BitField(name, position, width))
 26
 27    return new_fields
 28
 29
 30def _peripheral_map_to_tree(chapter, peripheral_maps):
 31    cap_replace = {"STM32F415/417xx": "STM32F415/417"}
 32
 33    peripheral_trees = []
 34    for caption, (heading, register_map) in peripheral_maps.items():
 35        print(caption)
 36        if match := re.search(f"OTG_[FH]S", caption):
 37            replace_name = peripheral_name = "OTG"
 38        elif match := re.search(f"JPEG", caption):
 39            replace_name = peripheral_name = "JPEG"
 40        elif match := re.search(f"CCU ", caption):
 41            peripheral_name = "CANCCU"
 42            replace_name = "FDCAN_CCU"
 43        else:
 44            peripheral_names = {n.split("_")[0] for n in register_map.keys()}
 45            replace_name = peripheral_name = list(sorted(peripheral_names))[-1]
 46            if all(p.startswith("COMP") for p in peripheral_names):
 47                peripheral_name = "COMP"
 48                replace_name = ""
 49            if all(p.startswith("OPAMP") for p in peripheral_names):
 50                peripheral_name = "OPAMP"
 51                replace_name = ""
 52            elif len(peripheral_names) > 1:
 53                print(f"Multiple peripheral names detected: {peripheral_names}")
 54
 55        if peripheral_name == "M7": continue
 56        # Some chapters have multiple tables for multiple instances
 57        filters = defaultdict(set)
 58        instances = set()
 59        if peripheral_name.startswith("LPTIM"):
 60            replace_name = peripheral_name = "LPTIM"
 61        elif peripheral_name.startswith("DLYB"):
 62            instances.add("DLYB")
 63        elif peripheral_name.startswith("TIM"):
 64            peripheral_name = "TIM"
 65            if match := re.search(r"TIM(\d+) +to +TIM(\d+)", caption):
 66                irange = list(sorted([int(match.group(1)), int(match.group(2))]))
 67                irange = range(irange[0], irange[1] + 1)
 68                instances.add(f"TIM({'|'.join(map(str, irange))})")
 69            for pfilter in re.findall(r"TIM\d+(?:/\d+)*", caption):
 70                if "/" in pfilter:
 71                    pfilter = f"TIM({pfilter[3:].replace('/', '|')})"
 72                instances.add(f"^{pfilter}$")
 73        elif "GPIOx" in peripheral_name:
 74            peripheral_name = "GPIO"
 75            for pfilter in re.findall(r"GPIO[A-Z](?:[/A-Z]+]+)?", caption):
 76                if "/" in pfilter:
 77                    pfilter = f"GPIO({pfilter[4:].replace('/', '|')})"
 78                instances.add(pfilter)
 79        if instances:
 80            filters["instances"].update(instances)
 81
 82        devices = set()
 83        for pfilter in re.findall(r"STM32[\w/]+", html_replace(caption, **cap_replace)):
 84            devices.update(split_device_filter(pfilter) if "/" in pfilter else [pfilter])
 85        if devices:
 86            filters["devices"].update(d.replace("x", ".") for d in devices)
 87
 88        if "connectivity line" in chapter.name:
 89            filters["devices"].add("STM32F10[57]")
 90        elif "low medium high and xl density" in chapter.name:
 91            filters["devices"].add("STM32F10[123]")
 92
 93        peripheral_type = PeripheralType(peripheral_name, _chapter=chapter,
 94                                         filters=dict(filters), section=heading)
 95        for rname, (offset, bitfields) in register_map.items():
 96            filters = {}
 97            if replace_name:
 98                if replace_name == "OTG" and (match := re.match("^OTG_[FH]S", rname)):
 99                    filters["instances"] = {match.group(0)}
100                    nrname = rname.replace(match.group(0) + "_", "")
101                else:
102                    nrname = rname.replace(replace_name + "_", "")
103                if len(rname) == len(nrname) and "_" in rname:
104                    instance = rname.split("_")[0]
105                    filters["instances"] = {instance+"$"}
106                    nrname = rname.replace(instance + "_", "")
107                    print(instance, nrname)
108                rname = nrname
109            if match := re.match("(.*?)connectivitylinedevices", rname):
110                rname = match.group(1)
111                filters["devices"] = {r"STM32F10[57]"}
112            elif match := re.match("(.*?)low,medium,highandXLdensitydevices", rname):
113                rname = match.group(1)
114                filters["devices"] = {r"STM32F10[123]"}
115            try: offset = int(offset, 16)
116            except: pass
117            register_type = Register(rname, offset, filters=filters, parent=peripheral_type)
118            fields = [BitField(field, bit) for bit, field in bitfields.items()]
119            register_type.children = _deduplicate_bit_fields(fields)
120
121        peripheral_trees.append(peripheral_type)
122
123    return peripheral_trees
124
125
126def _expand_register_offsets(peripheral_trees):
127    for peripheral in peripheral_trees:
128        unexpanded = defaultdict(list)
129        for register in peripheral.children:
130            if (isinstance(register.offset, str) or
131                ("CAN" in peripheral.name and "F1R2" in register.name) or
132                ("GFXMMU" in peripheral.name and "LUT0L" in register.name) or
133                ("GFXMMU" in peripheral.name and "LUT0H" in register.name) or
134                ("HSEM" in peripheral.name and "R1" in register.name)):
135                unexpanded[str(register.offset)].append(register)
136        for offsets, registers in unexpanded.items():
137            print(offsets, registers)
138
139            conv = lambda i: int(i, 16)
140            # if match := re.search(r"x=([\d,]+)", registers[0].name):
141            #     offsets = [offsets] * len(match.group(1).split(","))
142            if any(pat in offsets for pat in ["x=", "channelnumber"]):
143                if matches := re.findall(r"(0x[\dA-Fa-f]+)\(x=\w+\)", offsets):
144                    orange = enumerate(map(conv, matches))
145                    formula = "x"
146                elif "channelnumber" in offsets:
147                    orange = enumerate(range(0, 16))
148                    formula = offsets.replace("channelnumber", "x")
149                elif "moni-ringunitnumber" in offsets:
150                    orange = [(i, i) for i in range(1, 6)]
151                    formula = offsets.split("(x=")[0]
152                else:
153                    match = re.search(r"\(x=(\d+)(?:-\.?|\.\.)(\d+)", offsets)
154                    orange = [(i, i) for i in range(int(match.group(1)), int(match.group(2)) + 1)]
155                    formula = re.split(r"\(x=|,", offsets)[0]
156                offsets = [(ii, eval(formula, None, {"x": x})) for ii, x in orange]
157                print(formula, offsets, orange)
158            elif "-" in offsets:
159                omin, omax = list(map(conv, offsets.split("-")))
160                offsets = enumerate(range(omin, omax+1, 4))
161            elif "or" in offsets:
162                offsets = enumerate(list(map(conv, offsets.split("or"))))
163            elif "F1R2" in registers[0].name:
164                offsets = enumerate(range(int(offsets), int(offsets)+4*25*2+1, 4))
165            elif "LUT0" in registers[0].name:
166                offsets = enumerate(range(int(offsets), int(offsets)+4*2044+1, 8))
167            elif "HSEM" in peripheral.name:
168                print(offsets)
169                offsets = enumerate(range(int(offsets), int(offsets)+4*29+1, 4))
170            else:
171                print(f"Unknown expansion format for {offsets}!")
172                return False
173
174            fields = registers[0].children
175            if all(re.match(r"BKP\d+R", r.name) for r in registers):
176                name_template = lambda i: f"BKP{i}R"
177            elif "SAI" in peripheral.name:
178                name_template = lambda i: f"{registers[0].name[1:]}{chr(i+ord('A'))}"
179            elif "HRTIM" in peripheral.name:
180                name_template = lambda i: registers[0].name.replace("x", chr(i+ord('A')))
181            elif "CAN" in peripheral.name:
182                name_template = lambda i: f"F{(i+3)//2}R{(i+1)%2+1}"
183            elif "GFXMMU" in peripheral.name:
184                name_template = lambda i: f"LUT{i}{registers[0].name[-1]}"
185            elif "HSEM" in peripheral.name:
186                name_template = lambda i: f"{registers[0].name[:-1]}{i+1}"
187            elif len(registers) == 1:
188                # if "x=" in registers[0].name:
189                #     name_template = lambda i: f"{registers[0].name.split('x=')[0]}.{i}"
190                if "x" in registers[0].name:
191                    name_template = lambda i: registers[0].name.replace("x", str(i))
192                else:
193                    name_template = lambda i: f"{registers[0].name}.{i}"
194            else:
195                print(f"Unknown expansion pattern for {registers}!")
196                return False
197
198            for ii, offset in offsets:
199                nreg = Register(name_template(ii), offset, filters=registers[0].filters, parent=peripheral)
200                nreg.children = [BitField(f.name, f.position, f.width) for f in fields]
201            for register in registers:
202                register.parent = None
203
204    return True
205
206
207def _link_instance_to_type(rm, peripheral_types, instance_offsets):
208    cap_replace = {}
209    peripherals = set()
210    for caption, locations in rm.peripherals.items():
211        filters = defaultdict(set)
212        devices = set()
213        for pfilter in re.findall(r"STM32[\w/]+", html_replace(caption, **cap_replace)):
214            devices.update(split_device_filter(pfilter) if "/" in pfilter else [pfilter])
215        if "Low and medium-density device" in caption:
216            devices.add("STM32F10..[468B]")
217        elif "High-density device" in caption:
218            devices.add("STM32F10..[CDE]")
219        if devices:
220            filters["devices"].update(d.replace("x", ".") for d in devices)
221
222        for (names, amin, amax, bus, sections) in locations:
223            for name in names:
224                ptypes = [t for tname, types in peripheral_types.items() for t in types if tname == name]
225                if not ptypes:
226                    ptypes = [t for tname, types in peripheral_types.items() for t in types if tname in name]
227                if not ptypes:
228                    ptypes = [t for tname, types in peripheral_types.items()
229                              for t in types if t.section in sections]
230                if not ptypes and name.startswith("UART"):
231                    ptypes = [t for tname, types in peripheral_types.items() for t in types if tname == "USART"]
232                if not ptypes and "BKP" == name:
233                    ptypes = [t for tname, types in peripheral_types.items() for t in types if tname == "RTC"]
234                if not ptypes:
235                    print(f"Cannot find peripheral type for instance {name} in section {sections}!")
236                    nsections = list(sorted({t.section for types in peripheral_types.values() for t in types}))
237                    print(f"Available sections are {nsections}.")
238                    exit(1)
239                offsets = [v for k, v in instance_offsets.items() if re.search(k, name)]
240                if offsets: amin += offsets[0]
241                p = Peripheral(name, ptypes, amin, filters=dict(filters), sections=sections)
242                peripherals.add(p)
243    return peripherals
244
245
246def _resolve_filters(filters, **kw):
247    keys = []
248    for key, value in kw.items():
249        if values := filters.get(key):
250            keys.append(key)
251            if any(re.search(pat, value, flags=re.IGNORECASE) for pat in values):
252                return True
253    return not keys
254
255
256def _normalize_instances(memtree, peripherals, device):
257    for peripheral in peripherals:
258        if not _resolve_filters(peripheral.filters, devices=device.string):
259            continue
260        ptypes = peripheral.type
261        if len(ptypes) > 1:
262            ptypes = [ptype for ptype in sorted(peripheral.type, key=lambda p: -len(p.filters))
263                      if _resolve_filters(ptype.filters, instances=peripheral.name, devices=device.string)]
264            if len(ptypes) > 1 and any(p.filters for p in ptypes):
265                ptypes = [p for p in ptypes if p.filters]
266            if len(ptypes) > 1:
267                nptypes = [p for p in ptypes if any(p.section.startswith(per) or per.startswith(p.section)
268                                                    for per in peripheral.sections)]
269                if nptypes: ptypes = nptypes
270            for pname in ["DMAMUX", "BDMA", "OCTOSPI"]:
271                if len(ptypes) > 1 and pname in peripheral.name:
272                    ptypes = [p for p in ptypes if pname in p.name]
273
274        if len(ptypes) != 1:
275            print(f"Unknown peripheral type {device} {peripheral} {ptypes}!")
276            continue
277        ptype = ptypes[0]
278
279        nper = Peripheral(peripheral.name, ptype, peripheral.address,
280                          filters=peripheral.filters, parent=memtree)
281        rmap = defaultdict(list)
282        for treg in ptype.children:
283            rmap[treg.name].append(treg)
284
285        for name, tregs in rmap.items():
286            regs = [reg for reg in sorted(tregs, key=lambda p: -len(p.filters))
287                    if _resolve_filters(reg.filters, instances=peripheral.name, devices=device.string)]
288            if len(regs) > 1 and any(r.filters for r in regs):
289                regs = [r for r in regs if r.filters]
290            if len(regs) != 1:
291                if len(regs) > 1:
292                    print(f"Unsuccessful register filtering {peripheral.name} {device}: {tregs}!")
293                continue
294            treg = regs[0]
295            if _resolve_filters(treg.filters, devices=device.string, instances=nper.name):
296                preg = Register(treg.name, offset=treg.offset, width=treg.width,
297                                filters=treg.filters, parent=nper)
298                for tbit in treg.children:
299                    BitField(tbit.name, tbit.position, tbit.width, parent=preg)
300
301
302def _build_device_trees(rm, peripheral_types, instance_offsets):
303    devices = rm.filter_devices(modm_device_list())
304    memtrees = []
305
306    for device in devices:
307        memtree = Device(device)
308        peripherals = _link_instance_to_type(rm, peripheral_types, instance_offsets)
309        _normalize_instances(memtree, peripherals, device)
310        memtrees.append(memtree)
311    return memtrees
312
313
314def _compactify_device_trees(memtrees):
315    memtree_hashes = defaultdict(list)
316    for memtree in memtrees:
317        memtree_hashes[hash(memtree)].append(memtree)
318
319    new_memtrees = []
320    for memtrees in memtree_hashes.values():
321        memtree = memtrees[0]
322        for mtree in memtrees[1:]:
323            memtree.compatible.extend(mtree.compatible)
324        memtree.compatible.sort(key=lambda d: d.string)
325        memtree.name = memtree.compatible[0]
326        new_memtrees.append(memtree)
327
328    return new_memtrees
329
330
331def memory_map_from_reference_manual(rm):
332    if "RM0438" in rm.name or "RM0456" in rm.name:
333        print("RM0438, RM0456 are ARMv8-M with two memory maps!")
334        return []
335
336    all_chapters = rm.chapters()
337    type_chapters = {rm.chapter(f"chapter {s.split('.')[0]} ") for pers in rm.peripherals.values()
338                     for locs in pers for s in locs[4]}
339    peripheral_types = defaultdict(set)
340    instance_offsets = {}
341    for chapter in all_chapters:
342        print()
343        peripheral_maps, peripheral_offsets = rm.peripheral_maps(chapter, assert_table=chapter in type_chapters)
344        instance_offsets.update(peripheral_offsets)
345        peripheral_maps = _peripheral_map_to_tree(chapter, peripheral_maps)
346        if not _expand_register_offsets(peripheral_maps):
347            exit(1)
348        for pmap in peripheral_maps:
349            print(pmap)
350            # print(RenderTree(pmap, maxlevel=2))
351            peripheral_types[pmap.name].add(pmap)
352
353    for name, pmaps in peripheral_types.items():
354        print(name)
355        for pmap in pmaps:
356            print(pmap.section, pmap._chapter._relpath)
357            print(RenderTree(pmap, maxlevel=2))
358
359
360    memtrees = _build_device_trees(rm, peripheral_types, instance_offsets)
361    # for tree in memtrees:
362    #     print(RenderTree(tree, maxlevel=2))
363    #     exit(1)
364    memtrees = _compactify_device_trees(memtrees)
365    memtrees = [_normalize_order(memtree) for memtree in memtrees]
366    return memtrees
def memory_map_from_reference_manual(rm):
332def memory_map_from_reference_manual(rm):
333    if "RM0438" in rm.name or "RM0456" in rm.name:
334        print("RM0438, RM0456 are ARMv8-M with two memory maps!")
335        return []
336
337    all_chapters = rm.chapters()
338    type_chapters = {rm.chapter(f"chapter {s.split('.')[0]} ") for pers in rm.peripherals.values()
339                     for locs in pers for s in locs[4]}
340    peripheral_types = defaultdict(set)
341    instance_offsets = {}
342    for chapter in all_chapters:
343        print()
344        peripheral_maps, peripheral_offsets = rm.peripheral_maps(chapter, assert_table=chapter in type_chapters)
345        instance_offsets.update(peripheral_offsets)
346        peripheral_maps = _peripheral_map_to_tree(chapter, peripheral_maps)
347        if not _expand_register_offsets(peripheral_maps):
348            exit(1)
349        for pmap in peripheral_maps:
350            print(pmap)
351            # print(RenderTree(pmap, maxlevel=2))
352            peripheral_types[pmap.name].add(pmap)
353
354    for name, pmaps in peripheral_types.items():
355        print(name)
356        for pmap in pmaps:
357            print(pmap.section, pmap._chapter._relpath)
358            print(RenderTree(pmap, maxlevel=2))
359
360
361    memtrees = _build_device_trees(rm, peripheral_types, instance_offsets)
362    # for tree in memtrees:
363    #     print(RenderTree(tree, maxlevel=2))
364    #     exit(1)
365    memtrees = _compactify_device_trees(memtrees)
366    memtrees = [_normalize_order(memtree) for memtree in memtrees]
367    return memtrees