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()

Reply via email to