Skip to content

Commit 787f9bc

Browse files
scripts: kinetis: Add ability to generate pin groups
Script now supports generating pin groups from input mex file Signed-off-by: Daniel DeGrasse <[email protected]>
1 parent c26b0af commit 787f9bc

File tree

2 files changed

+195
-4
lines changed

2 files changed

+195
-4
lines changed

mcux/scripts/kinetis_gen_dts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212

1313
gen = NXPSdkUtil(xml, log_level = logging.INFO)
1414
gen.write_pinctrl_defs("pinctrl.dtsi")
15+
gen.write_pinctrl_groups("/mnt/windows-share/mex-files/FRDM-K64F.mex", 'board.dtsi')

mcux/scripts/kinetis_signal2dts.py

Lines changed: 194 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,18 @@ def __init__(self, connection):
3939
self._port = None
4040
for periph in connection.iter('peripheral_signal_ref'):
4141
self._periph = periph.attrib.get('peripheral')
42+
self._signal = periph.attrib.get('signal')
43+
self._channel = periph.attrib.get('channel')
4244
for assign in connection.iter('assign'):
4345
reg = assign.attrib.get('register')
4446
val = assign.attrib.get('bit_field_value')
4547
logging.debug('\t\t\t [ASSIGN] %s %s', reg, val)
4648
# Only process PCR registers
4749
match = re.match(r'PORT([A-Z])_PCR(\d+)', reg)
4850
if match:
49-
self._name += f"_PT{match.group(1)}{match.group(2)}"
51+
# For muxes like PTC5, do not append peripheral name
52+
if re.match(r'PT[A-Z]\d+', self._name) is None:
53+
self._name += f"_PT{match.group(1)}{match.group(2)}"
5054
self._port = match.group(1)
5155
self._pin = int(match.group(2))
5256
self._mux = int(val, 16)
@@ -72,6 +76,12 @@ def get_port(self):
7276
"""
7377
return self._port
7478

79+
def get_signal(self):
80+
"""
81+
Get mux signal name
82+
"""
83+
return self._signal
84+
7585
def get_pin(self):
7686
"""
7787
Get mux pin
@@ -90,6 +100,12 @@ def get_periph(self):
90100
"""
91101
return self._periph
92102

103+
def get_channel(self):
104+
"""
105+
Get channel number
106+
"""
107+
return self._channel
108+
93109
def __hash__(self):
94110
"""
95111
Override hash method to return pin name as hash
@@ -136,7 +152,13 @@ def __init__(self, pin):
136152
mux_opt = MUXOption(connections)
137153
# Only append mux options with a valid name
138154
if mux_opt.get_name() != '':
139-
self._mux_options[mux_opt.get_name()] = mux_opt
155+
# Use a string of the peripheral name and signal here, since that
156+
# is what the MEX file uses as a key to find this mux option
157+
key = f"{mux_opt.get_periph()}_{mux_opt.get_signal()}"
158+
if mux_opt.get_channel() is not None:
159+
# Special case, use channel for key
160+
key = f"{mux_opt.get_periph()}_{mux_opt.get_channel()}"
161+
self._mux_options[key] = mux_opt
140162

141163
def __repr__(self):
142164
"""
@@ -251,6 +273,112 @@ def _get_pin_properties(self, props):
251273
prop_mapping[prop_id][state.attrib['id']] = bit_value
252274
return prop_mapping
253275

276+
class PinGroup:
277+
"""
278+
Internal class representing pin group
279+
"""
280+
def __init__(self, function, signal_map):
281+
"""
282+
Creates a pin group
283+
@param function: function xml structure from MEX configuration file
284+
@param signal_map: Signal mapping, maps signal names to signal pins
285+
"""
286+
self._name = function.attrib.get('name')
287+
pins = function.find('mex:pins', NAMESPACES)
288+
signal_regex = re.compile(r'PT[A-Z]\d+')
289+
# Build dictionary mapping pin properties to pins. This allows us to
290+
# group pins based on shared configuration
291+
self._pin_groups = collections.defaultdict(lambda: [])
292+
for pin in pins:
293+
# find signal defintion for this pin
294+
signal_name = signal_regex.search(pin.attrib.get('pin_signal'))
295+
if signal_name is None or (not signal_name.group(0) in signal_map):
296+
# No definition for this pin mapping
297+
logging.warning('Signal %s not matched', pin.attrib.get('pin_signal'))
298+
continue
299+
signal = signal_map[signal_name.group(0)]
300+
# Get mux option for this pin
301+
key = f"{pin.attrib.get('peripheral')}_{pin.attrib.get('signal')}"
302+
channel = re.search(r'(\d+)', pin.attrib.get('signal'))
303+
# Special case, use channel for key
304+
if channel:
305+
key = f"{pin.attrib.get('peripheral')}_{channel.group(1)}"
306+
mux = signal.get_mux_connection(key)
307+
if mux is None:
308+
# Do not add pinmux option to group
309+
continue
310+
# Get pin defaults for this pin
311+
defaults = signal.get_pin_defaults()
312+
# Get pin overrides
313+
features = pin.find('mex:pin_features', NAMESPACES)
314+
if features is not None:
315+
pin_overrides = {}
316+
for feature in pin.find('mex:pin_features', NAMESPACES):
317+
pin_overrides[feature.attrib.get('name')] = feature.attrib.get('value')
318+
pin_props = self._props_to_dts(pin_overrides, defaults)
319+
self._pin_groups[pin_props].append(mux)
320+
321+
def __repr__(self):
322+
"""
323+
Get string representation of the object
324+
"""
325+
return "PinGroup(%s)" % (self._name)
326+
327+
def get_pin_props(self):
328+
"""
329+
Get all unique pin properties
330+
"""
331+
return self._pin_groups.keys()
332+
333+
def get_pins(self, props):
334+
"""
335+
Get all pins with a provided set of properties
336+
@param props: property set
337+
"""
338+
return self._pin_groups[props]
339+
340+
def get_name(self):
341+
"""
342+
Get pin group name
343+
"""
344+
return self._name
345+
346+
def _props_to_dts(self, props, defaults):
347+
"""
348+
Remap dictionary of property names from NXP defined values to
349+
Zephyr ones
350+
@param props: Dictionary of NXP property names and values
351+
@param defaults: Dictionary of NXP property names and default pin values
352+
@return array of strings suitable for writing to DTS
353+
"""
354+
zephyr_props = []
355+
prop_mapping = {
356+
'fast': 'fast',
357+
'slow': 'slow',
358+
'low': 'low',
359+
'high': 'high',
360+
}
361+
# Lambda to convert property names to zephyr formatted strings
362+
sanitize = lambda x: "\"" + prop_mapping[x] + "\"" if (x in prop_mapping) else ""
363+
# Lambda to get property value or fallback on default
364+
prop_val = lambda x: props[x] if x in props else defaults[x]
365+
# Check pin defaults and overrides to see if the pin will have a pull
366+
pull_enable = prop_val('pull_enable') == 'enable'
367+
# For each property, append the provided override or the default
368+
zephyr_props.append(f"drive-strength = {sanitize(prop_val('drive_strength'))}")
369+
if prop_val('open_drain') == 'enable':
370+
zephyr_props.append('drive-open-drain')
371+
if pull_enable:
372+
# If pull is enabled, select pull up or pull down
373+
if prop_val('pull_select') == 'up':
374+
zephyr_props.append('bias-pull-up')
375+
else:
376+
zephyr_props.append('bias-pull-down')
377+
zephyr_props.append(f"slew-rate = {sanitize(prop_val('slew_rate'))}")
378+
if prop_val('passive_filter') == 'enable':
379+
zephyr_props.append("nxp,passive-filter")
380+
return tuple(zephyr_props)
381+
254382

255383
class NXPSdkUtil:
256384
"""
@@ -271,7 +399,7 @@ def __init__(self, signal_file, copyright_header = "", log_level = logging.ERROR
271399
self._logger.setLevel(log_level)
272400
self._parse_signal_xml(signal_file)
273401
self._copyright = copyright_header
274-
logging.info("Loaded %d pin defs", len(self._pins))
402+
logging.info("Loaded %d configurable pin defs", len(self._pins))
275403

276404
def _parse_signal_xml(self, signal_fn):
277405
"""
@@ -381,4 +509,66 @@ def write_pinctrl_defs(self, outputfile):
381509
self._write_pins('c', pcr_pins, file)
382510
self._write_pins('d', pcr_pins, file)
383511
self._write_pins('e', pcr_pins, file)
384-
512+
513+
def _parse_mex_cfg(self, mexfile):
514+
"""
515+
Parses mex configuration into pin groups.
516+
@param mexfile: mex configuration file to parse
517+
@return parsed pin groups
518+
"""
519+
pin_groups = {}
520+
try:
521+
mex_xml = ET.parse(mexfile)
522+
for function in mex_xml.findall(
523+
'mex:tools/mex:pins/mex:functions_list/mex:function', NAMESPACES):
524+
group = PinGroup(function, self._pins)
525+
pin_groups[group.get_name()] = group
526+
return pin_groups
527+
except ET.ParseError:
528+
logging.error("Could not parse mex file %s", mex_xml)
529+
return None
530+
531+
def write_pinctrl_groups(self, mexfile, outputfile):
532+
"""
533+
Write pinctrl groups to disk as a parsed DTS file. Intended for use
534+
with the output of @ref write_pinctrl_defs
535+
@param mexfile: mex file to parse
536+
@param outputfile: DTS pinctrl file to write pin groups to
537+
"""
538+
file_header = ("/*\n"
539+
" * NOTE: Autogenerated file by kinetis_signal2dts.py\n"
540+
f" * for {self._part_num}/signal_configuration.xml\n"
541+
" *\n"
542+
f" * {self._copyright}\n"
543+
" * SPDX-License-Identifier: Apache-2.0\n"
544+
" */\n"
545+
"\n")
546+
pin_groups = self._parse_mex_cfg(mexfile)
547+
with open(outputfile, "w", encoding="utf8") as file:
548+
file.write(file_header)
549+
file.write("&pinctrl {\n")
550+
# Write pin groups back out to disk
551+
for group in pin_groups.values():
552+
pin_props = group.get_pin_props()
553+
if len(pin_props) == 0:
554+
# Do not write to disk
555+
continue
556+
logging.info("Writing pin group %s to disk", group.get_name())
557+
file.write(f"\t{group.get_name().lower()}:{group.get_name().lower()}: {{\n")
558+
idx = 0
559+
for pin_prop in sorted(pin_props):
560+
group_str = f"\t\tgroup{idx} {{\n"
561+
# Write all pin names
562+
group_str += "\t\t\tpins = <"
563+
for pin in group.get_pins(pin_prop):
564+
group_str += f"&{pin.get_name()}\n\t\t\t\t"
565+
# Strip out last 3 tabs and close pin name list
566+
group_str = re.sub(r'\n\t\t\t\t$', '>;\n', group_str)
567+
idx += 1
568+
# Write all pin props
569+
for prop in pin_prop:
570+
group_str += f"\t\t\t{prop}\n"
571+
group_str += "\t\t};\n"
572+
file.write(group_str)
573+
file.write("\t};\n\n")
574+
file.write("};\n")

0 commit comments

Comments
 (0)