diff --git a/scripts/requirements-base.txt b/scripts/requirements-base.txt index f6784b6ed458..1b4bb73b50cc 100644 --- a/scripts/requirements-base.txt +++ b/scripts/requirements-base.txt @@ -1,2 +1,3 @@ west>=1.0.0 certifi >=2024.7.4 # from requests above; https://nvd.nist.gov/vuln/detail/CVE-2024-39689 +jsonschema diff --git a/scripts/west_commands/genboard/ncs_genboard.py b/scripts/west_commands/genboard/ncs_genboard.py index 74f31350b2dd..26df61061986 100644 --- a/scripts/west_commands/genboard/ncs_genboard.py +++ b/scripts/west_commands/genboard/ncs_genboard.py @@ -2,13 +2,14 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause from pathlib import Path -import re +import json import shutil from jinja2 import Environment, FileSystemLoader from west.commands import WestCommand from west import log from yaml import load +import jsonschema try: from yaml import CLoader as Loader @@ -19,9 +20,7 @@ SCRIPT_DIR = Path(__file__).absolute().parent TEMPLATE_DIR = SCRIPT_DIR / "templates" CONFIG = SCRIPT_DIR / "config.yml" - -VENDOR_RE = re.compile(r"^[a-zA-Z0-9_-]+$") -BOARD_RE = re.compile(r"^[a-zA-Z0-9_-]+$") +SCHEMA = SCRIPT_DIR / "schema.json" class NcsGenboard(WestCommand): @@ -36,31 +35,38 @@ def do_add_parser(self, parser_adder): self.name, help=self.help, description=self.description ) - parser.add_argument( - "-o", "--output", required=True, type=Path, help="Output directory" + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument( + "-s", "--json-schema", action="store_true", help="Provide JSON schema" ) - parser.add_argument("-e", "--vendor", required=True, help="Vendor name") - parser.add_argument("-b", "--board", required=True, help="Board name") - parser.add_argument( - "-d", "--board-desc", required=True, help="Board description" + group.add_argument( + "-r", "--json-schema-response", type=str, help="JSON schema response" ) - parser.add_argument("-s", "--soc", required=True, help="SoC") - parser.add_argument("-v", "--variant", required=True, help="Variant") return parser def do_run(self, args, unknown_args): + with open(SCHEMA, "r") as f: + schema = json.loads(f.read()) + + if args.json_schema: + print(json.dumps(schema)) + return + with open(CONFIG, "r") as f: config = load(f, Loader=Loader) # validate input - if not VENDOR_RE.match(args.vendor): - log.err(f"Invalid vendor name: {args.vendor}") - return + input = json.loads(args.json_schema_response) - if not BOARD_RE.match(args.board): - log.err(f"Invalid board name: {args.board}") - return + try: + jsonschema.validate(input, schema) + except jsonschema.ValidationError as e: + raise Exception("Board configuration is not valid") from e + + soc_parts = input["soc"].split("-") + req_soc = soc_parts[0].lower() + req_variant = soc_parts[1].lower() series = None soc = None @@ -69,18 +75,18 @@ def do_run(self, args, unknown_args): break for soc_ in product["socs"]: - if args.soc == soc_["name"]: + if req_soc == soc_["name"]: series = product["series"] soc = soc_ break if not series: - log.err(f"Invalid/unsupported SoC: {args.soc}") + log.err(f"Invalid/unsupported SoC: {req_soc}") return targets = [] for variant in soc["variants"]: - if args.variant == variant["name"]: + if req_variant == variant["name"]: if "cores" in variant: for core in variant["cores"]: target = { @@ -119,7 +125,7 @@ def do_run(self, args, unknown_args): break if not targets: - log.err(f"Invalid/unsupported variant: {args.variant}") + log.err(f"Invalid/unsupported variant: {req_variant}") return # prepare Jinja environment @@ -129,16 +135,16 @@ def do_run(self, args, unknown_args): loader=FileSystemLoader(TEMPLATE_DIR / series), ) - env.globals["vendor"] = args.vendor - env.globals["board"] = args.board - env.globals["board_desc"] = args.board_desc + env.globals["vendor"] = input["vendor"] + env.globals["board"] = input["board"] + env.globals["board_desc"] = input["description"] env.globals["series"] = series - env.globals["soc"] = args.soc - env.globals["variant"] = args.variant + env.globals["soc"] = req_soc + env.globals["variant"] = req_variant env.globals["targets"] = targets # render templates/copy files - out_dir = args.output / args.vendor / args.board + out_dir = Path(input["root"]) / "boards" / input["vendor"] / input["board"] if not out_dir.exists(): out_dir.mkdir(parents=True) @@ -147,14 +153,14 @@ def do_run(self, args, unknown_args): shutil.copy(tmpl, out_dir) tmpl = TEMPLATE_DIR / series / "board-pinctrl.dtsi" - shutil.copy(tmpl, out_dir / f"{ args.board }-pinctrl.dtsi") + shutil.copy(tmpl, out_dir / f"{ input['board'] }-pinctrl.dtsi") tmpl = env.get_template("board.cmake.jinja2") with open(out_dir / "board.cmake", "w") as f: f.write(tmpl.render()) tmpl = env.get_template("Kconfig.board.jinja2") - with open(out_dir / f"Kconfig.{args.board}", "w") as f: + with open(out_dir / f"Kconfig.{input['board']}", "w") as f: f.write(tmpl.render()) tmpl = env.get_template("board.yml.jinja2") @@ -168,23 +174,23 @@ def do_run(self, args, unknown_args): # nrf53 specific files if series == "nrf53": tmpl = env.get_template("board-cpuapp_partitioning.dtsi.jinja2") - with open(out_dir / f"{ args.board }-cpuapp_partitioning.dtsi", "w") as f: + with open(out_dir / f"{ input['board'] }-cpuapp_partitioning.dtsi", "w") as f: f.write(tmpl.render(config)) tmpl = TEMPLATE_DIR / series / "board-shared_sram.dtsi" - shutil.copy(tmpl, out_dir / f"{ args.board }-shared_sram.dtsi") + shutil.copy(tmpl, out_dir / f"{ input['board'] }-shared_sram.dtsi") # nrf91 specific files if series == "nrf91": tmpl = env.get_template("board-partitioning.dtsi.jinja2") - with open(out_dir / f"{ args.board }-partitioning.dtsi", "w") as f: + with open(out_dir / f"{ input['board'] }-partitioning.dtsi", "w") as f: f.write(tmpl.render(config)) # per-target files for target in targets: - name = args.board + name = input["board"] if target.get("core"): - name += f"_{args.soc}_{target['core']}" + name += f"_{req_soc}_{target['core']}" if target["ns"]: name += "_ns" if target["xip"]: @@ -201,3 +207,5 @@ def do_run(self, args, unknown_args): tmpl = env.get_template("board_twister.yml.jinja2") with open(out_dir / f"{name}.yml", "w") as f: f.write(tmpl.render(target=target)) + + print(f"Board {input['board']} generated successfully") diff --git a/scripts/west_commands/genboard/schema.json b/scripts/west_commands/genboard/schema.json new file mode 100644 index 000000000000..08036d8747bb --- /dev/null +++ b/scripts/west_commands/genboard/schema.json @@ -0,0 +1,54 @@ +{ + "title": "NCS board", + "type": "object", + "required": [ + "board", + "description", + "vendor", + "soc", + "root" + ], + "properties": { + "board": { + "title": "Board name", + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "description": { + "title": "Description", + "type": "string" + }, + "vendor": { + "title": "Vendor name", + "type": "string", + "pattern": "^[a-zA-Z0-9_-]+$" + }, + "soc": { + "title": "SoC", + "type": "string", + "enum": [ + "nRF52805-CAAA", + "nRF52810-QFAA", + "nRF52811-QFAA", + "nRF52820-QDAA", + "nRF52832-CIAA", + "nRF52832-QFAA", + "nRF52832-QFAB", + "nRF52833-QDAA", + "nRF52833-QIAA", + "nRF52840-QFAA", + "nRF52840-QIAA", + "nRF5340-QKAA", + "nRF54L15-QFAA", + "nRF9131-LACA", + "nRF9151-LACA", + "nRF9160-SICA", + "nRF9161-LACA" + ] + }, + "root": { + "title": "Board root", + "type": "string" + } + } +} diff --git a/scripts/west_commands/genboard/west-ncs-genboard-test.sh b/scripts/west_commands/genboard/west-ncs-genboard-test.sh index a12152731368..4fec4beaf3d9 100755 --- a/scripts/west_commands/genboard/west-ncs-genboard-test.sh +++ b/scripts/west_commands/genboard/west-ncs-genboard-test.sh @@ -8,23 +8,23 @@ NCS_BASE="$SCRIPTDIR/../../.." SOC=${1} declare -a SOCS=( - "nrf52805 caaa" - "nrf52810 qfaa" - "nrf52811 qfaa" - "nrf52820 qdaa" - "nrf52832 ciaa" - "nrf52832 qfaa" - "nrf52832 qfab" - "nrf52833 qdaa" - "nrf52833 qiaa" - "nrf52840 qfaa" - "nrf52840 qiaa" - "nrf5340 qkaa" - "nrf54l15 qfaa" - "nrf9131 laca" - "nrf9151 laca" - "nrf9160 sica" - "nrf9161 laca" + "nRF52805-CAAA" + "nRF52810-QFAA" + "nRF52811-QFAA" + "nRF52820-QDAA" + "nRF52832-CIAA" + "nRF52832-QFAA" + "nRF52832-QFAB" + "nRF52833-QDAA" + "nRF52833-QIAA" + "nRF52840-QFAA" + "nRF52840-QIAA" + "nRF5340-QKAA" + "nRF54L15-QFAA" + "nRF9131-LACA" + "nRF9151-LACA" + "nRF9160-SICA" + "nRF9161-LACA" ) HELLO_WORLD="$NCS_BASE/../zephyr/samples/hello_world" @@ -32,40 +32,37 @@ HELLO_WORLD="$NCS_BASE/../zephyr/samples/hello_world" rm -rf $NCS_BASE/boards/testvnd for soc in "${SOCS[@]}"; do - read -a socarr <<< "$soc" + soc_parts=(${soc//-/ }) + soc_name=$(echo ${soc_parts[0]} | tr "[:upper:]" "[:lower:]") + soc_variant=$(echo ${soc_parts[1]} | tr "[:upper:]" "[:lower:]") - if [ ! -z "$SOC" ] && [ $SOC != ${socarr[0]} ]; then - echo "Skipping ${socarr[0]} (not requested)" + if [ ! -z "$SOC" ] && [ $SOC != ${soc} ]; then + echo "Skipping $soc (not requested)" continue fi - board=brd_${socarr[0]}_${socarr[1]} + board=brd_${soc_name}_${soc_variant} echo "Generating board: $board" - west ncs-genboard \ - -o $NCS_BASE/boards \ - -e "testvnd" \ - -b $board \ - -d "Test Board" \ - -s ${socarr[0]} \ - -v ${socarr[1]} \ + west ncs-genboard --json-schema-response \ + "{\"board\": \"$board\", \"description\": \"Test Board\", \"vendor\": \"testvnd\", \"soc\": \"$soc\", \"root\": \"$NCS_BASE\"}" echo "Building hello_world for: $board" - if [[ ${socarr[0]} == nrf52* ]]; then + if [[ $soc == nRF52* ]]; then west build -p -b $board $HELLO_WORLD - elif [[ ${socarr[0]} == nrf53* ]]; then - west build -p -b $board/${socarr[0]}/cpuapp $HELLO_WORLD - west build -p -b $board/${socarr[0]}/cpuapp/ns $HELLO_WORLD - west build -p -b $board/${socarr[0]}/cpunet $HELLO_WORLD - elif [[ ${socarr[0]} == nrf54l* ]]; then - west build -p -b $board/${socarr[0]}/cpuapp $HELLO_WORLD - # west build -p -b $board/${socarr[0]}/cpuflpr $HELLO_WORLD - # west build -p -b $board/${socarr[0]}/cpuflpr/xip $HELLO_WORLD - elif [[ ${socarr[0]} == nrf91* ]]; then - west build -p -b $board/${socarr[0]} $HELLO_WORLD - west build -p -b $board/${socarr[0]}/ns $HELLO_WORLD + elif [[ $soc == nRF53* ]]; then + west build -p -b $board/$soc_name/cpuapp $HELLO_WORLD + west build -p -b $board/$soc_name/cpuapp/ns $HELLO_WORLD + west build -p -b $board/$soc_name/cpunet $HELLO_WORLD + elif [[ $soc == nRF54L* ]]; then + west build -p -b $board/$soc_name/cpuapp $HELLO_WORLD + # west build -p -b $board/$soc_name/cpuflpr $HELLO_WORLD + # west build -p -b $board/$soc_name/cpuflpr/xip $HELLO_WORLD + elif [[ $soc == nRF91* ]]; then + west build -p -b $board/$soc_name $HELLO_WORLD + west build -p -b $board/$soc_name/ns $HELLO_WORLD fi rm -rf $NCS_BASE/boards/testvnd