Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
pull_request:
types: [ opened, synchronize, reopened ]
jobs:
run:
TactilityTests:
runs-on: ubuntu-latest
steps:
- name: "Checkout repo"
Expand All @@ -27,3 +27,17 @@ jobs:
run: build/Tests/Tactility/TactilityTests
- name: "Run TactilityKernel Tests"
run: build/Tests/TactilityKernel/TactilityKernelTests
DevicetreeTests:
runs-on: ubuntu-latest
steps:
- name: "Checkout repo"
uses: actions/checkout@v2
with:
submodules: recursive
- name: "Install Python Dependencies"
shell: bash
run: pip install lark==1.3.1 pyyaml==6.0.3
- name: "Run Devicetree Tests"
shell: bash
working-directory: Buildscripts/DevicetreeCompiler/tests
run: python test_integration.py
3 changes: 2 additions & 1 deletion Buildscripts/DevicetreeCompiler/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ def print_help():
is_verbose = "--verbose" in sys.argv
devicetree_yaml_config = args[0]
output_path = args[1]
main(devicetree_yaml_config, output_path, is_verbose)
result = main(devicetree_yaml_config, output_path, is_verbose)
sys.exit(result)

3 changes: 2 additions & 1 deletion Buildscripts/DevicetreeCompiler/source/binding_files.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from .exception import DevicetreeException

def find_bindings(directory_path: str) -> list[str]:
yaml_files = []
Expand All @@ -14,6 +15,6 @@ def find_all_bindings(directory_paths: list[str]) -> list[str]:
for directory_path in directory_paths:
new_paths = find_bindings(directory_path)
if len(new_paths) == 0:
raise Exception(f"No bindings found in {directory_path}")
raise DevicetreeException(f"No bindings found in {directory_path}")
yaml_files += new_paths
return yaml_files
1 change: 1 addition & 0 deletions Buildscripts/DevicetreeCompiler/source/binding_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def parse_binding(file_path: str, binding_dirs: list[str]) -> Binding:
type=details.get('type', 'unknown'),
required=details.get('required', False),
description=details.get('description', '').strip(),
default=details.get('default', None),
)
properties_dict[name] = prop
filename = os.path.basename(file_path)
Expand Down
2 changes: 2 additions & 0 deletions Buildscripts/DevicetreeCompiler/source/exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class DevicetreeException(Exception):
pass
57 changes: 41 additions & 16 deletions Buildscripts/DevicetreeCompiler/source/generator.py
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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("-", "_")

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand Down
59 changes: 33 additions & 26 deletions Buildscripts/DevicetreeCompiler/source/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,45 @@
from source.binding_files import find_all_bindings
from source.binding_parser import parse_binding
from source.config import *
from source.exception import DevicetreeException

def main(config_path: str, output_path: str, verbose: bool):
def main(config_path: str, output_path: str, verbose: bool) -> int:
print(f"Generating devicetree code\n config: {config_path}\n output: {output_path}")
if not os.path.isdir(config_path):
raise Exception(f"Directory not found: {config_path}")
print(f"Directory not found: {config_path}")
return 1

config = parse_config(config_path, os.getcwd())
if verbose:
pprint(config)

project_dir = os.path.dirname(os.path.realpath(__file__))
grammar_path = os.path.join(project_dir, "grammar.lark")
lark_data = read_file(grammar_path)
dts_data = read_file(config.dts)
lark = Lark(lark_data)
parsed = lark.parse(dts_data)
if verbose:
print(parsed.pretty())
transformed = DtsTransformer().transform(parsed)
if verbose:
pprint(transformed)
binding_files = find_all_bindings(config.bindings)
if verbose:
print(f"Bindings found:")
try:
project_dir = os.path.dirname(os.path.realpath(__file__))
grammar_path = os.path.join(project_dir, "grammar.lark")
lark_data = read_file(grammar_path)
dts_data = read_file(config.dts)
lark = Lark(lark_data)
parsed = lark.parse(dts_data)
if verbose:
print(parsed.pretty())
transformed = DtsTransformer().transform(parsed)
if verbose:
pprint(transformed)
binding_files = find_all_bindings(config.bindings)
if verbose:
print("Bindings found:")
for binding_file in binding_files:
print(f" {binding_file}")
if verbose:
print("Parsing bindings")
bindings = []
for binding_file in binding_files:
print(f" {binding_file}")
if verbose:
print(f"Parsing bindings")
bindings = []
for binding_file in binding_files:
bindings.append(parse_binding(binding_file, config.bindings))
if verbose:
for binding in bindings:
pprint(binding)
generate(output_path, transformed, bindings, verbose)
bindings.append(parse_binding(binding_file, config.bindings))
if verbose:
for binding in bindings:
pprint(binding)
generate(output_path, transformed, bindings, verbose)
return 0
except DevicetreeException as caught:
print("\033[31mError: ", caught, "\033[0m")
return 1
1 change: 1 addition & 0 deletions Buildscripts/DevicetreeCompiler/source/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class BindingProperty:
type: str
required: bool
description: str
default: object = None

@dataclass
class Binding:
Expand Down
7 changes: 4 additions & 3 deletions Buildscripts/DevicetreeCompiler/source/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from lark import Token
from source.models import *
from dataclasses import dataclass
from .exception import DevicetreeException

def flatten_token_array(tokens: List[Token], name: str):
result_list = list()
Expand All @@ -23,7 +24,7 @@ def start(self, tokens):
def dts_version(self, tokens: List[Token]):
version = tokens[0].value
if version != "dts-v1":
raise Exception(f"Unsupported DTS version: {version}")
raise DevicetreeException(f"Unsupported DTS version: {version}")
return DtsVersion(version)
def device(self, tokens: list):
node_name = None
Expand All @@ -46,12 +47,12 @@ def device_property(self, objects: List[object]):
if (len(objects) == 1) or (objects[1] is None):
return DeviceProperty(name, "boolean", True)
if type(objects[1]) is not PropertyValue:
raise Exception(f"Object was not converted to PropertyValue: {objects[1]}")
raise DevicetreeException(f"Object was not converted to PropertyValue: {objects[1]}")
return DeviceProperty(name, objects[1].type, objects[1].value)
def property_value(self, tokens: List):
token = tokens[0]
if type(token) is Token:
raise Exception(f"Failed to convert token to PropertyValue: {token}")
raise DevicetreeException(f"Failed to convert token to PropertyValue: {token}")
return token
def PHANDLE(self, token: Token):
return PropertyValue(type="phandle", value=token.value[1:])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
description: Test device binding
compatible: "test,device"
properties:
reg:
type: int
required: true
status:
type: string
boolean-prop:
type: boolean
int-prop:
type: int
string-prop:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
description: Test root binding
compatible: "test,root"
properties:
model:
type: string
2 changes: 2 additions & 0 deletions Buildscripts/DevicetreeCompiler/tests/data/devicetree.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dts: test.dts
bindings: bindings
17 changes: 17 additions & 0 deletions Buildscripts/DevicetreeCompiler/tests/data/test.dts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/dts-v1/;

#include <test_include.h>

/ {
compatible = "test,root";
model = "Test Model";

test_device: test-device@0 {
compatible = "test,device";
reg = <0>;
status = "okay";
boolean-prop;
int-prop = <42>;
string-prop = "hello";
};
};
Loading