Skip to content

Commit 9f85f25

Browse files
authored
Add extra checks to check_board_header.py (#1775)
* Small whitespace fixup * typo bugfix * Small refactoring of check_board_header.py * Make structure of rp2040_interface_pins.json more explicit, so that it can be more easily extended * Move definition of valid-pins from python to json * Check that each interface includes all (minimally) expected pins Note that UART_CTS, UART_RTS & SPI_CSN are classed as optional * Split "expected_functions" into "required" (all of these pins must be present) and "one_of" (at least one of these pins must be present)
1 parent 6624098 commit 9f85f25

File tree

2 files changed

+140
-84
lines changed

2 files changed

+140
-84
lines changed
Lines changed: 89 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,95 @@
11
{
2-
"UART": {
3-
"0": {
4-
"TX": [0, 12, 16, 28],
5-
"RX": [1, 13, 17, 29],
6-
"CTS": [2, 14, 18],
7-
"RTS": [3, 15, 19]
8-
},
9-
"1": {
10-
"TX": [4, 8, 20, 24],
11-
"RX": [5, 9, 21, 25],
12-
"CTS": [6, 10, 22, 26],
13-
"RTS": [7, 11, 23, 27]
14-
}
15-
},
16-
"I2C": {
17-
"0": {
18-
"SDA": [0, 4, 8, 12, 16, 20, 24, 28],
19-
"SCL": [1, 5, 9, 13, 17, 21, 25, 29]
20-
},
21-
"1": {
22-
"SDA": [2, 6, 10, 14, 18, 22, 26],
23-
"SCL": [3, 7, 11, 15, 19, 23, 27]
24-
}
25-
},
26-
"SPI": {
27-
"0": {
28-
"RX": [0, 4, 16, 20],
29-
"CSN": [1, 5, 17, 21],
30-
"SCK": [2, 6, 18, 22],
31-
"TX": [3, 7, 19, 23]
32-
},
33-
"1": {
34-
"RX": [8, 12, 24, 28],
35-
"CSN": [9, 13, 25, 29],
36-
"SCK": [10, 14, 26],
37-
"TX": [11, 15, 27]
38-
}
39-
},
40-
"PWM": {
41-
"0": {
42-
"A": [0, 16],
43-
"B": [1, 17]
44-
},
45-
"1": {
46-
"A": [2, 18],
47-
"B": [3, 19]
2+
"interfaces": {
3+
"UART": {
4+
"instances": {
5+
"0": {
6+
"TX": [0, 12, 16, 28],
7+
"RX": [1, 13, 17, 29],
8+
"CTS": [2, 14, 18],
9+
"RTS": [3, 15, 19]
10+
},
11+
"1": {
12+
"TX": [4, 8, 20, 24],
13+
"RX": [5, 9, 21, 25],
14+
"CTS": [6, 10, 22, 26],
15+
"RTS": [7, 11, 23, 27]
16+
}
17+
},
18+
"expected_functions": {
19+
"one_of": ["TX", "RX"]
20+
}
4821
},
49-
"2": {
50-
"A": [4, 20],
51-
"B": [5, 21]
22+
"I2C": {
23+
"instances": {
24+
"0": {
25+
"SDA": [0, 4, 8, 12, 16, 20, 24, 28],
26+
"SCL": [1, 5, 9, 13, 17, 21, 25, 29]
27+
},
28+
"1": {
29+
"SDA": [2, 6, 10, 14, 18, 22, 26],
30+
"SCL": [3, 7, 11, 15, 19, 23, 27]
31+
}
32+
},
33+
"expected_functions": {
34+
"required": ["SDA", "SCL"]
35+
}
5236
},
53-
"3": {
54-
"A": [6, 22],
55-
"B": [7, 23]
37+
"SPI": {
38+
"instances": {
39+
"0": {
40+
"RX": [0, 4, 16, 20],
41+
"CSN": [1, 5, 17, 21],
42+
"SCK": [2, 6, 18, 22],
43+
"TX": [3, 7, 19, 23]
44+
},
45+
"1": {
46+
"RX": [8, 12, 24, 28],
47+
"CSN": [9, 13, 25, 29],
48+
"SCK": [10, 14, 26],
49+
"TX": [11, 15, 27]
50+
}
51+
},
52+
"expected_functions": {
53+
"required": ["SCK"],
54+
"one_of": ["RX", "TX"]
55+
}
5656
},
57-
"4": {
58-
"A": [8, 24],
59-
"B": [9, 25]
60-
},
61-
"5": {
62-
"A": [10, 26],
63-
"B": [11, 27]
64-
},
65-
"6": {
66-
"A": [12, 28],
67-
"B": [13, 29]
68-
},
69-
"7": {
70-
"A": [14],
71-
"B": [15]
57+
"PWM": {
58+
"instances": {
59+
"0": {
60+
"A": [0, 16],
61+
"B": [1, 17]
62+
},
63+
"1": {
64+
"A": [2, 18],
65+
"B": [3, 19]
66+
},
67+
"2": {
68+
"A": [4, 20],
69+
"B": [5, 21]
70+
},
71+
"3": {
72+
"A": [6, 22],
73+
"B": [7, 23]
74+
},
75+
"4": {
76+
"A": [8, 24],
77+
"B": [9, 25]
78+
},
79+
"5": {
80+
"A": [10, 26],
81+
"B": [11, 27]
82+
},
83+
"6": {
84+
"A": [12, 28],
85+
"B": [13, 29]
86+
},
87+
"7": {
88+
"A": [14],
89+
"B": [15]
90+
}
91+
}
7292
}
73-
}
93+
},
94+
"pins": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
7495
}

tools/check_board_header.py

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,17 @@
3232
raise Exception("{} doesn't exist".format(board_header))
3333

3434
with open(interfaces_json) as interfaces_fh:
35-
interfaces = json.load(interfaces_fh)
35+
interface_pins = json.load(interfaces_fh)
36+
allowed_interfaces = interface_pins["interfaces"]
37+
allowed_pins = set(interface_pins["pins"])
3638
# convert instance-keys to integers (allowed by Python but not by JSON)
37-
for interface in interfaces:
38-
for instance in list(interfaces[interface]):
39-
interfaces[interface][int(instance)] = interfaces[interface].pop(instance)
39+
for interface in allowed_interfaces:
40+
instances = allowed_interfaces[interface]["instances"]
41+
# can't modify a list that we're iterating over, so iterate over a copy
42+
instances_copy = list(instances)
43+
for instance in instances_copy:
44+
instance_num = int(instance)
45+
instances[instance_num] = instances.pop(instance)
4046

4147
DefineType = namedtuple("DefineType", ["name", "value", "resolved_value", "lineno"])
4248

@@ -69,7 +75,7 @@
6975
if include_suggestion == expected_include_suggestion:
7076
has_include_suggestion = True
7177
else:
72-
raise Exception("{}:{} Suggests including \"{}\" but file is named \"{}\"".format(board_header, lineno, include_suggestion, expected_include_suggestion))
78+
raise Exception(r"{}:{} Suggests including \"{}\" but file is named \"{}\"".format(board_header, lineno, include_suggestion, expected_include_suggestion))
7379
# look for "#ifndef BLAH_BLAH"
7480
m = re.match(r"^#ifndef (\w+)\s*$", line)
7581
if m:
@@ -83,7 +89,7 @@
8389
value = m.group(2)
8490
# check all uppercase
8591
if name != name.upper():
86-
raise Exception("{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name))
92+
raise Exception(r"{}:{} Expected \"{}\" to be all uppercase".format(board_header, lineno, name))
8793
# check that adjacent #ifndef and #define lines match up
8894
if last_ifndef_lineno + 1 == lineno:
8995
if last_ifndef != name:
@@ -145,33 +151,62 @@
145151
warnings.warn("{}:{} Both {} and {} claim to be pin {}".format(board_header, lineno, pins[resolved_value][0].name, name, resolved_value))
146152
pins[resolved_value].append(define)
147153
else:
148-
if not (0 <= resolved_value <= 29):
149-
raise Exception("{}:{} Pin {} for {} is outside of the allowed range".foramt(board_header, lineno, resolved_value, name))
154+
if resolved_value not in allowed_pins:
155+
raise Exception("{}:{} Pin {} for {} isn't a valid pin-number".format(board_header, lineno, resolved_value, name))
150156
pins[resolved_value] = [define]
151157

152158
#import pprint; pprint.pprint(dict(sorted(defines.items(), key=lambda x: x[1].lineno)))
153159

154160
# check for invalid DEFAULT mappings
155161
for name, define in defines.items():
156-
m = re.match(r"^(PICO_DEFAULT_(\w+))_(\w+)_PIN$", name)
162+
m = re.match("^(PICO_DEFAULT_([A-Z0-9]+))_([A-Z0-9]+)_PIN$", name)
157163
if m:
158164
instance_name = m.group(1)
159165
interface = m.group(2)
160166
function = m.group(3)
161167
if interface == "WS2812":
162168
continue
163-
if interface not in interfaces:
169+
if interface not in allowed_interfaces:
164170
raise Exception("{}:{} {} is defined but {} isn't in {}".format(board_header, define.lineno, name, interface, interfaces_json))
165171
if instance_name not in defines:
166172
raise Exception("{}:{} {} is defined but {} isn't defined".format(board_header, define.lineno, name, instance_name))
167173
instance_define = defines[instance_name]
168-
instance = instance_define.resolved_value
169-
if instance not in interfaces[interface]:
170-
raise Exception("{}:{} {} is set to an invalid instance {}".format(board_header, instance_define.lineno, instance_define, instance))
171-
if function not in interfaces[interface][instance]:
174+
instance_num = instance_define.resolved_value
175+
if instance_num not in allowed_interfaces[interface]["instances"]:
176+
raise Exception("{}:{} {} is set to an invalid instance {}".format(board_header, instance_define.lineno, instance_define, instance_num))
177+
interface_instance = allowed_interfaces[interface]["instances"][instance_num]
178+
if function not in interface_instance:
172179
raise Exception("{}:{} {} is defined but {} isn't a valid function for {}".format(board_header, define.lineno, name, function, instance_define))
173-
if define.resolved_value not in interfaces[interface][instance][function]:
174-
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))
180+
if define.resolved_value not in interface_instance[function]:
181+
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_num))
182+
183+
def list_to_string_with(lst, joiner):
184+
elems = len(lst)
185+
if elems == 0:
186+
return ""
187+
elif elems == 1:
188+
return str(lst[0])
189+
else:
190+
return "{} {} {}".format(", ".join(str(l) for l in lst[:-1]), joiner, lst[-1])
191+
192+
# check that each used DEFAULT interface includes (at least) the expected pin-functions
193+
for name, define in defines.items():
194+
m = re.match("^PICO_DEFAULT_([A-Z0-9]+)$", name)
195+
if m:
196+
interface = m.group(1)
197+
if interface not in allowed_interfaces:
198+
raise Exception("{}:{} {} is defined but {} isn't in {}".format(board_header, define.lineno, name, interface, interfaces_json))
199+
if "expected_functions" in allowed_interfaces[interface]:
200+
expected_functions = allowed_interfaces[interface]["expected_functions"]
201+
if "required" in expected_functions:
202+
for function in expected_functions["required"]:
203+
expected_function_pin = "{}_{}_PIN".format(name, function)
204+
if expected_function_pin not in defines:
205+
raise Exception("{}:{} {} is defined but {} isn't defined".format(board_header, define.lineno, name, expected_function_pin))
206+
if "one_of" in expected_functions:
207+
expected_function_pins = list("{}_{}_PIN".format(name, function) for function in expected_functions["one_of"])
208+
if not any(func_pin in defines for func_pin in expected_function_pins):
209+
raise Exception("{}:{} {} is defined but none of {} are defined".format(board_header, define.lineno, name, list_to_string_with(expected_function_pins, "or")))
175210

176211
if not has_include_guard:
177212
raise Exception("{} has no include-guard (expected {})".format(board_header, expected_include_guard))

0 commit comments

Comments
 (0)