Skip to content

Commit

Permalink
scripts: west: genboard: use JSON schema
Browse files Browse the repository at this point in the history
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 <gerard.marull@nordicsemi.no>
  • Loading branch information
gmarull committed Nov 15, 2024
1 parent 0a95881 commit 6cac861
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 75 deletions.
1 change: 1 addition & 0 deletions scripts/requirements-base.txt
Original file line number Diff line number Diff line change
@@ -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
78 changes: 43 additions & 35 deletions scripts/west_commands/genboard/ncs_genboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -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
Expand All @@ -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 = {
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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")
Expand All @@ -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"]:
Expand All @@ -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")
54 changes: 54 additions & 0 deletions scripts/west_commands/genboard/schema.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
77 changes: 37 additions & 40 deletions scripts/west_commands/genboard/west-ncs-genboard-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,61 @@ 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"

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
Expand Down

0 comments on commit 6cac861

Please sign in to comment.