From 0e5cef3ffa222dcd30e1602bff0f1819242c6740 Mon Sep 17 00:00:00 2001
From: Andrew Scheller <andrew.scheller@raspberrypi.com>
Date: Fri, 21 Jun 2024 20:26:45 +0100
Subject: [PATCH] Boards header updates (#1724)

* Add script to automatically validate board header files

* Fix small automatically-found inconsistencies in various board header files

* Tweak and add board header file from abandoned PR #1174
---
 src/boards/include/boards/0xcb_helios.h       |  84 ++++++++
 .../include/boards/cytron_maker_pi_rp2040.h   |   6 +-
 .../include/boards/nullbits_bit_c_pro.h       |   2 +-
 src/boards/include/boards/pico_w.h            |   2 +-
 .../include/boards/pimoroni_tiny2040_2mb.h    |   2 +-
 .../include/boards/pololu_3pi_2040_robot.h    |   4 +-
 .../include/boards/pololu_zumo_2040_robot.h   |   4 +-
 .../include/boards/weact_studio_rp2040_2mb.h  |   8 +-
 .../include/boards/weact_studio_rp2040_4mb.h  |   8 +-
 .../include/boards/weact_studio_rp2040_8mb.h  |   8 +-
 src/rp2040/rp2040_interface_pins.json         |  74 +++++++
 tools/check_all_board_headers.sh              |   7 +
 tools/check_board_header.py                   | 183 ++++++++++++++++++
 13 files changed, 370 insertions(+), 22 deletions(-)
 create mode 100644 src/boards/include/boards/0xcb_helios.h
 create mode 100644 src/rp2040/rp2040_interface_pins.json
 create mode 100755 tools/check_all_board_headers.sh
 create mode 100755 tools/check_board_header.py

diff --git a/src/boards/include/boards/0xcb_helios.h b/src/boards/include/boards/0xcb_helios.h
new file mode 100644
index 0000000..e9df358
--- /dev/null
+++ b/src/boards/include/boards/0xcb_helios.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+// -----------------------------------------------------
+// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO
+//       SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
+// -----------------------------------------------------
+//
+//------------------------------------------------------------------------------------------
+// Board definition for the 0xCB Helios
+
+#ifndef _BOARDS_0XCB_HELIOS_H
+#define _BOARDS_0XCB_HELIOS_H
+
+// For board detection
+#define _0XCB_HELIOS
+
+#ifndef PICO_DEFAULT_UART
+#define PICO_DEFAULT_UART 0
+#endif
+#ifndef PICO_DEFAULT_UART_TX_PIN
+#define PICO_DEFAULT_UART_TX_PIN 0
+#endif
+#ifndef PICO_DEFAULT_UART_RX_PIN
+#define PICO_DEFAULT_UART_RX_PIN 1
+#endif
+
+// User LED and level shifted PIN
+#ifndef PICO_DEFAULT_LED_PIN
+#define PICO_DEFAULT_LED_PIN 17
+#endif
+#ifndef PICO_DEFAULT_WS2812_PIN
+#define PICO_DEFAULT_WS2812_PIN 25
+#endif
+
+// --- I2C ---
+#ifndef PICO_DEFAULT_I2C
+#define PICO_DEFAULT_I2C 1
+#endif
+#ifndef PICO_DEFAULT_I2C_SDA_PIN
+#define PICO_DEFAULT_I2C_SDA_PIN 2
+#endif
+#ifndef PICO_DEFAULT_I2C_SCL_PIN
+#define PICO_DEFAULT_I2C_SCL_PIN 3
+#endif
+
+// --- SPI ---
+#ifndef PICO_DEFAULT_SPI
+#define PICO_DEFAULT_SPI 0
+#endif
+#ifndef PICO_DEFAULT_SPI_SCK_PIN
+#define PICO_DEFAULT_SPI_SCK_PIN 22
+#endif
+#ifndef PICO_DEFAULT_SPI_TX_PIN
+#define PICO_DEFAULT_SPI_TX_PIN 23
+#endif
+#ifndef PICO_DEFAULT_SPI_RX_PIN
+#define PICO_DEFAULT_SPI_RX_PIN 20
+#endif
+#ifndef PICO_DEFAULT_SPI_CSN_PIN
+#define PICO_DEFAULT_SPI_CSN_PIN 21
+#endif
+
+#define PICO_BOOT_STAGE2_CHOOSE_W25Q080 1
+
+#ifndef PICO_FLASH_SPI_CLKDIV
+#define PICO_FLASH_SPI_CLKDIV 2
+#endif
+
+// board has 16M onboard flash
+#ifndef PICO_FLASH_SIZE_BYTES
+#define PICO_FLASH_SIZE_BYTES (16 * 1024 * 1024)
+#endif
+
+// All boards have B1 RP2040
+
+#ifndef PICO_RP2040_B0_SUPPORTED
+#define PICO_RP2040_B0_SUPPORTED 0
+#endif
+
+#endif
diff --git a/src/boards/include/boards/cytron_maker_pi_rp2040.h b/src/boards/include/boards/cytron_maker_pi_rp2040.h
index 72ed4be..77dd424 100644
--- a/src/boards/include/boards/cytron_maker_pi_rp2040.h
+++ b/src/boards/include/boards/cytron_maker_pi_rp2040.h
@@ -26,8 +26,8 @@
 #define MAKER_PI_RP2040_M1B_PIN 9
 #endif
 
-#ifndef MAKER_PI_RP2040_M2B_PIN
-#define MAKER_PI_RP2040_M2B_PIN 10
+#ifndef MAKER_PI_RP2040_M2A_PIN
+#define MAKER_PI_RP2040_M2A_PIN 10
 #endif
 
 #ifndef MAKER_PI_RP2040_M2B_PIN
@@ -186,4 +186,4 @@
 #define PICO_RP2040_B0_SUPPORTED 0
 #endif
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/boards/include/boards/nullbits_bit_c_pro.h b/src/boards/include/boards/nullbits_bit_c_pro.h
index cbc287d..169e0c1 100644
--- a/src/boards/include/boards/nullbits_bit_c_pro.h
+++ b/src/boards/include/boards/nullbits_bit_c_pro.h
@@ -56,7 +56,7 @@
 
 //------------- I2C -------------//
 #ifndef PICO_DEFAULT_I2C
-#define PICO_DEFAULT_I2C 0
+#define PICO_DEFAULT_I2C 1
 #endif
 
 #ifndef PICO_DEFAULT_I2C_SDA_PIN
diff --git a/src/boards/include/boards/pico_w.h b/src/boards/include/boards/pico_w.h
index 3109c78..ba80a49 100644
--- a/src/boards/include/boards/pico_w.h
+++ b/src/boards/include/boards/pico_w.h
@@ -9,7 +9,7 @@
 //       SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
 // -----------------------------------------------------
 
-// This header may be included by other board headers as "boards/pico.h"
+// This header may be included by other board headers as "boards/pico_w.h"
 
 #ifndef _BOARDS_PICO_W_H
 #define _BOARDS_PICO_W_H
diff --git a/src/boards/include/boards/pimoroni_tiny2040_2mb.h b/src/boards/include/boards/pimoroni_tiny2040_2mb.h
index e32d370..7aa07b2 100644
--- a/src/boards/include/boards/pimoroni_tiny2040_2mb.h
+++ b/src/boards/include/boards/pimoroni_tiny2040_2mb.h
@@ -13,8 +13,8 @@
 #define _BOARDS_PIMORONI_TINY2040_2MB_H
 
 // For board detection
-#define PIMORONI_TINY2040
 #define PIMORONI_TINY2040_2MB
+#define PIMORONI_TINY2040
 
 // --- BOARD SPECIFIC ---
 #define TINY2040_LED_R_PIN 18
diff --git a/src/boards/include/boards/pololu_3pi_2040_robot.h b/src/boards/include/boards/pololu_3pi_2040_robot.h
index a3110cc..94be451 100644
--- a/src/boards/include/boards/pololu_3pi_2040_robot.h
+++ b/src/boards/include/boards/pololu_3pi_2040_robot.h
@@ -9,8 +9,8 @@
 //       SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
 // -----------------------------------------------------
 
-#ifndef _POLOLU_3PI_2040_ROBOT_H
-#define _POLOLU_3PI_2040_ROBOT_H
+#ifndef _BOARDS_POLOLU_3PI_2040_ROBOT_H
+#define _BOARDS_POLOLU_3PI_2040_ROBOT_H
 
 // For board detection
 #define POLOLU_3PI_2040_ROBOT
diff --git a/src/boards/include/boards/pololu_zumo_2040_robot.h b/src/boards/include/boards/pololu_zumo_2040_robot.h
index 252cea9..7eb1cf0 100644
--- a/src/boards/include/boards/pololu_zumo_2040_robot.h
+++ b/src/boards/include/boards/pololu_zumo_2040_robot.h
@@ -9,8 +9,8 @@
 //       SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
 // -----------------------------------------------------
 
-#ifndef _POLOLU_ZUMO_2040_ROBOT_H
-#define _POLOLU_ZUMO_2040_ROBOT_H
+#ifndef _BOARDS_POLOLU_ZUMO_2040_ROBOT_H
+#define _BOARDS_POLOLU_ZUMO_2040_ROBOT_H
 
 // For board detection
 #define POLOLU_ZUMO_2040_ROBOT
diff --git a/src/boards/include/boards/weact_studio_rp2040_2mb.h b/src/boards/include/boards/weact_studio_rp2040_2mb.h
index 34667eb..d058ce3 100644
--- a/src/boards/include/boards/weact_studio_rp2040_2mb.h
+++ b/src/boards/include/boards/weact_studio_rp2040_2mb.h
@@ -9,13 +9,13 @@
 //       SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
 // -----------------------------------------------------
 
-// This header may be included by other board headers as "boards/weact_studio_rp2040_16mb.h"
+// This header may be included by other board headers as "boards/weact_studio_rp2040_2mb.h"
 
-#ifndef _BOARDS_WEACT_STUDIO_RP2040_16MB_H
-#define _BOARDS_WEACT_STUDIO_RP2040_16MB_H
+#ifndef _BOARDS_WEACT_STUDIO_RP2040_2MB_H
+#define _BOARDS_WEACT_STUDIO_RP2040_2MB_H
 
 // For board detection
-#define WEACT_STUDIO_RP2040_16MB
+#define WEACT_STUDIO_RP2040_2MB
 
 // --- UART ---
 #ifndef PICO_DEFAULT_UART
diff --git a/src/boards/include/boards/weact_studio_rp2040_4mb.h b/src/boards/include/boards/weact_studio_rp2040_4mb.h
index 98f10a1..6b49a4f 100644
--- a/src/boards/include/boards/weact_studio_rp2040_4mb.h
+++ b/src/boards/include/boards/weact_studio_rp2040_4mb.h
@@ -9,13 +9,13 @@
 //       SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
 // -----------------------------------------------------
 
-// This header may be included by other board headers as "boards/weact_studio_rp2040_16mb.h"
+// This header may be included by other board headers as "boards/weact_studio_rp2040_4mb.h"
 
-#ifndef _BOARDS_WEACT_STUDIO_RP2040_16MB_H
-#define _BOARDS_WEACT_STUDIO_RP2040_16MB_H
+#ifndef _BOARDS_WEACT_STUDIO_RP2040_4MB_H
+#define _BOARDS_WEACT_STUDIO_RP2040_4MB_H
 
 // For board detection
-#define WEACT_STUDIO_RP2040_16MB
+#define WEACT_STUDIO_RP2040_4MB
 
 // --- UART ---
 #ifndef PICO_DEFAULT_UART
diff --git a/src/boards/include/boards/weact_studio_rp2040_8mb.h b/src/boards/include/boards/weact_studio_rp2040_8mb.h
index 5d28e7b..bdddd22 100644
--- a/src/boards/include/boards/weact_studio_rp2040_8mb.h
+++ b/src/boards/include/boards/weact_studio_rp2040_8mb.h
@@ -9,13 +9,13 @@
 //       SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
 // -----------------------------------------------------
 
-// This header may be included by other board headers as "boards/weact_studio_rp2040_16mb.h"
+// This header may be included by other board headers as "boards/weact_studio_rp2040_8mb.h"
 
-#ifndef _BOARDS_WEACT_STUDIO_RP2040_16MB_H
-#define _BOARDS_WEACT_STUDIO_RP2040_16MB_H
+#ifndef _BOARDS_WEACT_STUDIO_RP2040_8MB_H
+#define _BOARDS_WEACT_STUDIO_RP2040_8MB_H
 
 // For board detection
-#define WEACT_STUDIO_RP2040_16MB
+#define WEACT_STUDIO_RP2040_8MB
 
 // --- UART ---
 #ifndef PICO_DEFAULT_UART
diff --git a/src/rp2040/rp2040_interface_pins.json b/src/rp2040/rp2040_interface_pins.json
new file mode 100644
index 0000000..35dd823
--- /dev/null
+++ b/src/rp2040/rp2040_interface_pins.json
@@ -0,0 +1,74 @@
+{
+    "UART": {
+        "0": {
+            "TX":  [0, 12, 16, 28],
+            "RX":  [1, 13, 17, 29],
+            "CTS": [2, 14, 18],
+            "RTS": [3, 15, 19]
+       },
+       "1": {
+            "TX":  [4, 8,  20, 24],
+            "RX":  [5, 9,  21, 25],
+            "CTS": [6, 10, 22, 26],
+            "RTS": [7, 11, 23, 27]
+       }
+    },
+    "I2C": {
+        "0": {
+            "SDA": [0, 4, 8,  12, 16, 20, 24, 28],
+            "SCL": [1, 5, 9,  13, 17, 21, 25, 29]
+       },
+       "1": {
+            "SDA": [2, 6, 10, 14, 18, 22, 26],
+            "SCL": [3, 7, 11, 15, 19, 23, 27]
+       }
+    },
+    "SPI": {
+        "0": {
+            "RX":  [0,  4,  16, 20],
+            "CSN": [1,  5,  17, 21],
+            "SCK": [2,  6,  18, 22],
+            "TX":  [3,  7,  19, 23]
+       },
+       "1": {
+            "RX":  [8,  12, 24, 28],
+            "CSN": [9,  13, 25, 29],
+            "SCK": [10, 14, 26],
+            "TX":  [11, 15, 27]
+       }
+    },
+    "PWM": {
+        "0": {
+            "A": [0,  16],
+            "B": [1,  17]
+        },
+        "1": {
+            "A": [2,  18],
+            "B": [3,  19]
+        },
+        "2": {
+            "A": [4,  20],
+            "B": [5,  21]
+        },
+        "3": {
+            "A": [6,  22],
+            "B": [7,  23]
+        },
+        "4": {
+            "A": [8,  24],
+            "B": [9,  25]
+        },
+        "5": {
+            "A": [10, 26],
+            "B": [11, 27]
+        },
+        "6": {
+            "A": [12, 28],
+            "B": [13, 29]
+        },
+        "7": {
+            "A": [14],
+            "B": [15]
+        }
+    }
+}
diff --git a/tools/check_all_board_headers.sh b/tools/check_all_board_headers.sh
new file mode 100755
index 0000000..bdbe3f7
--- /dev/null
+++ b/tools/check_all_board_headers.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+for HEADER in src/boards/include/boards/*.h; do
+    tools/check_board_header.py $HEADER
+    if [[ $? -ne 0 ]]; then
+      break
+    fi
+done
diff --git a/tools/check_board_header.py b/tools/check_board_header.py
new file mode 100755
index 0000000..23176aa
--- /dev/null
+++ b/tools/check_board_header.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2024 Raspberry Pi Ltd.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#
+# Simple script to check basic validity of a board-header-file
+#
+# Usage:
+#
+# tools/check_board_header.py src/boards/include/boards/<board.h>
+
+
+import re
+import sys
+import os.path
+import json
+import warnings
+
+from collections import namedtuple
+
+# warnings off by default, because some boards use the same pin for multiple purposes
+show_warnings = False
+
+interfaces_json = "src/rp2040/rp2040_interface_pins.json"
+if not os.path.isfile(interfaces_json):
+    raise Exception("{} doesn't exist".format(interfaces_json))
+
+board_header = sys.argv[1]
+if not os.path.isfile(board_header):
+    raise Exception("{} doesn't exist".format(board_header))
+
+with open(interfaces_json) as interfaces_fh:
+    interfaces = json.load(interfaces_fh)
+    # convert instance-keys to integers (allowed by Python but not by JSON)
+    for interface in interfaces:
+        for instance in list(interfaces[interface]):
+            interfaces[interface][int(instance)] = interfaces[interface].pop(instance)
+
+DefineType = namedtuple("DefineType", ["name", "value", "resolved_value", "lineno"])
+
+defines = dict()
+pins = dict() # dict of lists
+has_include_guard = False
+has_board_detection = False
+has_include_suggestion = False
+expected_include_suggestion = "/".join(board_header.split("/")[-2:])
+expected_include_guard = "_" + re.sub(r"\W", "_", expected_include_suggestion.upper())
+expected_board_detection = re.sub(r"\W", "_", expected_include_suggestion.split("/")[-1].upper()[:-2])
+
+with open(board_header) as header_fh:
+    last_ifndef = None
+    last_ifndef_lineno = -1
+    board_detection_is_next = False
+    for lineno, line in enumerate(header_fh.readlines()):
+        lineno += 1
+        # strip trailing comments
+        line = re.sub(r"(?<=\S)\s*//.*$", "", line)
+
+        # look for board-detection comment
+        if re.match("// For board detection", line):
+            board_detection_is_next = True
+            continue
+        # check include-suggestion
+        m = re.match(r"^// This header may be included by other board headers as \"(.+?)\"", line)
+        if m:
+            include_suggestion = m.group(1)
+            if include_suggestion == expected_include_suggestion:
+                has_include_suggestion = True
+            else:
+                raise Exception("{}:{}  Suggests including \"{}\" but file is named \"{}\"".format(board_header, lineno, include_suggestion, expected_include_suggestion))
+        # look for "#ifndef BLAH_BLAH"
+        m = re.match(r"^#ifndef (\w+)\s*$", line)
+        if m:
+            last_ifndef = m.group(1)
+            last_ifndef_lineno = lineno
+        # look for "#define BLAH_BLAH" or "#define BLAH_BLAH 42"
+        m = re.match(r"^#define (\w+)(?:\s+(.+?))?\s*$", line)
+        if m:
+            #print(m.groups())
+            name = m.group(1)
+            value = m.group(2)
+            # check all uppercase
+            if name != name.upper():
+                raise Exception("{}:{}  Expected \"{}\" to be all uppercase".format(board_header, lineno, name))
+            # check that adjacent #ifndef and #define lines match up
+            if last_ifndef_lineno + 1 == lineno:
+                if last_ifndef != name:
+                    raise Exception("{}:{}  #ifndef {} / #define {} mismatch".format(board_header, last_ifndef_lineno, last_ifndef, name))
+            if value:
+                try:
+                    # most board-defines are integer values
+                    value = int(value, 0)
+                except ValueError:
+                    pass
+
+                # resolve nested defines
+                resolved_value = value
+                while resolved_value in defines:
+                    resolved_value = defines[resolved_value].resolved_value
+            else:
+                resolved_value = None
+
+            define = DefineType(name, value, resolved_value, lineno)
+
+            # check the include-guard define
+            if re.match(r"^_BOARDS_(\w+)_H$", name):
+                # check it has an #ifndef
+                if last_ifndef_lineno +1 != lineno:
+                    raise Exception("{}:{}  Include-guard #define {} is missing an #ifndef".format(board_header, lineno, name))
+                if value:
+                    raise Exception("{}:{}  Include-guard #define {} shouldn't have a value".format(board_header, lineno, name))
+                if len(defines):
+                    raise Exception("{}:{}  Include-guard #define {} should be the first define".format(board_header, lineno, name))
+                if name == expected_include_guard:
+                    has_include_guard = True
+                else:
+                    raise Exception("{}:{}  Found include-guard #define {} but expected {}".format(board_header, lineno, name, expected_include_guard))
+            # check board-detection define
+            if board_detection_is_next:
+                board_detection_is_next = False
+                if value:
+                    raise Exception("{}:{}  Board-detection #define {} shouldn't have a value".format(board_header, lineno, name))
+                # this is a bit messy because pico.h does "#define RASPBERRYPI_PICO" and metrotech_xerxes_rp2040.h does "#define XERXES_RP2040"
+                if name.endswith(expected_board_detection) or expected_board_detection.endswith(name):
+                    has_board_detection = True
+                else:
+                    raise Exception("{}:{}  Board-detection #define {} should end with {}".format(board_header, lineno, name, expected_board_detection))
+            # check for multiply-defined values
+            if name in defines:
+                raise Exception("{}:{}  Multiple definitions for {} ({} and {})".format(board_header, lineno, name, defines[name].value, value))
+            else:
+                defines[name] = define
+
+            # check for pin-conflicts
+            if name.endswith("_PIN"):
+                if resolved_value is None:
+                    raise Exception("{}:{}  {} is set to an undefined value".format(board_header, lineno, name))
+                elif not isinstance(resolved_value, int):
+                    raise Exception("{}:{}  {} resolves to a non-integer value {}".format(board_header, lineno, name, resolved_value))
+                else:
+                    if resolved_value in pins and resolved_value == value:
+                        if show_warnings:
+                            warnings.warn("{}:{}  Both {} and {} claim to be pin {}".format(board_header, lineno, pins[resolved_value][0].name, name, resolved_value))
+                        pins[resolved_value].append(define)
+                    else:
+                        if not (0 <= resolved_value <= 29):
+                            raise Exception("{}:{}  Pin {} for {} is outside of the allowed range".foramt(board_header, lineno, resolved_value, name))
+                        pins[resolved_value] = [define]
+
+#import pprint; pprint.pprint(dict(sorted(defines.items(), key=lambda x: x[1].lineno)))
+
+# check for invalid DEFAULT mappings
+for name, define in defines.items():
+    m = re.match(r"^(PICO_DEFAULT_(\w+))_(\w+)_PIN$", name)
+    if m:
+        instance_name = m.group(1)
+        interface = m.group(2)
+        function = m.group(3)
+        if interface == "WS2812":
+            continue
+        if interface not in interfaces:
+            raise Exception("{}:{}  {} is defined but {} isn't in {}".format(board_header, define.lineno, name, interface, interfaces_json))
+        if instance_name not in defines:
+            raise Exception("{}:{}  {} is defined but {} isn't defined".format(board_header, define.lineno, name, instance_name))
+        instance_define = defines[instance_name]
+        instance = instance_define.resolved_value
+        if instance not in interfaces[interface]:
+            raise Exception("{}:{}  {} is set to an invalid instance {}".format(board_header, instance_define.lineno, instance_define, instance))
+        if function not in interfaces[interface][instance]:
+            raise Exception("{}:{}  {} is defined but {} isn't a valid function for {}".format(board_header, define.lineno, name, function, instance_define))
+        if define.resolved_value not in interfaces[interface][instance][function]:
+            raise Exception("{}:{}  {} is set to {} which isn't a valid pin for {} on {} {}".format(board_header, define.lineno, name, define.resolved_value, function, interface, instance))
+
+if not has_include_guard:
+    raise Exception("{} has no include-guard (expected {})".format(board_header, expected_include_guard))
+if not has_board_detection and expected_board_detection != "NONE":
+    raise Exception("{} has no board-detection #define (expected {})".format(board_header, expected_board_detection))
+# lots of headers don't have this
+#if not has_include_suggestion:
+#    raise Exception("{} has no include-suggestion (expected {})".format(board_header, expected_include_suggestion))
+
-- 
GitLab