tools:Add STM32 Pin migration tool
tools/stm32_pinmap_tool: Generate legacy file stm32_pinmap_tool:Handel #undef
This commit is contained in:
parent
c00498c164
commit
b5913af01b
571
tools/stm32_pinmap_tool.py
Executable file
571
tools/stm32_pinmap_tool.py
Executable file
@ -0,0 +1,571 @@
|
||||
#!/usr/bin/env python3
|
||||
############################################################################
|
||||
# tools/stm32_pinmap_tool.py
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
# for python2.7 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from argparse import RawTextHelpFormatter
|
||||
from glob import glob
|
||||
|
||||
suffix = "_0"
|
||||
remaps_re = re.compile(r"(.*REMAP.*)=y")
|
||||
ip_block_re = re.compile(r"CONFIG_STM32[A-Z0-9]*_([A-Z0-9]+[0-9]*)=")
|
||||
stm32f1_re = re.compile(r"stm32f10[0-9][a-z]*_pinmap")
|
||||
speed_re = re.compile(r"(GPIO_(?:SPEED|MODE)_[zA-Z0-9]+)")
|
||||
port_re = re.compile(r"GPIO_PORT([A-Z])\|")
|
||||
pin_re = re.compile(r"GPIO_PIN(\d+)")
|
||||
define_re = re.compile(r"#\s*define\s+(GPIO.*)\s+(GPIO.*?)\s+")
|
||||
|
||||
|
||||
class GPIODef:
|
||||
def __init__(self, original_name, name, description):
|
||||
self.original_name = original_name
|
||||
self.name = name
|
||||
self.block = name.split("_")[1]
|
||||
self.speed = None
|
||||
s = speed_re.search(description)
|
||||
if s:
|
||||
self.speed = s.group(1)
|
||||
s = port_re.search(description)
|
||||
if s:
|
||||
self.port = s.group(1)
|
||||
s = pin_re.search(description)
|
||||
if s:
|
||||
self.pin = s.group(1)
|
||||
|
||||
def __str__(self):
|
||||
fmt = "#define {0: <20} {1} /* P{2} */"
|
||||
if self.speed:
|
||||
if "MODE" in self.speed:
|
||||
if "MHz" in self.speed:
|
||||
# F1 has mode, MHz is output, we must adjust the speed
|
||||
fmt = "#define {0: <20} GPIO_ADJUST_MODE({1}, {3}) /* P{2} */ "
|
||||
else:
|
||||
# All others had a OSPEDD reg so we just set it
|
||||
fmt = "#define {0: <20} ({1} | {3}) /* P{2} */ "
|
||||
|
||||
return fmt.format(
|
||||
self.original_name,
|
||||
self.name,
|
||||
self.port + self.pin,
|
||||
self.speed,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<GPIODef block:{self.block} \
|
||||
original_name:{self.original_name} \
|
||||
name:{self.name} port:{self.port} \
|
||||
pin:{self.pin} speed:{self.speed}>"
|
||||
|
||||
|
||||
# Detect python version
|
||||
if sys.version_info[0] < 3:
|
||||
runningPython3 = False
|
||||
else:
|
||||
runningPython3 = True
|
||||
|
||||
|
||||
def parse_args():
|
||||
# Parse commandline arguments
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=RawTextHelpFormatter,
|
||||
description="""stm32_pinmap_tool.py
|
||||
|
||||
This tool is used to migrate legacy stm32 pinmap files that
|
||||
had included pin speed (slew rate control) in pinmap pin definitions
|
||||
|
||||
These speeds should have never been part of the arch defines as these
|
||||
are layout and board dependent. Therefore, the complete definition
|
||||
should be a composition of the pinmap defines and speed, and defined in
|
||||
board.h
|
||||
|
||||
Furthermore, pinmaps did not suffix pins that had only one ALT
|
||||
appearance on a GPIO. Therefore there was no way to change the speed
|
||||
or any other pins attribute i.e. Pullup Pulldown, Push pull. Open Drain etc.
|
||||
|
||||
The tool has a conversion mode and a report mode.
|
||||
|
||||
Conversion mode tool use:
|
||||
|
||||
Run the tool to do the conversion:
|
||||
i.e tools/stm32_pinmap_tool.py
|
||||
--pinmap arch/arm/src/stm32h7/hardware/stm32h7x3xx_pinmap.h
|
||||
--legacy > arch/arm/src/stm32h7/hardware/stm32h7x3xx_pinmap-new.h
|
||||
|
||||
-- pinmap - the file to convert
|
||||
--legacy will make a copy of the pinmap. Properly updating the file with
|
||||
xxxx/xxxxxxx_legacy to the title block,
|
||||
and adding _LEGACY to the #ifdef, #define and endif comment of the inclusion guard.
|
||||
|
||||
Conversion mode follow up edits:
|
||||
1. diff and verify the original pinmap and the pinmap-new.h are as expected.
|
||||
delete original pinmap
|
||||
rename pinmap-new.h to the original pinmap name.
|
||||
2. Edit the top level pinmap (i.e. arch/arm/src/stm32x/stm32x_pinmap.h) file and
|
||||
add a CONFIG_STM32xx_USE_LEGACY_PINMAP section
|
||||
that includes the legacy pinmap files.
|
||||
|
||||
For example
|
||||
if defined(CONFIG_STM32H7_USE_LEGACY_PINMAP)
|
||||
if defined(CONFIG_STM32H7_STM32H7X3XX)
|
||||
include "hardware/stm32h7x3xx_pinmap_legacy.h"
|
||||
elif defined(CONFIG_STM32H7_STM32H7B3XX)
|
||||
include "hardware/stm32h7x3xx_pinmap_legacy.h"
|
||||
elif defined(CONFIG_STM32H7_STM32H7X7XX)
|
||||
include "hardware/stm32h7x3xx_pinmap_legacy.h"
|
||||
else
|
||||
error "Unsupported STM32 H7 Pin map"
|
||||
endif
|
||||
else
|
||||
if defined(CONFIG_STM32H7_STM32H7X3XX)
|
||||
include "hardware/stm32h7x3xx_pinmap.h"
|
||||
elif defined(CONFIG_STM32H7_STM32H7B3XX)
|
||||
include "hardware/stm32h7x3xx_pinmap.h"
|
||||
elif defined(CONFIG_STM32H7_STM32H7X7XX)
|
||||
include "hardware/stm32h7x3xx_pinmap.h"
|
||||
else
|
||||
error "Unsupported STM32 H7 Pin map"
|
||||
endif
|
||||
endif
|
||||
|
||||
3. Add a STM32Hx_USE_LEGACY_PINMAP to the Kconfig defaulted to y
|
||||
|
||||
For example
|
||||
|
||||
config STM32H7_USE_LEGACY_PINMAP
|
||||
bool "Use the legacy pinmap with GPIO_SPEED_xxx included."
|
||||
default y
|
||||
---help---
|
||||
In the past, pinmap files included GPIO_SPEED_xxxMhz. These speed
|
||||
settings should have come from the board.h as it describes the wiring
|
||||
of the SoC to the board. The speed is really slew rate control and
|
||||
therefore is related to the layout and can only be properly set
|
||||
in board.h.
|
||||
|
||||
STM32H7_USE_LEGACY_PINMAP is provided, to allow lazy migration to
|
||||
using pinmaps without speeds. The work required to do this can be aided
|
||||
by running tools/stm32_pinmap_tool.py. The tools will take a board.h
|
||||
file and a legacy pinmap and output the required changes that one needs
|
||||
to make to a board.h file.
|
||||
|
||||
Eventually, STM32H7_USE_LEGACY_PINMAP will be deprecated and the legacy
|
||||
pinmaps removed from NuttX. Any new boards added should set
|
||||
STM32H7_USE_LEGACY_PINMAP=n and fully define the pins in board.h
|
||||
4. Add a warning to the xxx_gpio.c file
|
||||
|
||||
For example
|
||||
|
||||
#if defined(CONFIG_STM32_USE_LEGACY_PINMAP)
|
||||
# pragma message "CONFIG_STM32_USE_LEGACY_PINMAP will be deprecated migrate board.h see tools/stm32_pinmap_tool.py"
|
||||
#endif
|
||||
|
||||
Report mode tool use:
|
||||
|
||||
Run the tool to aid in migrating a board.h
|
||||
|
||||
tools/stm32_pinmap_tool.py --pinmap arch/arm/src/stm32h7/hardware/stm32h7x3xx_pinmap_legacy.h
|
||||
--report <fullpath>/include/board.h
|
||||
|
||||
it will output 2 sections that should be used to update the board.h.
|
||||
board.h defines that need to have speeds added.
|
||||
board.h defines that will need to be added:
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--pinmap",
|
||||
action="store",
|
||||
help="""pin map file to convert (changes are printed on stdout) or
|
||||
Legacy file pin map file named <filename>_legacy.<ext> to report board.h changes""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--report",
|
||||
default=False,
|
||||
action="store",
|
||||
help="Generate change set for a board",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--legacy",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="If one does not exist, create a copy of the original pin map named <filename>_legacy.<ext>",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def create_legacy(source):
|
||||
legacy = source.replace(".h", "_legacy.h")
|
||||
sourceshort = source[source.find("arch") :]
|
||||
legacyshort = legacy[legacy.find("arch") :]
|
||||
srctag = "__" + sourceshort.upper().replace("/", "_")
|
||||
destag = "__" + legacyshort.upper().replace("/", "_").replace(".", "_")
|
||||
if not os.path.isfile(legacy):
|
||||
fout = open(legacy, "w")
|
||||
fin = open(source, "r")
|
||||
|
||||
for line in fin:
|
||||
out = re.sub(sourceshort, legacyshort, line)
|
||||
out = re.sub(srctag, destag, out)
|
||||
fout.write(out)
|
||||
fout.close()
|
||||
fin.close()
|
||||
|
||||
|
||||
def read_defconfigs(boardfile_path):
|
||||
configs_lines = []
|
||||
defconfigs_files = []
|
||||
|
||||
for dir, _, _ in os.walk(boardfile_path[: boardfile_path.find("include/board.h")]):
|
||||
defconfigs_files.extend(glob(os.path.join(dir, "defconfig")))
|
||||
|
||||
for file in defconfigs_files:
|
||||
defconfigfile = open(file, "r")
|
||||
configs_lines.extend(defconfigfile.readlines())
|
||||
defconfigfile.close()
|
||||
return configs_lines
|
||||
|
||||
|
||||
def build_ip_remap_list(boardfile_path):
|
||||
ip_blocks = []
|
||||
ip_remaps = []
|
||||
configs_lines = read_defconfigs(boardfile_path)
|
||||
configs_lines = sorted(set(configs_lines))
|
||||
|
||||
for line in configs_lines:
|
||||
s = ip_block_re.search(line)
|
||||
if s:
|
||||
ip_blocks.extend([s.group(1)])
|
||||
else:
|
||||
s = remaps_re.search(line)
|
||||
if s:
|
||||
ip_remaps.extend([s.group(1)])
|
||||
return [ip_blocks, ip_remaps]
|
||||
|
||||
|
||||
def read_board_h(boardfile_path):
|
||||
boardfile = open(boardfile_path, "r")
|
||||
lines = boardfile.readlines()
|
||||
boardfile.close()
|
||||
return lines
|
||||
|
||||
|
||||
def formated_print(lines):
|
||||
maxlen = 0
|
||||
for line in lines:
|
||||
linelen = line.find("/*")
|
||||
if linelen > maxlen:
|
||||
maxlen = linelen
|
||||
|
||||
for line in lines:
|
||||
linelen = line.find("/*")
|
||||
if linelen > 1 and linelen < maxlen:
|
||||
nl = line[:linelen] + " " * (maxlen - linelen) + line[linelen:]
|
||||
line = nl
|
||||
print(line)
|
||||
|
||||
|
||||
def report(boardfile_path, boards_ip_blocks, changelog, changelog_like):
|
||||
output = [
|
||||
"",
|
||||
]
|
||||
output.extend(
|
||||
[
|
||||
"""
|
||||
There were 3 issues with the Legacy pinmaps.
|
||||
|
||||
1. The legacy version of the pin defines included speed settings. (These are
|
||||
in reality, slew rates).
|
||||
|
||||
2. Legacy pinmaps erroneously added speeds on pins that are only used
|
||||
as an inputs (i.e UART4_RX). These speeds can be removed from the board.h
|
||||
defines.
|
||||
|
||||
3. Also the legacy version of the pin defines did not have a suffix on all
|
||||
pins and therefore all pins could not have the attributes set or changed
|
||||
by board.h
|
||||
|
||||
The new pinmaps correct these issues:
|
||||
|
||||
Pin that had an explicit (GPIO_SPEED|MODE)_xxxMHz are removed or set to
|
||||
the lowest speed.
|
||||
|
||||
If the pin had only one choice previously (un-suffixed) the pin name now
|
||||
contains _0 as the suffix.
|
||||
|
||||
N.B. The correct speed setting for a given pin is very dependent on the
|
||||
layout of the circuit board and load presented to the SoC on that pin.
|
||||
|
||||
The speeds listed below are from the Legacy pinmaps and are provided ONLY
|
||||
to insure these changes do not break existing systems that are relying on
|
||||
the legacy speed settings.
|
||||
|
||||
It highly recommended that the speed setting for each pin be verified for
|
||||
overshoot and undershoot on real hardware and adjusted in the board,h
|
||||
appropriately.
|
||||
|
||||
|
||||
board.h defines that need to have speeds added.
|
||||
|
||||
"""
|
||||
]
|
||||
)
|
||||
|
||||
boards_blocks = []
|
||||
Lines = read_board_h(boardfile_path)
|
||||
for line in Lines:
|
||||
s = define_re.search(line)
|
||||
if s:
|
||||
# #define GPIO_SD_CK GPIO_SD_CK_1 /* PD6 FC_PD6_SD_CK */
|
||||
define = s.group(1)
|
||||
original_name = s.group(2)
|
||||
change = changelog.get(original_name)
|
||||
if change:
|
||||
pindef = GPIODef(define, original_name, line)
|
||||
if pindef.block not in boards_blocks:
|
||||
boards_blocks.append(pindef.block)
|
||||
output.extend([f"\n/* {pindef.block} */\n"])
|
||||
output.extend([str(changelog[original_name])])
|
||||
if len(boards_blocks) == 0:
|
||||
output.extend(
|
||||
[
|
||||
"""
|
||||
No pins are defined in board.h to change speeds on (most likely an stm32f1")
|
||||
We will define all the pins used next...
|
||||
"""
|
||||
]
|
||||
)
|
||||
|
||||
formated_print(output)
|
||||
output = []
|
||||
|
||||
output.extend(
|
||||
[
|
||||
"""
|
||||
|
||||
Pin that had only one choice previously (un-suffixed) pins will need to be
|
||||
defined in board.h to map the un-suffixed) pin name used in the drives to
|
||||
the _0 suffixed ones.
|
||||
|
||||
Pins that did not have an explicit (GPIO_SPEED|MODE)_xxxMHz specified are
|
||||
listed with the pin name containing the new suffix.
|
||||
|
||||
|
||||
board.h defines that may need to be added if the pins are used on the board:
|
||||
|
||||
|
||||
"""
|
||||
]
|
||||
)
|
||||
|
||||
for block in boards_ip_blocks:
|
||||
change = changelog_like.get(block)
|
||||
if change:
|
||||
block_title = f"\n/* {block} */\n"
|
||||
for gpio in change:
|
||||
if re.search(r"_\d+$", gpio.original_name) is None:
|
||||
if block_title:
|
||||
output.extend([block_title])
|
||||
block_title = None
|
||||
output.extend([str(gpio)])
|
||||
|
||||
formated_print(output)
|
||||
|
||||
|
||||
def formatcols(list, cols):
|
||||
lines = ("\t".join(list[i : i + cols]) for i in range(0, len(list), cols))
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def parse_conditional(lines, conditions):
|
||||
defines = []
|
||||
|
||||
def_remap_re = re.compile(r"\s*defined\s*\((.*REMAP.*)\)")
|
||||
def_else_re = re.compile(r"#\s*else")
|
||||
def_endif_re = re.compile(r"#\s*endif")
|
||||
|
||||
active_define = None
|
||||
output = True
|
||||
once = False
|
||||
|
||||
for line in lines:
|
||||
# process #[el]if define(...REMAP)
|
||||
s = def_remap_re.search(line)
|
||||
if s:
|
||||
once = True
|
||||
define = s.group(1)
|
||||
if define in conditions:
|
||||
active_define = define
|
||||
output = True
|
||||
else:
|
||||
output = False
|
||||
else:
|
||||
# process #endif
|
||||
s = def_endif_re.search(line)
|
||||
if s:
|
||||
active_define = None
|
||||
output = True
|
||||
else:
|
||||
# process #elese
|
||||
s = def_else_re.search(line)
|
||||
if s:
|
||||
once = True
|
||||
# the if or elif was taken do not output the else
|
||||
if active_define:
|
||||
output = False
|
||||
else:
|
||||
output = output ^ True
|
||||
|
||||
if once or output:
|
||||
once = False
|
||||
defines.extend([line])
|
||||
return defines
|
||||
|
||||
|
||||
def formmatter(args):
|
||||
# if pinmap passed is a legacy pinmap. Just generate a report
|
||||
report_only = args.report is not False
|
||||
|
||||
speed_not_mode = stm32f1_re.search(args.pinmap) is None
|
||||
|
||||
if not report_only and args.legacy is True:
|
||||
create_legacy(args.pinmap)
|
||||
|
||||
pinfile = open(args.pinmap, "r")
|
||||
Lines = pinfile.readlines()
|
||||
|
||||
if report_only:
|
||||
boards_ip_blocks, remaps = build_ip_remap_list(args.report)
|
||||
print(
|
||||
f"\n\nBoard enabled Blocks:\n\n{formatcols(sorted(boards_ip_blocks), 8)}\n\n"
|
||||
)
|
||||
if (
|
||||
"ADC1" in boards_ip_blocks
|
||||
or "ADC2" in boards_ip_blocks
|
||||
or "ADC3" in boards_ip_blocks
|
||||
):
|
||||
boards_ip_blocks.extend(["ADC12"])
|
||||
boards_ip_blocks.extend(["ADC123"])
|
||||
boards_ip_blocks = sorted(boards_ip_blocks)
|
||||
# Filter out ifdefed by remap conditionals (F1)
|
||||
if len(remaps) > 0:
|
||||
Lines = parse_conditional(Lines, remaps)
|
||||
|
||||
Pass = False
|
||||
inComment = False
|
||||
|
||||
changelog = {}
|
||||
changelog_like = {}
|
||||
pass_list = [r"#\s*if", r"#\s*else", r"#\s*end", r"#\s*include", r"#\s*undef"]
|
||||
pass_list_re = re.compile("|".join(pass_list))
|
||||
|
||||
for line in Lines:
|
||||
if len(line.strip()) == 0:
|
||||
Pass = True
|
||||
if pass_list_re.search(line):
|
||||
Pass = True
|
||||
if "#define" in line and "GPIO" not in line:
|
||||
Pass = True
|
||||
if "defined(" in line:
|
||||
Pass = True
|
||||
if "/*" in line:
|
||||
inComment = True
|
||||
Pass = True
|
||||
if "*/" in line:
|
||||
inComment = False
|
||||
Pass = True
|
||||
if Pass or inComment:
|
||||
Pass = False
|
||||
if not report_only:
|
||||
print(line.rstrip(), end="")
|
||||
else:
|
||||
changed = False
|
||||
# split the line on spaces
|
||||
pieces = line.split()
|
||||
# deal with white space in the # define for nested defines
|
||||
sel = 0
|
||||
# Does it have white space then use next set?
|
||||
if pieces[0] == "#":
|
||||
sel = 1
|
||||
original_name = pieces[sel + 1]
|
||||
gpiocgf = pieces[sel + 2]
|
||||
new_name = original_name
|
||||
if re.search(r"_\d+$", original_name) is None:
|
||||
# Add suffix
|
||||
pad = ""
|
||||
sel = line.find(original_name) + len(original_name)
|
||||
if line[sel + len(suffix)] == "(":
|
||||
pad = " "
|
||||
if line[sel + len(suffix)] == "G":
|
||||
pad = " ("
|
||||
nl = line[:sel] + suffix + pad + line[sel + len(suffix) :]
|
||||
new_name = original_name + suffix
|
||||
changed = True
|
||||
else:
|
||||
nl = line
|
||||
# Remove the speed or chege the Mode
|
||||
if speed_not_mode:
|
||||
ol = re.sub(r"\s*GPIO_SPEED_[zA-Z0-9]+\s*\|", "", nl)
|
||||
else:
|
||||
ol = re.sub(
|
||||
r"(\s*)GPIO_MODE_[0-9]+MHz(\s*\|)", r"\g<1>GPIO_MODE_2MHz\g<2>", nl
|
||||
)
|
||||
|
||||
changed = changed or ol != nl
|
||||
if not report_only:
|
||||
print(ol.strip(), end="")
|
||||
if args.report and changed:
|
||||
changelog[original_name] = pindef = GPIODef(
|
||||
original_name, new_name, gpiocgf
|
||||
)
|
||||
|
||||
# create changes by block if enabled
|
||||
if pindef.block in boards_ip_blocks:
|
||||
# Is block in already?
|
||||
if pindef.block in changelog_like:
|
||||
# do not duplicate it
|
||||
if pindef not in changelog_like[pindef.block]:
|
||||
changelog_like[pindef.block].append(pindef)
|
||||
else:
|
||||
changelog_like[pindef.block] = [pindef]
|
||||
|
||||
if not report_only:
|
||||
print("")
|
||||
if args.report:
|
||||
report(args.report, boards_ip_blocks, changelog, changelog_like)
|
||||
|
||||
|
||||
def main():
|
||||
# Python2 is EOL
|
||||
if not runningPython3:
|
||||
raise RuntimeError(
|
||||
"Python 2 is not supported. Please try again using Python 3."
|
||||
)
|
||||
args = parse_args()
|
||||
formmatter(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user