image builder: Add ability to run checks on BSP level

The 'checks' section that was available for block definition YAMLs is
now also available at the BSP level.
This commit is contained in:
Martin Braun 2024-06-19 15:33:51 +02:00 committed by joergho
parent 1ba6fdaf9c
commit d0e9a8d8fe
2 changed files with 96 additions and 78 deletions

View File

@ -142,9 +142,7 @@ class ImageBuilderConfig:
raise ValueError(failure)
def _sort_modules(self):
"""
Sort blocks in the desired order.
"""
""" Sort blocks in the desired order. """
for sort_modules in ('noc_blocks', 'modules', 'transport_adapters'):
module_dict = getattr(self, sort_modules)
setattr(self, sort_modules,
@ -152,9 +150,7 @@ class ImageBuilderConfig:
def _attach_defs(self, known_modules):
"""
Attach the block/module definitions to the individual block/module
instances.
"""Attach the block/module definitions to the individual block/module instances.
"""
for module_type, defs in known_modules.items():
if module_type == "includes":
@ -175,6 +171,20 @@ class ImageBuilderConfig:
logging.warning(
"%s contains unknown parameter(s): %s",
name, ", ".join(invalid_keys))
# Also set parameters for the device
default_params = getattr(self.device, 'parameters', {})
self.parameters = {
**default_params,
**{k: v for k, v in self.parameters.items() if k in default_params}
}
invalid_keys = [k for k in self.parameters if k not in default_params]
if invalid_keys:
logging.warning(
"Device contains unknown parameter(s): %s",
", ".join(invalid_keys))
setattr(self.device, 'parameters', self.parameters)
# For the sake of symmetry, create a desc attribute on self.device as a copy of itself
setattr(self.device, 'desc', self.device)
def _annotate_modules(self):
"""Add information to all the module types.
@ -332,33 +342,32 @@ class ImageBuilderConfig:
sep["buff_size"] = sep["buff_size_bytes"] // (sep["chdr_width"] // 8)
elif "buff_size" in sep:
sep["buff_size_bytes"] = sep["buff_size"] * (sep["chdr_width"] // 8)
# Run checks on noc blocks/modules/transport adapters
for module_type in ("noc_blocks", "modules", "transport_adapters"):
for module_name, module in getattr(self, module_type).items():
checks = getattr(module.desc, "checks", [])
for check in checks:
if "condition" in check:
if not bool(resolve(check["condition"], config=self, **module)):
if "message" in check:
failures += [
f"Condition failed for {module_name}: "
+ resolve(check["message"], config=self, **module)
]
else:
failures += [
f"Condition failed for {module_name}: {check['condition']}"
]
if "run" in check:
try:
resolve(check["run"], config=self, **module)
except Exception as ex:
if "message" in check:
failures += [
f"Condition failed for {module_name}: "
+ resolve(check["message"], config=self, **module, error=ex)
]
else:
failures += [f"Condition failed for {module_name}: {str(ex)}"]
# Run checks on noc blocks/modules/transport adapters/device
for module_name, module in self.get_module_list("all").items():
checks = getattr(module.desc, "checks", [])
for check in checks:
if "condition" in check:
if not bool(resolve(check["condition"], config=self, **module)):
if "message" in check:
msg = f"Condition failed for {module_name}: " \
+ resolve(check["message"], config=self, **module)
else:
msg = f"Condition failed for {module_name}: {check['condition']}"
if check.get("severity", "error") == "warning":
logging.warning(msg)
else:
failures += [msg]
if "run" in check:
try:
resolve(check["run"], config=self, **module)
except Exception as ex:
if "message" in check:
failures += [
f"Condition failed for {module_name}: "
+ resolve(check["message"], config=self, **module, error=ex)
]
else:
failures += [f"Condition failed for {module_name}: {str(ex)}"]
# Handle any errors
if failures:
logging.error("The image core configuration is invalid:")
@ -956,52 +965,51 @@ class ImageBuilderConfig:
self.connections[conn_idx] = con
# Go through all the modules and check IO ports are connected
for module_type in ("noc_blocks", "modules", "transport_adapters"):
for module_name, module in getattr(self, module_type).items():
for io_port_name, io_port in module.io_ports.items():
required = io_port.get("required")
if required:
is_connected = any(
con["srcblk"] == module_name
and con["srcport"] == io_port_name
or con["dstblk"] == module_name
and con["dstport"] == io_port_name
for con in self.connections
)
if not is_connected:
available_ports = self._get_available_ports(io_port)
msg = f"IO port {module_name}.{io_port_name} is not connected. "
if len(available_ports) == 1:
msg += f"Suggested connection: " \
f"{available_ports[0][0]}.{available_ports[0][1]}\n"
msg += f"Add the following connection to the image core file to " \
f"use this connection:\n"
if available_ports[0][2] == "src":
msg += f"{{ srcblk: {available_ports[0][0]}, " \
f"srcport: {available_ports[0][1]}, dstblk: " \
f"{module_name}, dstport: {io_port_name} }}"
else:
msg += f"{{ srcblk: {module_name}, srcport: {io_port_name}, " \
f"dstblk: {available_ports[0][0]}, dstport: " \
f"{available_ports[0][1]} }}"
elif len(available_ports) > 1:
msg += f"Available connections:\n"
for port in available_ports:
msg += f" {port[0]}.{port[1]}\n"
msg += "Add a connection line to the image core file to enable " \
"this connection:\n"
if available_ports[0][2] == "src":
msg += f"{{ srcblk: ..., srcport: ..., dstblk: " \
f"{module_name}, dstport: {io_port_name} }}"
else:
msg += f"{{ srcblk: {module_name}, srcport: {io_port_name}, " \
f"dstblk: ..., dstport: ... }}"
for module_name, module in self.get_module_list("all").items():
for io_port_name, io_port in module.io_ports.items():
required = io_port.get("required")
if required:
is_connected = any(
con["srcblk"] == module_name
and con["srcport"] == io_port_name
or con["dstblk"] == module_name
and con["dstport"] == io_port_name
for con in self.connections
)
if not is_connected:
available_ports = self._get_available_ports(io_port)
msg = f"IO port {module_name}.{io_port_name} is not connected. "
if len(available_ports) == 1:
msg += f"Suggested connection: " \
f"{available_ports[0][0]}.{available_ports[0][1]}\n"
msg += f"Add the following connection to the image core file to " \
f"use this connection:\n"
if available_ports[0][2] == "src":
msg += f"{{ srcblk: {available_ports[0][0]}, " \
f"srcport: {available_ports[0][1]}, dstblk: " \
f"{module_name}, dstport: {io_port_name} }}"
else:
msg += f"No available IO ports found!"
if required == "recommended":
logging.warning(msg)
msg += f"{{ srcblk: {module_name}, srcport: {io_port_name}, " \
f"dstblk: {available_ports[0][0]}, dstport: " \
f"{available_ports[0][1]} }}"
elif len(available_ports) > 1:
msg += f"Available connections:\n"
for port in available_ports:
msg += f" {port[0]}.{port[1]}\n"
msg += "Add a connection line to the image core file to enable " \
"this connection:\n"
if available_ports[0][2] == "src":
msg += f"{{ srcblk: ..., srcport: ..., dstblk: " \
f"{module_name}, dstport: {io_port_name} }}"
else:
failure += msg
msg += f"{{ srcblk: {module_name}, srcport: {io_port_name}, " \
f"dstblk: ..., dstport: ... }}"
else:
msg += f"No available IO ports found!"
if required == "recommended":
logging.warning(msg)
else:
failure += msg
# Go through blocks and check all ports are connected
for block_name, block in self.noc_blocks.items():
for direction in ("inputs", "outputs"):

View File

@ -13,6 +13,7 @@ import json
import logging
import os
import sys
from collections.abc import Mapping
from collections import OrderedDict
from ruamel import yaml
@ -61,7 +62,7 @@ deprecated_port_name_map = {
# pylint: disable=too-few-public-methods
class IOConfig:
class IOConfig(Mapping):
"""
Class containing configuration from a yml file.
@ -78,6 +79,15 @@ class IOConfig:
self.__dict__.update(**config)
if hasattr(self, "io_ports"):
expand_io_port_desc(getattr(self, "io_ports"), signatures)
def __iter__(self):
return iter(self.__dict__)
def __getitem__(self, key):
return self.__dict__[key]
def __len__(self) -> int:
return len(self.__dict__)
# pylint: enable=too-few-public-methods
USRP3_TOP_DIR = os.path.join('usrp3', 'top')