Devicetree DTS and YAML format improvements (#492)

* **DevicetreeCompiler**
  * Binding properties now support default values.
  * Compiler returns meaningful exit codes and reports errors more clearly.
  * Stronger validation of device configurations with unified error handling.
  * Added integration tests and a dedicated Devicetree test workflow.

* **Changes**
  * Platform binding schemas updated: some fields made required, others gained explicit defaults.
  * Many device-tree files simplified by removing unused/placeholder pin and transfer-size entries.

* **Documentation**
  * Removed several outdated TODO items.
This commit is contained in:
Ken Van Hoeylandt
2026-02-09 17:38:06 +01:00
committed by GitHub
parent d27404964a
commit 93efadd5e3
57 changed files with 257 additions and 283 deletions
@@ -1,7 +1,7 @@
import os.path
from textwrap import dedent
from source.models import *
from .exception import DevicetreeException
def write_include(file, include: IncludeC, verbose: bool):
if verbose:
@@ -26,9 +26,9 @@ def get_device_node_name_safe(device: Device):
def get_device_type_name(device: Device, bindings: list[Binding]):
device_binding = find_device_binding(device, bindings)
if device_binding is None:
raise Exception(f"Binding not found for {device.node_name}")
raise DevicetreeException(f"Binding not found for {device.node_name}")
if device_binding.compatible is None:
raise Exception(f"Couldn't find compatible binding for {device.node_name}")
raise DevicetreeException(f"Couldn't find compatible binding for {device.node_name}")
compatible_safe = device_binding.compatible.split(",")[-1]
return compatible_safe.replace("-", "_")
@@ -41,7 +41,7 @@ def find_device_property(device: Device, name: str) -> DeviceProperty:
def find_device_binding(device: Device, bindings: list[Binding]) -> Binding:
compatible_property = find_device_property(device, "compatible")
if compatible_property is None:
raise Exception(f"property 'compatible' not found in device {device.node_name}")
raise DevicetreeException(f"property 'compatible' not found in device {device.node_name}")
for binding in bindings:
if binding.compatible == compatible_property.value:
return binding
@@ -57,44 +57,69 @@ def find_phandle(devices: list[Device], phandle: str):
for device in devices:
if device.node_name == phandle or device.node_alias == phandle:
return f"&{get_device_node_name_safe(device)}"
raise Exception(f"phandle '{phandle}' not found in device tree")
raise DevicetreeException(f"phandle '{phandle}' not found in device tree")
def property_to_string(property: DeviceProperty, devices: list[Device]) -> str:
type = property.type
if type == "value":
if type == "value" or type == "int":
return property.value
elif type == "boolean":
elif type == "boolean" or type == "bool":
return "true"
elif type == "text":
return f"\"{property.value}\""
elif type == "values":
return "{ " + ",".join(property.value) + " }"
value_list = list()
for item in property.value:
if isinstance(item, PropertyValue):
value_list.append(property_to_string(DeviceProperty(name="", type=item.type, value=item.value), devices))
else:
value_list.append(str(item))
return "{ " + ",".join(value_list) + " }"
elif type == "phandle":
return find_phandle(devices, property.value)
else:
raise Exception(f"property_to_string() has an unsupported type: {type}")
raise DevicetreeException(f"property_to_string() has an unsupported type: {type}")
def resolve_parameters_from_bindings(device: Device, bindings: list[Binding], devices: list[Device]) -> list:
compatible_property = find_device_property(device, "compatible")
if compatible_property is None:
raise Exception(f"Cannot find 'compatible' property for {device.node_name}")
raise DevicetreeException(f"Cannot find 'compatible' property for {device.node_name}")
device_binding = find_binding(compatible_property.value, bindings)
if device_binding is None:
raise Exception(f"Binding not found for {device.node_name} and compatible '{compatible_property.value}'")
raise DevicetreeException(f"Binding not found for {device.node_name} and compatible '{compatible_property.value}'")
# Filter out system properties
binding_properties = []
binding_property_names = set()
for property in device_binding.properties:
if property.name != "compatible":
binding_properties.append(property)
binding_property_names.add(property.name)
# Check for invalid properties in device
for device_property in device.properties:
if device_property.name in ["compatible"]:
continue
if device_property.name not in binding_property_names:
raise DevicetreeException(f"Device '{device.node_name}' has invalid property '{device_property.name}'")
# Allocate total expected configuration arguments
result = [0] * len(binding_properties)
for index, binding_property in enumerate(binding_properties):
device_property = find_device_property(device, binding_property.name)
if device_property is None:
if binding_property.required:
raise Exception(f"device {device.node_name} doesn't have property '{binding_property.name}'")
if binding_property.default is not None:
temp_prop = DeviceProperty(
name=binding_property.name,
type=binding_property.type,
value=binding_property.default
)
result[index] = property_to_string(temp_prop, devices)
elif binding_property.required:
raise DevicetreeException(f"device {device.node_name} doesn't have property '{binding_property.name}'")
elif binding_property.type == "bool":
result[index] = "false"
else:
result[index] = '0'
raise DevicetreeException(f"Device {device.node_name} doesn't have property '{binding_property.name}' and no default value is set")
else:
result[index] = property_to_string(device_property, devices)
return result
@@ -121,7 +146,7 @@ def write_device_structs(file, device: Device, parent_device: Device, bindings:
type_name = get_device_type_name(device, bindings)
compatible_property = find_device_property(device, "compatible")
if compatible_property is None:
raise Exception(f"Cannot find 'compatible' property for {device.node_name}")
raise DevicetreeException(f"Cannot find 'compatible' property for {device.node_name}")
node_name = get_device_node_name_safe(device)
config_variable_name = f"{node_name}_config"
if parent_device is not None:
@@ -148,7 +173,7 @@ def write_device_init(file, device: Device, bindings: list[Binding], verbose: bo
# Assemble some pre-requisites
compatible_property = find_device_property(device, "compatible")
if compatible_property is None:
raise Exception(f"Cannot find 'compatible' property for {device.node_name}")
raise DevicetreeException(f"Cannot find 'compatible' property for {device.node_name}")
# Type & instance names
node_name = get_device_node_name_safe(device)
device_variable = node_name