Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add clock trees and query interface #17

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions tools/generator/dfg/input/xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ def _openDeviceXML(self, filename):
return xmltree

def queryTree(self, query):
"""
This tries to apply the query to the device tree and returns either
- an array of element nodes,
- an array of strings or
- None, if the query failed.
"""
response = None
try:
response = self.tree.xpath(query)
Expand Down
127 changes: 127 additions & 0 deletions tools/generator/dfg/stm32/stm_clock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Niklas Hauser
# All rights reserved.

from os.path import commonprefix, basename
import re
import sys
import logging
import subprocess
import tempfile
import textwrap

from collections import defaultdict
from jinja2 import Environment
from pathlib import Path
from ..input.xml import XMLReader

from . import stm

LOGGER = logging.getLogger('dfg.stm32.clock')



class STMClock:
ROOT_PATH = Path(__file__).parents[2]
CLOCK_FILE_PATH = ROOT_PATH / "raw-device-data/stm32-devices/plugins/clock"
CACHE_CLOCK_TREE = {}

@staticmethod
def get(did, rcc_ip_file):
if rcc_ip_file.filename not in STMClock.CACHE_CLOCK_TREE:
STMClock.CACHE_CLOCK_TREE[rcc_ip_file.filename] = STMClock(did, rcc_ip_file)
return STMClock.CACHE_CLOCK_TREE[rcc_ip_file.filename]

def __init__(self, did, rcc_ip_file):
self.ip_file = rcc_ip_file
self.did = did

match = basename(self.ip_file.filename)
match = re.search(r"RCC-STM32(((..).?.?)E?)[_-]rcc", match)
ip_name = match.group(1)
rcc_name = match.group(2)
family = match.group(3)
# print(family, rcc_name, ip_name)
files = [c for c in STMClock.CLOCK_FILE_PATH.glob("*.xml") if str(c).endswith("{}.xml".format(rcc_name))]
if not files:
files = [c for c in STMClock.CLOCK_FILE_PATH.glob("*.xml") if rcc_name in str(c)]
if not files:
files = [c for c in STMClock.CLOCK_FILE_PATH.glob("*.xml") if str(c).endswith("{}.xml".format(family))]
if len(files) != 1:
LOGGER.error("Unknown clock file for device '{}' and IP file '{}': {}".format(did.string, self.ip_file, files))
return
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self: the ClockFile is encoded in the MCU XML at the very top: <Mcu ClockTree="STM32F0"

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Mcu ClockTree="STM32F0" DBVersion="V3.0" Family="STM32F0" HasPowerPad="false" IOType="" Line="STM32F0x0 Value Line" Package="LQFP48" RefName="STM32F030C8Tx" xmlns="http://mcd.rou.st.com/modules.php?name=mcu">
	<Core>Arm Cortex-M0</Core>
	<Frequency>48</Frequency>


self.clock_file = XMLReader(files[0])
# print(family, files[0])

self.params = {ref.get("Name"):ref for ref in self.ip_file.query("//RefParameter")}
self.nodes = {node.get("id"):node for node in self.clock_file.query("//Element")}
self.signals = {signal.get("id"):signal for signal in self.clock_file.query("//Signal")}
self.ips = {}
self.edges = {}
# ips = {ip.strip():ipn for ipn in self.ip_file.query("//@IP/..") for ip in ipn.get("IP").split(",")}
# signals = self.clock_file.query("//@signalId")

for edge in self.clock_file.query("//Input"):
name = "{}<-{}".format(edge.get("signalId"), edge.get("from"))
self.edges[name] = edge
for edge in self.clock_file.query("//Output"):
name = "{}->{}".format(edge.get("signalId"), edge.get("to"))
self.edges[name] = edge

def get_refs(node):
ref = self.params.get(node.get("refParameter", ""))
if ref is None: return {};
attrib = ref.attrib
if ref.get("Type", "") == "list":
values = [value.get("Value") for value in ref if value.get("Value") is not None]
if values:
prefix = commonprefix(values)
attrib["Values"] = "{}{{{}}}".format(prefix, ",".join(value.replace(prefix, "") for value in values))
attrib.pop("Visible", None)
attrib.pop("Display", None)
attrib.pop("Comment", None)
attrib.pop("Unit", None)
attrib.pop("Type", None)
nmin, ndef, nmax = attrib.pop("Min", ""), attrib.pop("DefaultValue", ""), attrib.pop("Max", "")
if nmin and nmax and nmin == nmax:
value = ndef
else:
value = ""
if nmin: value += "{} <= ".format(nmin);
if ndef: value += "({})".format(ndef);
if nmax: value += " <= {}".format(nmax);
attrib["Value"] = value

return attrib


import graphviz as gv
graph = gv.Digraph(format="svg",
node_attr={"style": "filled,solid", "shape": "box"})

for name, node in self.nodes.items():
refs = get_refs(node)
label = ["{} {}".format(name, node.get("type", ""))] + ["{}: {}".format(k, v) for k,v in refs.items() if k not in ["IP"]]
label = [textwrap.fill(l, width=50) for l in label]
graph.node(name, label="\n".join(label))
if name == "I2SClockSource":
print(self.edges.keys())
if "IP" in refs and not any(edge.startswith("{}->".format(name)) or edge.endswith("<-{}".format(name)) for edge in self.edges.keys()):
for ip in [ip.strip() for ip in refs["IP"].split(",") if ip.strip()]:
self.edges["{}->{}".format(name, ip)] = node
graph.node(ip, shape="egg")
self.ips[ip] = node

for name in set(self.signals) - set(self.nodes):
graph.node(name, shape="pentagon")

for name, edge in self.edges.items():
if "->" in name:
efrom, eto = name.split("->")
elif "<-" in name:
eto, efrom = name.split("<-")
graph.edge(efrom, eto, label=edge.get("refValue", ""))

outfile = Path("clock/{}.svg".format(ip_name))
outfile.write_text(graph.pipe().decode("utf-8"))
21 changes: 16 additions & 5 deletions tools/generator/dfg/stm32/stm_device_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import os
import re
import logging
import networkx as nx

from ..device_tree import DeviceTree
from ..input.xml import XMLReader

from .stm_header import STMHeader
from .stm_identifier import STMIdentifier
from .stm_clock import STMClock
from . import stm
from . import stm_peripherals

Expand All @@ -25,6 +27,12 @@ class STMDeviceTree:
rootpath = os.path.join(os.path.dirname(__file__), "..", "..", "raw-device-data", "stm32-devices", "mcu")
familyFile = XMLReader(os.path.join(rootpath, "families.xml"))

@staticmethod
def getIpFile(device_file, peripheral):
ip_file = device_file.query('//IP[@Name="{}"]'.format(peripheral))[0].get("Version")
ip_file = os.path.join(STMDeviceTree.rootpath, "IP", "{}-{}_Modes.xml".format(peripheral, ip_file))
return XMLReader(ip_file)

@staticmethod
def getDevicesFromFamily(family):
rawDevices = STMDeviceTree.familyFile.query('//Family[@Name="{}"]/SubFamily/Mcu/@RefName'.format(family))
Expand Down Expand Up @@ -63,6 +71,10 @@ def _properties_from_partname(partname):

LOGGER.info("Parsing '{}'".format(did.string))

rccFile = STMDeviceTree.getIpFile(device_file, "RCC")
clock = STMClock.get(did, rccFile)
return None

# information about the core and architecture
core = device_file.query('//Core')[0].text.lower().replace("arm ", "")
if core.endswith("m4") or core.endswith("m7"):
Expand Down Expand Up @@ -157,8 +169,8 @@ def clean_up_version(version):
match = re.search("v[1-9]_[0-9x]", version.replace(".", "_"))
if match:
version = match.group(0).replace("_", ".")
else:
print(version)
# else:
# print(version)
return version

modules = []
Expand Down Expand Up @@ -195,9 +207,8 @@ def clean_up_version(version):
p["interrupts"] = stm_header.get_interrupt_table()

# lets load additional information about the GPIO IP
ip_file = device_file.query('//IP[@Name="GPIO"]')[0].get("Version")
ip_file = os.path.join(STMDeviceTree.rootpath, "IP", "GPIO-" + ip_file + "_Modes.xml")
gpioFile = XMLReader(ip_file)
gpioFile = STMDeviceTree.getIpFile(device_file, "GPIO")


pins = device_file.query('//Pin[@Type="I/O"][starts-with(@Name,"P")]')
def raw_pin_sort(p):
Expand Down