This is an automated email from the ASF dual-hosted git repository. raiden00 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit b5913af01be697b8dad0b52493da1ed60e8e010c Author: David Sidrane <[email protected]> AuthorDate: Mon Apr 10 06:01:04 2023 -0700 tools:Add STM32 Pin migration tool tools/stm32_pinmap_tool: Generate legacy file stm32_pinmap_tool:Handel #undef --- tools/stm32_pinmap_tool.py | 571 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 571 insertions(+) diff --git a/tools/stm32_pinmap_tool.py b/tools/stm32_pinmap_tool.py new file mode 100755 index 0000000000..e0dce0a2ac --- /dev/null +++ b/tools/stm32_pinmap_tool.py @@ -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()
