Skip to content

Commit 008ae93

Browse files
committed
add board summaries
1 parent 5cc8d44 commit 008ae93

File tree

2 files changed

+275
-22
lines changed

2 files changed

+275
-22
lines changed

docs/board_summaries.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# The MIT License (MIT)
2+
#
3+
# SPDX-FileCopyrightText: Copyright (c) 2024 Tim Chinowsky
4+
#
5+
# Permission is hereby granted, free of charge, to any person obtaining a copy
6+
# of this software and associated documentation files (the "Software"), to deal
7+
# in the Software without restriction, including without limitation the rights
8+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
# copies of the Software, and to permit persons to whom the Software is
10+
# furnished to do so, subject to the following conditions:
11+
#
12+
# The above copyright notice and this permission notice shall be included in
13+
# all copies or substantial portions of the Software.
14+
#
15+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
# THE SOFTWARE.
22+
#
23+
24+
import re
25+
from shared_bindings_matrix import support_matrix_by_board
26+
27+
28+
def module_incidence_matrix_csvs(support_matrix, rows=1000, present="1", absent=""):
29+
all_modules = set()
30+
for info in support_matrix.values():
31+
all_modules.update(info["modules"])
32+
all_modules = sorted(all_modules)
33+
csvs = []
34+
row = 0
35+
for board, info in support_matrix.items():
36+
if (row % rows) == 0:
37+
csv = ["board,branded_name,mcu,flash,port,pins," +
38+
','.join(all_modules) + '\n']
39+
chip_pin_set = set([chip_pin for _, chip_pin in info["pins"]])
40+
n_chip_pins = len(chip_pin_set)
41+
module_incidence = [present if m in info["modules"]
42+
else absent for m in all_modules]
43+
line = f"{board},{info.get('branded_name')},{info.get('mcu')}," + \
44+
f"{info.get('flash')},{info.get('port')},{n_chip_pins}," + \
45+
','.join(module_incidence) + '\n'
46+
csv.append(line)
47+
row += 1
48+
if (row % rows) == 0:
49+
csvs.append(csv)
50+
if (row % rows) != 0:
51+
csvs.append(csv)
52+
return csvs
53+
54+
55+
def frozen_incidence_matrix_csvs(support_matrix, rows=1000, present="1", absent=""):
56+
all_frozen = set()
57+
for info in support_matrix.values():
58+
frozen = info["frozen_libraries"]
59+
frozen = [f[0] if type(f) == tuple else f for f in frozen]
60+
all_frozen.update(frozen)
61+
all_frozen = sorted(all_frozen)
62+
csvs = []
63+
row = 0
64+
for board, info in support_matrix.items():
65+
if (row % rows) == 0:
66+
csv = ["board,branded_name,mcu,flash,port,pins," +
67+
",".join(all_frozen) + "\n"]
68+
# remove urls if present
69+
frozen = info["frozen_libraries"]
70+
71+
chip_pin_set = set([chip_pin for _, chip_pin in info["pins"]])
72+
n_chip_pins = len(chip_pin_set)
73+
74+
frozen = [f[0] if type(f) == tuple else f for f in frozen]
75+
frozen_incidence = [present if f in frozen else absent for f in all_frozen]
76+
line = f"{board},{info.get('branded_name')},{info.get('mcu')}," + \
77+
f"{info.get('flash')},{info.get('port')},{n_chip_pins}," + \
78+
",".join(frozen_incidence) + '\n'
79+
csv.append(line)
80+
row += 1
81+
if (row % rows) == 0:
82+
csvs.append(csv)
83+
if (row % rows) != 0:
84+
csvs.append(csv)
85+
return csvs
86+
87+
88+
def summarize_pins(pins):
89+
"""Given a list of pin names, generate a compact string
90+
like "BUTTON, LED1:4, PA0:10;13:14, PB5:7, SCL, SDA"
91+
summarizing the names in the list"""
92+
pin_prefixes = {}
93+
for p in pins:
94+
match = re.match(r'^(.*?)(\d*)$', p)
95+
if match:
96+
prefix = match.group(1)
97+
n_str = match.group(2)
98+
else:
99+
raise ValueError("Cannot parse pin name")
100+
if prefix in pin_prefixes :
101+
pin_prefixes[prefix].add(n_str)
102+
else:
103+
pin_prefixes[prefix] = {n_str}
104+
105+
return ', '.join([f"{prefix}{span_string(pin_prefixes[prefix])}"
106+
for prefix in sorted(pin_prefixes.keys())])
107+
108+
109+
def int_or_zero(s):
110+
try:
111+
return int(s)
112+
except ValueError:
113+
return 0
114+
115+
116+
def span_string(number_strs):
117+
int_strs = sorted([(int_or_zero(s), s) for s in number_strs])
118+
last_i = None
119+
last_s = None
120+
run = False
121+
for i, s in int_strs:
122+
if last_i is None:
123+
out = s
124+
elif i != last_i + 1:
125+
if run:
126+
out += f":{last_s};{s}"
127+
else:
128+
out += f";{s}"
129+
run = False
130+
else:
131+
run = True
132+
last_i = i
133+
last_s = s
134+
if run:
135+
out += f":{s}"
136+
return out
137+
138+
139+
def board_pins_matrix_csvs(support_matrix, rows=1000):
140+
csvs = []
141+
row = 0
142+
for board, info in support_matrix.items():
143+
if (row % rows) == 0:
144+
csv = ["board,branded_name,mcu,flash,port,n_board_pins,"
145+
"board_pins,n_chip_pins,chip_pins\n"]
146+
board_pins = [board_pin for board_pin, _ in info["pins"]]
147+
chip_pins = [chip_pin for _, chip_pin in info["pins"]]
148+
line = f"{board},{info.get('branded_name')},{info.get('mcu')}," + \
149+
f"{info.get('flash')},{info.get('port')}," + \
150+
str(len(set(board_pins))) + ',"' + summarize_pins(board_pins) + '",' + \
151+
str(len(set(chip_pins))) + ',"' + summarize_pins(chip_pins) + '"\n'
152+
csv.append(line)
153+
row += 1
154+
if (row % rows) == 0:
155+
csvs.append(csv)
156+
if (row % rows) != 0:
157+
csvs.append(csv)
158+
return csvs
159+
160+
161+
def write_csvs(rows=1000, present="1", absent="0"):
162+
print('generating csvs...')
163+
s = support_matrix_by_board(use_branded_name=False, add_port=True,
164+
add_chips=True, add_pins=True,
165+
add_branded_name=True)
166+
csvs = {"modules": module_incidence_matrix_csvs(s, rows=rows,
167+
present=present, absent=absent),
168+
"frozen": frozen_incidence_matrix_csvs(s, rows=rows,
169+
present=present, absent=absent),
170+
"pins": board_pins_matrix_csvs(s, rows=rows)}
171+
for key in csvs:
172+
for i in range(len(csvs[key])):
173+
filename = f"{key}_{i}.csv"
174+
print(f'writing {filename}')
175+
with open(filename, "w") as f:
176+
f.writelines(csvs[key][i])
177+
178+
179+
if __name__ == "__main__":
180+
write_csvs()

docs/shared_bindings_matrix.py

Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@
2626
import pathlib
2727
import re
2828
import subprocess
29-
import sys
30-
import functools
29+
3130

3231
from concurrent.futures import ThreadPoolExecutor
3332

@@ -57,7 +56,8 @@
5756

5857
ALIASES_BRAND_NAMES = {
5958
"circuitplayground_express_4h": "Adafruit Circuit Playground Express 4-H",
60-
"circuitplayground_express_digikey_pycon2019": "Circuit Playground Express Digi-Key PyCon 2019",
59+
"circuitplayground_express_digikey_pycon2019":
60+
"Circuit Playground Express Digi-Key PyCon 2019",
6161
"edgebadge": "Adafruit EdgeBadge",
6262
"pyportal_pynt": "Adafruit PyPortal Pynt",
6363
"gemma_m0_pycon2018": "Adafruit Gemma M0 PyCon 2018",
@@ -87,7 +87,7 @@
8787
"usb": "CIRCUITPY_PYUSB",
8888
}
8989

90-
MODULES_NOT_IN_BINDINGS = [ "binascii", "errno", "json", "re", "ulab" ]
90+
MODULES_NOT_IN_BINDINGS = ["binascii", "errno", "json", "re", "ulab"]
9191

9292
FROZEN_EXCLUDES = ["examples", "docs", "tests", "utils", "conf.py", "setup.py"]
9393
"""Files and dirs at the root of a frozen directory that should be ignored.
@@ -105,7 +105,8 @@ def get_circuitpython_root_dir():
105105

106106

107107
def get_bindings():
108-
"""Get a list of modules in shared-bindings and ports/*/bindings based on folder names."""
108+
"""Get a list of modules in shared-bindings and ports/*/bindings
109+
based on folder names."""
109110
shared_bindings_modules = [
110111
module.name
111112
for module in (get_circuitpython_root_dir() / "shared-bindings").iterdir()
@@ -114,7 +115,8 @@ def get_bindings():
114115
bindings_modules = []
115116
for d in get_circuitpython_root_dir().glob("ports/*/bindings"):
116117
bindings_modules.extend(module.name for module in d.iterdir() if d.is_dir())
117-
return shared_bindings_modules + bindings_modules + MODULES_NOT_IN_BINDINGS + list(ADDITIONAL_MODULES.keys())
118+
return shared_bindings_modules + bindings_modules + MODULES_NOT_IN_BINDINGS + \
119+
list(ADDITIONAL_MODULES.keys())
118120

119121

120122
def get_board_mapping():
@@ -127,7 +129,6 @@ def get_board_mapping():
127129
board_path = root_dir / "ports" / port / "boards"
128130
for board_path in os.scandir(board_path):
129131
if board_path.is_dir():
130-
board_files = os.listdir(board_path.path)
131132
board_id = board_path.name
132133
aliases = ALIASES_BY_BOARD.get(board_path.name, [])
133134
boards[board_id] = {
@@ -181,7 +182,10 @@ def get_settings_from_makefile(port_dir, board_name):
181182
}
182183

183184
contents = subprocess.run(
184-
["make", "-C", port_dir, "-f", "Makefile", f"BOARD={board_name}", "print-CFLAGS", "print-CIRCUITPY_BUILD_EXTENSIONS", "print-FROZEN_MPY_DIRS", "print-SRC_PATTERNS", "print-SRC_SUPERVISOR"],
185+
["make", "-C", port_dir, "-f", "Makefile", f"BOARD={board_name}",
186+
"print-CFLAGS", "print-CIRCUITPY_BUILD_EXTENSIONS",
187+
"print-FROZEN_MPY_DIRS", "print-SRC_PATTERNS",
188+
"print-SRC_SUPERVISOR"],
185189
encoding="utf-8",
186190
errors="replace",
187191
stdout=subprocess.PIPE,
@@ -288,7 +292,6 @@ def lookup_setting(settings, key, default=""):
288292
return value
289293

290294

291-
@functools.cache
292295
def all_ports_all_boards(ports=SUPPORTED_PORTS):
293296
for port in ports:
294297
port_dir = get_circuitpython_root_dir() / "ports" / port
@@ -298,7 +301,9 @@ def all_ports_all_boards(ports=SUPPORTED_PORTS):
298301
yield (port, entry)
299302

300303

301-
def support_matrix_by_board(use_branded_name=True, withurl=True):
304+
def support_matrix_by_board(use_branded_name=True, withurl=True,
305+
add_port=False, add_chips=False,
306+
add_pins=False, add_branded_name=False):
302307
"""Compiles a list of the available core modules available for each
303308
board.
304309
"""
@@ -309,17 +314,64 @@ def support_matrix(arg):
309314
port_dir = get_circuitpython_root_dir() / "ports" / port
310315
settings = get_settings_from_makefile(str(port_dir), entry.name)
311316

312-
if use_branded_name:
317+
if use_branded_name or add_branded_name:
313318
with open(entry / "mpconfigboard.h") as get_name:
314319
board_contents = get_name.read()
315320
board_name_re = re.search(
316321
r"(?<=MICROPY_HW_BOARD_NAME)\s+(.+)", board_contents
317322
)
318323
if board_name_re:
319-
board_name = board_name_re.group(1).strip('"')
324+
branded_name = board_name_re.group(1).strip('"')
325+
if '"' in branded_name: # sometimes the closing " is not at line end
326+
branded_name = branded_name[:branded_name.index('"')]
327+
board_name = branded_name
328+
329+
if use_branded_name:
330+
board_name = branded_name
320331
else:
321332
board_name = entry.name
322333

334+
if add_chips:
335+
with open(entry / "mpconfigboard.h") as get_name:
336+
board_contents = get_name.read()
337+
mcu_re = re.search(
338+
r'(?<=MICROPY_HW_MCU_NAME)\s+(.+)', board_contents
339+
)
340+
if mcu_re:
341+
mcu = mcu_re.group(1).strip('"')
342+
if '"' in mcu: # in case the closing " is not at line end
343+
mcu = mcu[:mcu.index('"')]
344+
else:
345+
mcu = ""
346+
with open(entry / "mpconfigboard.mk") as get_name:
347+
board_contents = get_name.read()
348+
flash_re = re.search(
349+
r'(?<=EXTERNAL_FLASH_DEVICES)\s+=\s+(.+)', board_contents
350+
)
351+
if flash_re:
352+
# deal with the variability in the way multiple flash chips
353+
# are denoted. We want them to end up as a quoted,
354+
# comma separated string
355+
flash = flash_re.group(1).replace('"','')
356+
flash = f'"{flash}"'
357+
else:
358+
flash = ""
359+
360+
if add_pins:
361+
pins = []
362+
try:
363+
with open(entry / "pins.c") as get_name:
364+
pin_lines = get_name.readlines()
365+
except FileNotFoundError: # silabs boards have no pins.c
366+
pass
367+
else:
368+
for p in pin_lines:
369+
pin_re = re.search(r"QSTR_([^\)]+).+pin_([^\)]+)", p)
370+
if pin_re:
371+
board_pin = pin_re.group(1)
372+
chip_pin = pin_re.group(2)
373+
pins.append((board_pin, chip_pin))
374+
323375
board_modules = []
324376
for module in base:
325377
key = base[module]["key"]
@@ -344,14 +396,25 @@ def support_matrix(arg):
344396
frozen_modules.sort()
345397

346398
# generate alias boards too
399+
400+
board_info = {
401+
"modules": board_modules,
402+
"frozen_libraries": frozen_modules,
403+
"extensions": board_extensions,
404+
}
405+
if add_branded_name:
406+
board_info["branded_name"] = branded_name
407+
if add_port:
408+
board_info["port"] = port
409+
if add_chips:
410+
board_info["mcu"] = mcu
411+
board_info["flash"] = flash
412+
if add_pins:
413+
board_info["pins"] = pins
347414
board_matrix = [
348415
(
349416
board_name,
350-
{
351-
"modules": board_modules,
352-
"frozen_libraries": frozen_modules,
353-
"extensions": board_extensions,
354-
},
417+
board_info
355418
)
356419
]
357420
if entry.name in ALIASES_BY_BOARD:
@@ -361,14 +424,24 @@ def support_matrix(arg):
361424
alias = ALIASES_BRAND_NAMES[alias]
362425
else:
363426
alias = alias.replace("_", " ").title()
427+
board_info = {
428+
"modules": board_modules,
429+
"frozen_libraries": frozen_modules,
430+
"extensions": board_extensions,
431+
}
432+
if add_branded_name:
433+
board_info["branded_name"] = branded_name
434+
if add_port:
435+
board_info["port"] = port
436+
if add_chips:
437+
board_info["mcu"] = mcu
438+
board_info["flash"] = flash
439+
if add_pins:
440+
board_info["pins"] = pins
364441
board_matrix.append(
365442
(
366443
alias,
367-
{
368-
"modules": board_modules,
369-
"frozen_libraries": frozen_modules,
370-
"extensions": board_extensions,
371-
},
444+
board_info
372445
)
373446
)
374447

0 commit comments

Comments
 (0)