Commit 4bd6ec9a authored by Jim Mussared's avatar Jim Mussared Committed by Damien George

tools/boardgen.py: Add initial implementation of a common make-pins.py.

For now, this implements the functionality required for esp32 and rp2,
including support for numeric pins, rp2 alternate functions, and rp2
extended pins.

This also updates the rp2 port to use the same structure for pins.h and
pins.csv as for esp32, and moves the pin definitions directly into the
table (rather than having a table of pointers), which is a small code size
improvement.

Support for "hidden" pins in pins.csv is added (matching the stm32
implementation).

This work was funded through GitHub Sponsors.
Signed-off-by: default avatarJim Mussared <jim.mussared@gmail.com>
parent b0aec6a0
#!/usr/bin/env python
import argparse
import os
import sys
import csv
import re
MAX_CPU_PINS = 49
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../../tools"))
import boardgen
def parse_pin(name_str):
"""Parses a string and returns a pin number."""
if len(name_str) < 2:
raise ValueError("Expecting pin name to be at least 2 characters.")
if not name_str.startswith("GPIO"):
raise ValueError("Expecting pin name to start with GPIO")
return int(re.findall(r"\d+$", name_str)[0])
# Pins start at zero, and the highest pin index on any ESP32* chip is 48.
NUM_GPIOS = 49
class Pin:
def __init__(self, pin):
self.pin = pin
self.is_board = False
class Esp32Pin(boardgen.Pin):
# Required by NumericPinGenerator.
def index(self):
return int(self._cpu_pin_name[4:])
def cpu_pin_name(self):
return "GPIO{:d}".format(self.pin)
# The IDF provides `GPIO_NUM_x = x` as an enum.
def index_name(self):
return "GPIO_NUM_{:d}".format(self.index())
def is_board_pin(self):
return self.is_board
# Emit the combined struct which contains both the pin and irq instances.
def definition(self):
return "{ .base = { .type = &machine_pin_type }, .irq = { .base = { .type = &machine_pin_irq_type } } }"
def set_is_board_pin(self):
self.is_board = True
# This script isn't family-aware, so we always emit the maximum number of
# pins and rely on the `MICROPY_HW_ENABLE_GPIOn` macros defined in
# machine_pin.h to figure out which ones are actually available.
def enable_macro(self):
return "MICROPY_HW_ENABLE_{}".format(self._cpu_pin_name)
# ESP32 cpu names must be "GPIOn".
@staticmethod
def validate_cpu_pin_name(cpu_pin_name):
boardgen.Pin.validate_cpu_pin_name(cpu_pin_name)
class NamedPin:
def __init__(self, name, pin):
self._name = name
self._pin = pin
def pin(self):
return self._pin
def name(self):
return self._name
class Pins:
def __init__(self):
self.cpu_pins = [] # list of NamedPin objects
self.board_pins = [] # list of NamedPin objects
def find_pin(self, pin_name):
for pin in self.cpu_pins:
if pin.name() == pin_name:
return pin.pin()
def create_pins(self):
for pin_num in range(MAX_CPU_PINS):
pin = Pin(pin_num)
self.cpu_pins.append(NamedPin(pin.cpu_pin_name(), pin))
def parse_board_file(self, filename):
with open(filename, "r") as csvfile:
rows = csv.reader(csvfile)
for row in rows:
if len(row) == 0 or row[0].startswith("#"):
# Skip empty lines, and lines starting with "#"
continue
if len(row) != 2:
raise ValueError("Expecting two entries in a row")
cpu_pin_name = row[1]
parse_pin(cpu_pin_name)
pin = self.find_pin(cpu_pin_name)
if not pin:
raise ValueError("Unknown pin {}".format(cpu_pin_name))
pin.set_is_board_pin()
if row[0]: # Only add board pins that have a name
self.board_pins.append(NamedPin(row[0], pin))
def print_cpu_table(self, out_source):
print("", file=out_source)
print(
"const machine_pin_obj_t machine_pin_obj_table[GPIO_NUM_MAX] = {",
file=out_source,
)
for pin in self.cpu_pins:
print(" #if MICROPY_HW_ENABLE_{}".format(pin.name()), file=out_source)
print(
" [GPIO_NUM_{}] = {{ .base = {{ .type = &machine_pin_type }}, .irq = {{ .base = {{ .type = &machine_pin_irq_type }} }} }},".format(
pin.pin().pin,
),
file=out_source,
)
print(" #endif", file=out_source)
print("};", file=out_source)
def print_named(self, label, named_pins, out_source):
print("", file=out_source)
print(
"STATIC const mp_rom_map_elem_t machine_pin_{:s}_pins_locals_dict_table[] = {{".format(
label
),
file=out_source,
)
for named_pin in named_pins:
pin = named_pin.pin()
print(
" {{ MP_ROM_QSTR(MP_QSTR_{:s}), MP_ROM_PTR(&pin_{:s}) }},".format(
named_pin.name(), pin.cpu_pin_name()
),
file=out_source,
if not cpu_pin_name.startswith("GPIO") or not cpu_pin_name[4:].isnumeric():
raise boardgen.PinGeneratorError(
"Invalid cpu pin name '{}', must be 'GPIOn'".format(cpu_pin_name)
)
print("};", file=out_source)
print(
"MP_DEFINE_CONST_DICT(machine_pin_{:s}_pins_locals_dict, machine_pin_{:s}_pins_locals_dict_table);".format(
label, label
),
file=out_source,
)
def print_tables(self, out_source):
self.print_cpu_table(out_source)
self.print_named("board", self.board_pins, out_source)
def print_header(self, out_header):
# Provide #defines for each cpu pin.
for named_pin in self.cpu_pins:
pin = named_pin.pin()
n = pin.cpu_pin_name()
print("#if MICROPY_HW_ENABLE_{}".format(n), file=out_header)
print(
"#define pin_{:s} (machine_pin_obj_table[{}])".format(n, pin.pin),
file=out_header,
)
print("#endif", file=out_header)
# Provide #define's mapping board to cpu name.
for named_pin in self.board_pins:
if named_pin.pin().is_board_pin():
print(
"#define pin_{:s} pin_{:s}".format(
named_pin.name(), named_pin.pin().cpu_pin_name()
),
file=out_header,
)
if not (0 <= int(cpu_pin_name[4:]) < NUM_GPIOS):
raise boardgen.PinGeneratorError("Unknown cpu pin '{}'".format(cpu_pin_name))
def main():
parser = argparse.ArgumentParser(description="Generate board specific pin file")
parser.add_argument("--board-csv")
parser.add_argument("--prefix")
parser.add_argument("--output-source")
parser.add_argument("--output-header")
args = parser.parse_args()
pins = Pins()
pins.create_pins()
with open(args.output_source, "w") as out_source:
print("// This file was automatically generated by make-pins.py", file=out_source)
print("//", file=out_source)
if args.board_csv:
print("// --board-csv {:s}".format(args.board_csv), file=out_source)
pins.parse_board_file(args.board_csv)
class Esp32PinGenerator(boardgen.NumericPinGenerator):
def __init__(self):
# Use custom pin type above.
super().__init__(pin_type=Esp32Pin)
if args.prefix:
print("// --prefix {:s}".format(args.prefix), file=out_source)
print("", file=out_source)
with open(args.prefix, "r") as prefix_file:
print(prefix_file.read(), end="", file=out_source)
# Pre-define the pins (i.e. don't require them to be listed in pins.csv).
for i in range(NUM_GPIOS):
self.add_cpu_pin("GPIO{}".format(i))
pins.print_tables(out_source)
# Only use pre-defined cpu pins (do not let board.csv create them).
def find_pin_by_cpu_pin_name(self, cpu_pin_name, create=True):
return super().find_pin_by_cpu_pin_name(cpu_pin_name, create=False)
with open(args.output_header, "w") as out_header:
pins.print_header(out_header)
# This is provided by the IDF and is one more than the highest available
# GPIO num.
def cpu_table_size(self):
return "GPIO_NUM_MAX"
if __name__ == "__main__":
main()
Esp32PinGenerator().main()
......@@ -479,7 +479,6 @@ set(GEN_PINS_PREFIX "${MICROPY_BOARDS_DIR}/rp2_prefix.c")
set(GEN_PINS_MKPINS "${MICROPY_BOARDS_DIR}/make-pins.py")
set(GEN_PINS_SRC "${CMAKE_BINARY_DIR}/pins_${MICROPY_BOARD}.c")
set(GEN_PINS_HDR "${MICROPY_GENHDR_DIR}/pins.h")
set(GEN_PINS_AF_CONST "${MICROPY_GENHDR_DIR}/pins_af_const.h")
if(EXISTS "${MICROPY_BOARDS_DIR}/${MICROPY_BOARD}/pins.csv")
set(GEN_PINS_BOARD_CSV "${MICROPY_BOARDS_DIR}/${MICROPY_BOARD}/pins.csv")
......
This diff is collapsed.
......@@ -69,14 +69,14 @@ MP_DEFINE_CONST_OBJ_TYPE(
pin_cpu_pins_obj_type,
MP_QSTR_cpu,
MP_TYPE_FLAG_NONE,
locals_dict, &pin_cpu_pins_locals_dict
locals_dict, &machine_pin_cpu_pins_locals_dict
);
MP_DEFINE_CONST_OBJ_TYPE(
pin_board_pins_obj_type,
MP_QSTR_board,
MP_TYPE_FLAG_NONE,
locals_dict, &pin_board_pins_locals_dict
locals_dict, &machine_pin_board_pins_locals_dict
);
typedef struct _machine_pin_irq_obj_t {
......@@ -86,7 +86,7 @@ typedef struct _machine_pin_irq_obj_t {
} machine_pin_irq_obj_t;
STATIC const mp_irq_methods_t machine_pin_irq_methods;
extern const machine_pin_obj_t *machine_pin_cpu_pins[NUM_BANK0_GPIOS];
extern const machine_pin_obj_t machine_pin_obj_table[NUM_BANK0_GPIOS];
// Mask with "1" indicating that the corresponding pin is in simulated open-drain mode.
uint32_t machine_pin_open_drain_mask;
......@@ -170,18 +170,18 @@ const machine_pin_af_obj_t *machine_pin_find_alt_by_index(const machine_pin_obj_
const machine_pin_obj_t *machine_pin_find(mp_obj_t pin) {
// Is already a object of the proper type
if (mp_obj_is_type(pin, &machine_pin_type)) {
return pin;
return MP_OBJ_TO_PTR(pin);
}
if (mp_obj_is_str(pin)) {
const char *name = mp_obj_str_get_str(pin);
// Try to find the pin in the board pins first.
const machine_pin_obj_t *self = machine_pin_find_named(&pin_board_pins_locals_dict, pin);
const machine_pin_obj_t *self = machine_pin_find_named(&machine_pin_board_pins_locals_dict, pin);
if (self != NULL) {
return self;
}
// If not found, try to find the pin in the cpu pins which include
// CPU and and externally controlled pins (if any).
self = machine_pin_find_named(&pin_cpu_pins_locals_dict, pin);
self = machine_pin_find_named(&machine_pin_cpu_pins_locals_dict, pin);
if (self != NULL) {
return self;
}
......@@ -189,8 +189,8 @@ const machine_pin_obj_t *machine_pin_find(mp_obj_t pin) {
} else if (mp_obj_is_int(pin)) {
// get the wanted pin object
int wanted_pin = mp_obj_get_int(pin);
if (0 <= wanted_pin && wanted_pin < MP_ARRAY_SIZE(machine_pin_cpu_pins)) {
return machine_pin_cpu_pins[wanted_pin];
if (0 <= wanted_pin && wanted_pin < MP_ARRAY_SIZE(machine_pin_obj_table)) {
return &machine_pin_obj_table[wanted_pin];
}
}
mp_raise_ValueError("invalid pin");
......@@ -432,7 +432,7 @@ STATIC machine_pin_irq_obj_t *machine_pin_get_irq(mp_hal_pin_obj_t pin) {
irq = m_new_obj(machine_pin_irq_obj_t);
irq->base.base.type = &mp_irq_type;
irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods;
irq->base.parent = MP_OBJ_FROM_PTR(machine_pin_cpu_pins[pin]);
irq->base.parent = MP_OBJ_FROM_PTR(&machine_pin_obj_table[pin]);
irq->base.handler = mp_const_none;
irq->base.ishard = false;
MP_STATE_PORT(machine_pin_irq_obj[pin]) = irq;
......
......@@ -65,10 +65,10 @@ extern const mp_obj_type_t machine_pin_af_type;
#include "genhdr/pins.h"
extern const mp_obj_type_t pin_cpu_pins_obj_type;
extern const mp_obj_dict_t pin_cpu_pins_locals_dict;
extern const mp_obj_dict_t machine_pin_cpu_pins_locals_dict;
extern const mp_obj_type_t pin_board_pins_obj_type;
extern const mp_obj_dict_t pin_board_pins_locals_dict;
extern const mp_obj_dict_t machine_pin_board_pins_locals_dict;
void machine_pin_ext_init(void);
bool machine_pin_ext_is_adc_channel(const machine_pin_obj_t *self);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment