Skip to content

Commit 3f4a4cd

Browse files
gmarullcarlescufi
authored andcommitted
scripts: west: genboard: use JSON schema
Use JSON-schema for input/output. This eases tool integration with other tooling as we have a standardized interface. Signed-off-by: Gerard Marull-Paretas <[email protected]>
1 parent 12bc79b commit 3f4a4cd

File tree

4 files changed

+135
-75
lines changed

4 files changed

+135
-75
lines changed

scripts/requirements-base.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
west>=1.0.0
22
certifi >=2024.7.4 # from requests above; https://nvd.nist.gov/vuln/detail/CVE-2024-39689
3+
jsonschema

scripts/west_commands/genboard/ncs_genboard.py

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
33

44
from pathlib import Path
5-
import re
5+
import json
66
import shutil
77

88
from jinja2 import Environment, FileSystemLoader
99
from west.commands import WestCommand
1010
from west import log
1111
from yaml import load
12+
import jsonschema
1213

1314
try:
1415
from yaml import CLoader as Loader
@@ -19,9 +20,7 @@
1920
SCRIPT_DIR = Path(__file__).absolute().parent
2021
TEMPLATE_DIR = SCRIPT_DIR / "templates"
2122
CONFIG = SCRIPT_DIR / "config.yml"
22-
23-
VENDOR_RE = re.compile(r"^[a-zA-Z0-9_-]+$")
24-
BOARD_RE = re.compile(r"^[a-zA-Z0-9_-]+$")
23+
SCHEMA = SCRIPT_DIR / "schema.json"
2524

2625

2726
class NcsGenboard(WestCommand):
@@ -36,31 +35,38 @@ def do_add_parser(self, parser_adder):
3635
self.name, help=self.help, description=self.description
3736
)
3837

39-
parser.add_argument(
40-
"-o", "--output", required=True, type=Path, help="Output directory"
38+
group = parser.add_mutually_exclusive_group(required=True)
39+
group.add_argument(
40+
"-s", "--json-schema", action="store_true", help="Provide JSON schema"
4141
)
42-
parser.add_argument("-e", "--vendor", required=True, help="Vendor name")
43-
parser.add_argument("-b", "--board", required=True, help="Board name")
44-
parser.add_argument(
45-
"-d", "--board-desc", required=True, help="Board description"
42+
group.add_argument(
43+
"-r", "--json-schema-response", type=str, help="JSON schema response"
4644
)
47-
parser.add_argument("-s", "--soc", required=True, help="SoC")
48-
parser.add_argument("-v", "--variant", required=True, help="Variant")
4945

5046
return parser
5147

5248
def do_run(self, args, unknown_args):
49+
with open(SCHEMA, "r") as f:
50+
schema = json.loads(f.read())
51+
52+
if args.json_schema:
53+
print(json.dumps(schema))
54+
return
55+
5356
with open(CONFIG, "r") as f:
5457
config = load(f, Loader=Loader)
5558

5659
# validate input
57-
if not VENDOR_RE.match(args.vendor):
58-
log.err(f"Invalid vendor name: {args.vendor}")
59-
return
60+
input = json.loads(args.json_schema_response)
6061

61-
if not BOARD_RE.match(args.board):
62-
log.err(f"Invalid board name: {args.board}")
63-
return
62+
try:
63+
jsonschema.validate(input, schema)
64+
except jsonschema.ValidationError as e:
65+
raise Exception("Board configuration is not valid") from e
66+
67+
soc_parts = input["soc"].split("-")
68+
req_soc = soc_parts[0].lower()
69+
req_variant = soc_parts[1].lower()
6470

6571
series = None
6672
soc = None
@@ -69,18 +75,18 @@ def do_run(self, args, unknown_args):
6975
break
7076

7177
for soc_ in product["socs"]:
72-
if args.soc == soc_["name"]:
78+
if req_soc == soc_["name"]:
7379
series = product["series"]
7480
soc = soc_
7581
break
7682

7783
if not series:
78-
log.err(f"Invalid/unsupported SoC: {args.soc}")
84+
log.err(f"Invalid/unsupported SoC: {req_soc}")
7985
return
8086

8187
targets = []
8288
for variant in soc["variants"]:
83-
if args.variant == variant["name"]:
89+
if req_variant == variant["name"]:
8490
if "cores" in variant:
8591
for core in variant["cores"]:
8692
target = {
@@ -119,7 +125,7 @@ def do_run(self, args, unknown_args):
119125
break
120126

121127
if not targets:
122-
log.err(f"Invalid/unsupported variant: {args.variant}")
128+
log.err(f"Invalid/unsupported variant: {req_variant}")
123129
return
124130

125131
# prepare Jinja environment
@@ -129,16 +135,16 @@ def do_run(self, args, unknown_args):
129135
loader=FileSystemLoader(TEMPLATE_DIR / series),
130136
)
131137

132-
env.globals["vendor"] = args.vendor
133-
env.globals["board"] = args.board
134-
env.globals["board_desc"] = args.board_desc
138+
env.globals["vendor"] = input["vendor"]
139+
env.globals["board"] = input["board"]
140+
env.globals["board_desc"] = input["description"]
135141
env.globals["series"] = series
136-
env.globals["soc"] = args.soc
137-
env.globals["variant"] = args.variant
142+
env.globals["soc"] = req_soc
143+
env.globals["variant"] = req_variant
138144
env.globals["targets"] = targets
139145

140146
# render templates/copy files
141-
out_dir = args.output / args.vendor / args.board
147+
out_dir = Path(input["root"]) / "boards" / input["vendor"] / input["board"]
142148
if not out_dir.exists():
143149
out_dir.mkdir(parents=True)
144150

@@ -147,14 +153,14 @@ def do_run(self, args, unknown_args):
147153
shutil.copy(tmpl, out_dir)
148154

149155
tmpl = TEMPLATE_DIR / series / "board-pinctrl.dtsi"
150-
shutil.copy(tmpl, out_dir / f"{ args.board }-pinctrl.dtsi")
156+
shutil.copy(tmpl, out_dir / f"{ input['board'] }-pinctrl.dtsi")
151157

152158
tmpl = env.get_template("board.cmake.jinja2")
153159
with open(out_dir / "board.cmake", "w") as f:
154160
f.write(tmpl.render())
155161

156162
tmpl = env.get_template("Kconfig.board.jinja2")
157-
with open(out_dir / f"Kconfig.{args.board}", "w") as f:
163+
with open(out_dir / f"Kconfig.{input['board']}", "w") as f:
158164
f.write(tmpl.render())
159165

160166
tmpl = env.get_template("board.yml.jinja2")
@@ -168,23 +174,23 @@ def do_run(self, args, unknown_args):
168174
# nrf53 specific files
169175
if series == "nrf53":
170176
tmpl = env.get_template("board-cpuapp_partitioning.dtsi.jinja2")
171-
with open(out_dir / f"{ args.board }-cpuapp_partitioning.dtsi", "w") as f:
177+
with open(out_dir / f"{ input['board'] }-cpuapp_partitioning.dtsi", "w") as f:
172178
f.write(tmpl.render(config))
173179

174180
tmpl = TEMPLATE_DIR / series / "board-shared_sram.dtsi"
175-
shutil.copy(tmpl, out_dir / f"{ args.board }-shared_sram.dtsi")
181+
shutil.copy(tmpl, out_dir / f"{ input['board'] }-shared_sram.dtsi")
176182

177183
# nrf91 specific files
178184
if series == "nrf91":
179185
tmpl = env.get_template("board-partitioning.dtsi.jinja2")
180-
with open(out_dir / f"{ args.board }-partitioning.dtsi", "w") as f:
186+
with open(out_dir / f"{ input['board'] }-partitioning.dtsi", "w") as f:
181187
f.write(tmpl.render(config))
182188

183189
# per-target files
184190
for target in targets:
185-
name = args.board
191+
name = input["board"]
186192
if target.get("core"):
187-
name += f"_{args.soc}_{target['core']}"
193+
name += f"_{req_soc}_{target['core']}"
188194
if target["ns"]:
189195
name += "_ns"
190196
if target["xip"]:
@@ -201,3 +207,5 @@ def do_run(self, args, unknown_args):
201207
tmpl = env.get_template("board_twister.yml.jinja2")
202208
with open(out_dir / f"{name}.yml", "w") as f:
203209
f.write(tmpl.render(target=target))
210+
211+
print(f"Board {input['board']} generated successfully")
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"title": "NCS board",
3+
"type": "object",
4+
"required": [
5+
"board",
6+
"description",
7+
"vendor",
8+
"soc",
9+
"root"
10+
],
11+
"properties": {
12+
"board": {
13+
"title": "Board name",
14+
"type": "string",
15+
"pattern": "^[a-zA-Z0-9_-]+$"
16+
},
17+
"description": {
18+
"title": "Description",
19+
"type": "string"
20+
},
21+
"vendor": {
22+
"title": "Vendor name",
23+
"type": "string",
24+
"pattern": "^[a-zA-Z0-9_-]+$"
25+
},
26+
"soc": {
27+
"title": "SoC",
28+
"type": "string",
29+
"enum": [
30+
"nRF52805-CAAA",
31+
"nRF52810-QFAA",
32+
"nRF52811-QFAA",
33+
"nRF52820-QDAA",
34+
"nRF52832-CIAA",
35+
"nRF52832-QFAA",
36+
"nRF52832-QFAB",
37+
"nRF52833-QDAA",
38+
"nRF52833-QIAA",
39+
"nRF52840-QFAA",
40+
"nRF52840-QIAA",
41+
"nRF5340-QKAA",
42+
"nRF54L15-QFAA",
43+
"nRF9131-LACA",
44+
"nRF9151-LACA",
45+
"nRF9160-SICA",
46+
"nRF9161-LACA"
47+
]
48+
},
49+
"root": {
50+
"title": "Board root",
51+
"type": "string"
52+
}
53+
}
54+
}

scripts/west_commands/genboard/west-ncs-genboard-test.sh

Lines changed: 37 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,64 +8,61 @@ NCS_BASE="$SCRIPTDIR/../../.."
88
SOC=${1}
99

1010
declare -a SOCS=(
11-
"nrf52805 caaa"
12-
"nrf52810 qfaa"
13-
"nrf52811 qfaa"
14-
"nrf52820 qdaa"
15-
"nrf52832 ciaa"
16-
"nrf52832 qfaa"
17-
"nrf52832 qfab"
18-
"nrf52833 qdaa"
19-
"nrf52833 qiaa"
20-
"nrf52840 qfaa"
21-
"nrf52840 qiaa"
22-
"nrf5340 qkaa"
23-
"nrf54l15 qfaa"
24-
"nrf9131 laca"
25-
"nrf9151 laca"
26-
"nrf9160 sica"
27-
"nrf9161 laca"
11+
"nRF52805-CAAA"
12+
"nRF52810-QFAA"
13+
"nRF52811-QFAA"
14+
"nRF52820-QDAA"
15+
"nRF52832-CIAA"
16+
"nRF52832-QFAA"
17+
"nRF52832-QFAB"
18+
"nRF52833-QDAA"
19+
"nRF52833-QIAA"
20+
"nRF52840-QFAA"
21+
"nRF52840-QIAA"
22+
"nRF5340-QKAA"
23+
"nRF54L15-QFAA"
24+
"nRF9131-LACA"
25+
"nRF9151-LACA"
26+
"nRF9160-SICA"
27+
"nRF9161-LACA"
2828
)
2929

3030
HELLO_WORLD="$NCS_BASE/../zephyr/samples/hello_world"
3131

3232
rm -rf $NCS_BASE/boards/testvnd
3333

3434
for soc in "${SOCS[@]}"; do
35-
read -a socarr <<< "$soc"
35+
soc_parts=(${soc//-/ })
36+
soc_name=$(echo ${soc_parts[0]} | tr "[:upper:]" "[:lower:]")
37+
soc_variant=$(echo ${soc_parts[1]} | tr "[:upper:]" "[:lower:]")
3638

37-
if [ ! -z "$SOC" ] && [ $SOC != ${socarr[0]} ]; then
38-
echo "Skipping ${socarr[0]} (not requested)"
39+
if [ ! -z "$SOC" ] && [ $SOC != ${soc} ]; then
40+
echo "Skipping $soc (not requested)"
3941
continue
4042
fi
4143

42-
board=brd_${socarr[0]}_${socarr[1]}
44+
board=brd_${soc_name}_${soc_variant}
4345

4446
echo "Generating board: $board"
4547

46-
west ncs-genboard \
47-
-o $NCS_BASE/boards \
48-
-e "testvnd" \
49-
-b $board \
50-
-d "Test Board" \
51-
-s ${socarr[0]} \
52-
-v ${socarr[1]} \
48+
west ncs-genboard --json-schema-response \
49+
"{\"board\": \"$board\", \"description\": \"Test Board\", \"vendor\": \"testvnd\", \"soc\": \"$soc\", \"root\": \"$NCS_BASE\"}"
5350

5451
echo "Building hello_world for: $board"
5552

56-
if [[ ${socarr[0]} == nrf52* ]]; then
53+
if [[ $soc == nRF52* ]]; then
5754
west build -p -b $board $HELLO_WORLD
58-
elif [[ ${socarr[0]} == nrf53* ]]; then
59-
west build -p -b $board/${socarr[0]}/cpuapp $HELLO_WORLD
60-
west build -p -b $board/${socarr[0]}/cpuapp/ns $HELLO_WORLD
61-
west build -p -b $board/${socarr[0]}/cpunet $HELLO_WORLD
62-
elif [[ ${socarr[0]} == nrf54l* ]]; then
63-
west build -p -b $board/${socarr[0]}/cpuapp $HELLO_WORLD
64-
# west build -p -b $board/${socarr[0]}/cpuflpr $HELLO_WORLD
65-
# west build -p -b $board/${socarr[0]}/cpuflpr/xip $HELLO_WORLD
66-
elif [[ ${socarr[0]} == nrf91* ]]; then
67-
west build -p -b $board/${socarr[0]} $HELLO_WORLD
68-
west build -p -b $board/${socarr[0]}/ns $HELLO_WORLD
55+
elif [[ $soc == nRF53* ]]; then
56+
west build -p -b $board/$soc_name/cpuapp $HELLO_WORLD
57+
west build -p -b $board/$soc_name/cpuapp/ns $HELLO_WORLD
58+
west build -p -b $board/$soc_name/cpunet $HELLO_WORLD
59+
elif [[ $soc == nRF54L* ]]; then
60+
west build -p -b $board/$soc_name/cpuapp $HELLO_WORLD
61+
# west build -p -b $board/$soc_name/cpuflpr $HELLO_WORLD
62+
# west build -p -b $board/$soc_name/cpuflpr/xip $HELLO_WORLD
63+
elif [[ $soc == nRF91* ]]; then
64+
west build -p -b $board/$soc_name $HELLO_WORLD
65+
west build -p -b $board/$soc_name/ns $HELLO_WORLD
6966
fi
7067

7168
rm -rf $NCS_BASE/boards/testvnd

0 commit comments

Comments
 (0)