Skip to content

tools/boardgen.py: Add a common library for ports to implement make-pins.py. #12211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Nov 3, 2023

Conversation

jimmo
Copy link
Member

@jimmo jimmo commented Aug 10, 2023

Each port currently has it's own complete implementation of make-pins.py. Historically the original implementation was done for stm32, and this has been copied and modified to 6 other ports (cc3200, teensy, nrf, mimxrt, rp2, renesas-ra), and then a brand-new from-scratch implementation was recently added for esp32.

They all do roughly the same things, but with some differences (e.g. hidden pins, output format, naming, argument parsing), and in some places there is unused code that hasn't been removed while adapting. Importantly, they duplicate a lot of code, and for someone implementing a new port it's difficult to see exactly the bits that are port-specific and the bits that don't need to be modified, and it's easy to make mistakes. I am in the process of adding a make-pins.py for the Zephyr port, which is why I started looking into this.

This PR does the following things:

  • Small fixes to the port makefiles to simplify the make-pins.py rules.
  • Remove the unnecessary generation of pin_qstr.h (pins.c is found in qstr discovery anyway), so this is not needed.
  • Remove the generation of pins_af.py (and the corresponding example that uses it). Not all ports supported this and it's not worth the complexity.
  • Make all versions of make-pins.py take the same common command line arguments, and output the same way (always to files, not to stdout).
  • Simplification to esp32 to make the irq object a subfield of pin.
  • Minor optimisation to rp2 to make the pin table contain the actual pins rather than pointers to pins.
  • Fix some errors in stm32 pin.csv and af.csv files.

And then the main work in this PR is to implement tools/boardgen.py which is a helper for ports to implement make-pins.py. It's named boardgen because I hope it can be expanded to do more than just pins. It supports numeric and named pins, as well as optional alternate function support.

Then the esp32 make-pins is updated to use this (chosen because it's the simplest make-pins.py), then rp2 (because it's quite similar to esp32, just with alternate functions), and then stm32 (because it's the most complex), and then the remaining ports: samd, mimxrt, renesas-ra. I have left out nrf, teensy, cc3200.

For stm32, the new version is a bit stricter so this uncovered a few issues with board pins.csv and af.csv files.

This work was funded through GitHub Sponsors.

@github-actions
Copy link

github-actions bot commented Aug 10, 2023

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
     mimxrt:  -292 -0.080% TEENSY40[incl -248(data)]
        rp2:   -88 -0.027% RPI_PICO
       samd:  -188 -0.072% ADAFRUIT_ITSYBITSY_M4_EXPRESS

@robert-hh
Copy link
Contributor

Yes, this make_pins stuff is pretty feral.
I have a unpublished PR for SAMD which swaps the fields in pins.csv, which is different ATM to the other ports. I can push that now, such that it is at least visible.

@codecov
Copy link

codecov bot commented Aug 10, 2023

Codecov Report

Merging #12211 (6ef9b29) into master (aa329d1) will not change coverage.
The diff coverage is n/a.

@@           Coverage Diff           @@
##           master   #12211   +/-   ##
=======================================
  Coverage   98.39%   98.39%           
=======================================
  Files         158      158           
  Lines       20973    20973           
=======================================
  Hits        20637    20637           
  Misses        336      336           

robert-hh added a commit to robert-hh/micropython that referenced this pull request Aug 13, 2023
- Rename make-pins-table.py to make-pins.py
- Name the input arguments --board-csv, --af-csv and --output-source.
- Use matching names for the internal symbols.

Signed-off-by: robert-hh <robert@hammelrath.com>
@robert-hh
Copy link
Contributor

@jimmo. I added a commit to PR #12212 to match the argument names of the make-pins.py equivalent to your PR here, and also renamed that script to make-pins.py.

@jimmo
Copy link
Member Author

jimmo commented Aug 13, 2023

@jimmo. I added a commit to PR #12212 to match the argument names of the make-pins.py equivalent to your PR here, and also renamed that script to make-pins.py.

Thanks for that @robert-hh , I will integrate that into this PR.

One question with SAMD, I notice the convention in this port for cpu pins up to 10 appears to be PA01 whereas on STM32 that would be PA1 (i.e. samd is adding the leading zero).

In general I think we should match the style of what the vendor calls them, but also I would like where possible for things to be as consistent as possible across ports, and of course I'm hesitant to break compatibility here. What should we do?

@robert-hh
Copy link
Contributor

I will integrate that into this PR.

This commit must be used with the first commit in the PR, which swaps the columns of the pins.csv files. Otherwise the builds will fail.

I notice the convention in this port for cpu pins up to 10 appears to be PA01

MicroChip uses consistently the 2-digit numbering for pins in their documents and library. See e.g. chapter 32.5.1 of the SAMD51 data sheet:
"The I/O lines of the PORT are mapped to pins of the physical device. The following naming scheme is used:
Each line bundle with up to 32 lines is assigned an identifier 'xy', with letter x=A, B, C… and two-digit number y=00, 01, …31. Examples: A24, C03.
PORT pins are labeled 'Pxy' accordingly, for example PA24, PC03. This identifies each pin in the device uniquely.
"
The board vendors do the same in their schematics, when they denote MCU pins. So I think we should stick to it.

@jimmo
Copy link
Member Author

jimmo commented Aug 13, 2023

So I think we should stick to it.

OK, makes sense to me! Thanks @robert-hh

@dpgeorge dpgeorge added the tools Relates to tools/ directory in source, or other tooling label Aug 15, 2023
@jimmo jimmo force-pushed the make-pins-refactor branch 3 times, most recently from 4b77dc2 to 3740647 Compare August 16, 2023 08:11
@jimmo
Copy link
Member Author

jimmo commented Aug 16, 2023

@robert-hh I've updated this PR with samd and mimxrt using boardgen.py now.

@jimmo jimmo force-pushed the make-pins-refactor branch 2 times, most recently from e94386c to 4bb2bb5 Compare August 16, 2023 09:05
@robert-hh
Copy link
Contributor

Getting lost between 2 PRs. What I wanted to say:

You digged up quite a bit of the code, which was grown over time and may have needed some clean-up. From a distance, things look sometimes more clear. I made a trial build for a SAMD51 & SAMD21 board and noticed, that the the element name in machine_pin_obj_t is not properly assigned any more. It used to be the board pin name or '-', if no name is assigned. Now it is the CPU pin name, meaning that the function pin_name() returns the wrong value.

@robert-hh
Copy link
Contributor

robert-hh commented Aug 16, 2023

Teensy 4.1, SEEED_ARCH_MIX and MIMXRT1176_EVK do not build. And OLIMEX_RT1010 and ADAFRUIT_METRO_M7 still have the UART RTS/CTS bug (not your playground).

Edit: I see. The respective Pin definitions with the leading dash are missing in pins.csv

@jimmo jimmo force-pushed the make-pins-refactor branch from 4bb2bb5 to ff23155 Compare August 16, 2023 12:38
@jimmo
Copy link
Member Author

jimmo commented Aug 16, 2023

the element name in machine_pin_obj_t is not properly assigned any more. It used to be the board pin name or '-', if no name is assigned. Now it is the CPU pin name, meaning that the function pin_name() returns the wrong value.

That was intentional, and it's how it behaves on other ports. e.g. on stm32:

>>> machine.Pin.board.LED_RED
Pin(Pin.cpu.B1, mode=Pin.OUT)

I've updated the samd printing to match the same style. FYI you can use %q as a format specifier with mp_printf to directly print a qstr, so the pin_name function converting to char* wasn't necessary.

I agree it's neat that the pin knew its board name, but I'd rather make this consistent.

Teensy 4.1, SEEED_ARCH_MIX and MIMXRT1176_EVK do not build.

The way pins.csv works on other ports is that unless a pin is in pins.csv it doesn't get generated. (With the exception of rp2 and esp32 which always generate the fixed set of numeric cpu pins).

But to keep "pin to board" mapping centralised, this is also supported by pins.csv even for pins that will not be made available to Python. This is why the stm32 pins.csv (and now everything using boardgen.py) supports "hidden" pins. The idea is that you can add, e.g.

-ENET_RESET,-GPIO_LPSR_12

to pins.csv to make pin_ENET_RESET available (pointing to pin_GPIO_LPSR_12_obj), but neither will be in Pin.board/Pin.cpu. So this means eth.c can use pin_ENET_RESET, removing the defines in mpconfigboard.h.

Anyway, I had already fixed this for the other boards, but missed TEENSY41 and missed the 1G eth pins for MIMXRT1176_EVK.

Similarly for the USDHC1 pins, these also needed to be added to pins.csv.

I've patched in the changes from the RTS/CTS PR and now all boards are compiling.

@jimmo jimmo force-pushed the make-pins-refactor branch from ff23155 to 4d7c131 Compare August 16, 2023 12:41
@robert-hh
Copy link
Contributor

That was intentional, and it's how it behaves on other ports. e.g. on stm32:

I do not agree to that change. The name field is intended to show the board pin name, so that the Pin print shows both the board name and the CPU name. Otherwise the field itself is obsolete, because the cpu name and the pin id are directly related. And since it's more likely that people use the board name for pins, it should be shown when devices properties are printed.

@robert-hh
Copy link
Contributor

The changes for the MIMXRT boards seems fine. The three boards that did not build before build and Ethernet works. The info about the hidden pins is useful.

I agree it's neat that the pin knew its board name

From a usage perspective it's essential, not neat. People should be able to tell the pin name that is shown by the a board API without looking into information which exists somewhere.

@jimmo
Copy link
Member Author

jimmo commented Aug 16, 2023

People should be able to tell the pin name that is shown by the a board API without looking into information which exists somewhere.

I agree this is useful, but if we're going to do this then I think we need to agree to do it on all ports, and currently none of samd, stm32, rp2, esp32, nrf show the board name. There are some other minor considerations, for example it is technically possible for a cpu pin to have multiple board aliases.

@robert-hh
Copy link
Contributor

currently none of samd, stm32, rp2, esp32, nrf show the board name.

samd does.
Anyhow. I agree that it is required to have a common baseline between ports. But the mapping between a pins cpu name and the board name is a one way road. People look at the board and see the board name. So there should be a way back to get the board name by a pin object or pin id. For the SAMD port there is the samd.pininfo() method, which returns the board name and the pin af vector and which is used by the pininfo.py script to print a pin assignment table. People found that very useful when actually using the board. For that, a way is needed to return the board name of a pin e.g. from the pin.board dictionary.

@robert-hh
Copy link
Contributor

robert-hh commented Aug 16, 2023

Another thing that is missing: when testing with the SAMD port i noticed, that pins which are not assigned to a board name are missing in the Pin.cpu list. At the moment they are present and it is useful to have them.

Same for MIMXRT.

Pin.cpu must contain all Pins of the MCU. The respective pin_af.csv tables list all possible pins. So that must be used for creating Pin.cpu.

@jimmo
Copy link
Member Author

jimmo commented Aug 17, 2023

Pin.cpu must contain all Pins of the MCU. The respective pin_af.csv tables list all possible pins. So that must be used for creating Pin.cpu.

This change is also for compatibility with the other ports. I realise that there isn't an easily definable "common behavior" because there is so much variation in each of the make-pins.py implementations, so to be clear, wherever there's a discrepancy I'm using stm32 as the reference implementation.

The idea is that if a pin isn't in the board pins.csv, then it's not "on the board" and therefore there would be no way to access it and no reason to make it available. "on the board" doesn't just mean "available on a header" though, it's more that the pin is routed somewhere on the board and has some function.

And when it comes to making it available, the "hidden" pins feature allows it to be just made available to C (e.g. the ETH_RESET pins discussed earlier are a good example). CPU pins also don't have to be given board aliases -- e.g. ,PA1 is a valid row in pins.csv.

One obvious reason for doing this is code size -- there's no point generating machine_pin_obj_t instances in ROM for every possible CPU pin if we know it's not used anywhere.

So with the example you ran into with SAMD testing, the intention is that the pin should have been in pins.csv (possibly without a board name). Is that reasonable? Or is there another scenario I'm missing?

FWIW, on stm32, not all board definitions get this right, so some clean-up here is in order too. For example there are many boards with pins.csv that just list every possible pin as PAxy,PAxy, which is pointless (adding the CPU names to Pin.board). Although many boards do list the CPU name on the silkscreen...so you could argue this is correct in some cases (but I think when a pin is clearly the CPU name, it should not be in Pin.board).

There are two exceptions to this on existing ports -- esp32 and rp2, where we added named pin support after board definition support, and so we have boards that haven't been updated with pins.csv files. So for now all CPU pins are enabled. But this should be fixed once we have pins.csv for all boards. ESP32 also doesn't have the Pin.cpu dict at all.

I really feel that consistency is the most important thing. Once everything is using boardgen.py, we can make changes as necessary in one place that apply to all ports.

@robert-hh
Copy link
Contributor

"on the board" doesn't just mean "available on a header" though, it's more that the pin is routed somewhere on the board and has some function.

I verified for the SAMD and MIMXRT port that one can simply add all pins from the pin_af.csv file to pins.csv and get back a complete list of usable CPU pins into Pin.cpu. Usable CPU pins could both be pins that are routed to the board, or pins that are just internal to the MCU package but have a function, like those connected to an in-package peripheral. Adding that to each pins.csv file is possible, but lacks elegance. An option would be an include mechanism or a addition cpu_pins.csv file for common subsets.

there's no point generating machine_pin_obj_t instances in ROM for every possible CPU pin if we know it's not used anywhere.

How can you know, what users will use, how users will use boards? We should not patronize users.

wherever there's a discrepancy I'm using stm32 as the reference implementation.

The STM32 port has many excellent features implemented. But it carries with it a lot of legacy and history, that may not all be useful for getting a new common structure. I prefer to use the RP2 port as a reference. It is way more consistent and clear. Obviously it's for a single MCU only, which makes it easier to read and maintain.

Once everything is using boardgen.py, we can make changes as necessary in one place that apply to all ports.

I do not like to have a state which breaks things that worked before, just to eventually bring them back sometime later, if a PR for it is made and if it will be processed.

@andrewleech
Copy link
Contributor

there's no point generating machine_pin_obj_t instances in ROM for every possible CPU pin if we know it's not used anywhere.

How can you know, what users will use, how users will use boards? We should not patronize users.

In my understanding of this there's a difference between what a CPU can provide and what a user can physically get access to.

A solid example that comes to mind is the Seeed Wio-E5 Wireless Module, from a micropython point of view this is just a STM32WL chip and SX126X Lora radio. It's a sealed can module, with only a small subset of the STM pins routed to the outside world and internal lora transceiver.
I definitely think it makes sense on a module (or board using this module) to only expose pins that are routed out of the module / used internally for radio, any others are just consfusing noise taking up flash space.

On a more generic front, if a board only has some CPU pins routed from the chip to peripherals / headers / test-points with the rest not even having tracks laid, I'd prefer to not have those blank pins included by default.
If I'm going to take a board like that and dead-bug wire some other parts onto those cpu pins, well I'm also happy to custom compile my firmware as it's no longer matching that board definition.

@robert-hh
Copy link
Contributor

In my understanding of this there's a difference between what a CPU can provide and what a user can physically get access to.

Physical access is not the only aspect. It should include the aspect of what user code can use, even if a CPU pin is not routed on or to the board. Two examples:

  • At the SEEED XIAO SAMD21 the PA03 is not routed to the board edge. PA03 is the AREF pin, and kept floating. That makes using the ADC a little bit tricky. But since it is available as CPU pin, one set set it to high level and have a AREF voltage applied at the pin.
  • The MIMXRT PWM allows to synchronize PWM channels to another specific channel of a PWM module. These specific channels may not be routed to a board connector, but that's not required to use the sync mechanism. They must just be available to the code as Pin object.
    The may be more examples of situation, where a CPU pin can be used even if not routed to the board connectors or used on the board. Not all people a willing or able to build their own firmware in case they miss a pin definition. So why make things more complicated and restrictive? Pin.board may list only the labelled pins and dedicated pins on the board. But Pin.cpu should have all that can be used by code.

@jimmo
Copy link
Member Author

jimmo commented Nov 2, 2023

Let's keep the discussion about the ADCs in #12706. I've replicated @iabdalkader's comment above there.

jimmo added 20 commits November 3, 2023 13:57
This prevents each port Makefile from having to add an explicit rule for
`build-BOARD/pins_BOARD.c`.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Also remove af-const header, as this is left over from the STM32 version
and unused.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Qstrs are picked up from the generated pin source files in the usual qstr
processing stage.

Similarly for the stm constant qstrs.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
The output pins.c can be processed for qstrs like any other C file.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
The output pins.c can be processed for qstrs like any other C file.

Also remove af_const from Makefile (unimplemented in make-pins.py) and fix
target dependency on ad_const.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
The output pins.c can be processed for qstrs like any other C file.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
It's not supported on all ports, adds complexity to the build to generate
pins_af.py, and can mostly be replicated just by printing the pin objects.

Remove support for generating pins_af.py from all ports (nrf, stm32,
renesas-ra, mimxrt, rp2).

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
It's unused.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
All ports now use `--board-csv`, `--prefix`, `--output-souce`,
`--output-header` and no longer write to stdout.  This matches the esp32
implementation.

Ports that have an AF input use `--af-csv` (to match `--board-csv`).

Any additional output files are now prefixed with `output-` (e.g.
`--output-af-const`).

Default arguments are removed (all makefiles should always specify all
arguments, using default values is likely an error).

Replaced the `af-defs-cmp-strings` and `hdr-obj-decls` args for stm32 with
just `mboot-mode`.  Previously they were set on the regular build, now the
logic is reversed so mboot sets it.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This simplifies pin generation.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
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: Jim Mussared <jim.mussared@gmail.com>
Fixes are:
- Comment out lines in pins.csv that do not have valid CPU pins.
  It's useful to keep these in the file as "documentation" but in order to
  make make-pins.py stricter they need to be commented out.
- Fix some typos (missing P prefix) in pins.csv.
  This resulted in some missing board pins.
- Fix some typos in af.csv files.
  Some typos of "ADC" and some other that were previously ignored.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Changes are:
- Pad all cells to make them easier to read.
- Ensure all files have exactly 19 columns (Port,Pin,AF0-15,ADC)

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This is now consistent with other ports.

Also renamed `pin_{board/cpu}_pins_locals_dict` to
`machine_pin_{board/cpu}_pins_locals_dict`.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Requires additions to tools/boardgen.py for stm32 pin generation.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This fixes the H7 af.csv files to include the dual-pad information, by
listing the ADCs supported on the _C pad with a C_ADC prefix.

Minimal change to make-pins.py to ignore these entries. This will be
implemented later to emit constants (similar to ADC.CORE_TEMP) to access
these channels.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This replaces the previous make-pin-table.py with an implementation based
on boardgen.py.

- MICROPY_PY_MACHINE_PIN_BOARD_CPU macro is removed. This isn't optional
  on other ports, so no need for it to be optional on SAMD.
- pin_af_table is removed, and lookups just search the cpu dict instead
  (this saves N*wordsize bytes of firmware size to have this extra table).
- pins.csv is now BOARD,CPU to match other ports.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Minor change to remove support for using numeric IDs for machine.Pin.  This
was previously based on the index of the pin in the board csv, but this is
different (and incompatible) with other ports.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This removes previously unused functionality to generate pins_ad_const.h,
as well as the unused handling of pin AF in machine_pin.c.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
It's not worth the effort to update these ports to use boardgen.py, but
put a note just in case anyone uses this as a reference for a new port.

This work was funded through GitHub Sponsors.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
@dpgeorge dpgeorge merged commit 6ef9b29 into micropython:master Nov 3, 2023
@dpgeorge
Copy link
Member

dpgeorge commented Nov 3, 2023

Merged!

Any remaining inconsistencies/regressions can be dealt with separately.

robert-hh added a commit to robert-hh/micropython that referenced this pull request Nov 4, 2023
This option was remove in PR micropython#12211.

Signed-off-by: robert-hh <robert@hammelrath.com>
dpgeorge pushed a commit to robert-hh/micropython that referenced this pull request Nov 4, 2023
This option was removed in PR micropython#12211.

Signed-off-by: robert-hh <robert@hammelrath.com>
graeme-winter pushed a commit to winter-special-projects/micropython that referenced this pull request Sep 21, 2024
This option was removed in PR micropython#12211.

Signed-off-by: robert-hh <robert@hammelrath.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ports Relates to multiple ports, or a new/proposed port tools Relates to tools/ directory in source, or other tooling
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants