Skip to content
Closed
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
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
3 changes: 3 additions & 0 deletions Buildscripts/DevicetreeCompiler/source/exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class DevicetreeException(Exception):
def __init__(self, message):
super().__init__(message)
47 changes: 34 additions & 13 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,12 +57,14 @@ 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":
return property.value
elif type == "int":
return property.value
elif type == "boolean":
return "true"
elif type == "text":
Expand All @@ -72,29 +74,48 @@ def property_to_string(property: DeviceProperty, devices: list[Device]) -> str:
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.type == "bool":
result[index] = "false"
elif binding_property.required:
raise DevicetreeException(f"device {device.node_name} doesn't have property '{binding_property.name}'")
elif 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)
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 +142,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 +169,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
58 changes: 32 additions & 26 deletions Buildscripts/DevicetreeCompiler/source/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,44 @@
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}")
raise DevicetreeException(f"Directory not found: {config_path}")

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(f"Bindings found:")
for binding_file in binding_files:
print(f" {binding_file}")
if verbose:
print(f"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
4 changes: 0 additions & 4 deletions Documentation/ideas.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
## Higher Priority

- Make a root device type so it can be discovered more easily.
- DTS/yaml: Consider support for default values.
- DTS: throw custom exceptions and catch them to show cleaner error messages.
- When device.py selects a new device, it should automatically delete the build dirs (build/, cmake-*/) when it detects that the platform has changed.
- Add font design tokens such as "regular", "title" and "smaller". Perhaps via the LVGL kernel module.
- Add kernel listening mechanism so that the root device init can be notified when a device becomes available:
Expand Down Expand Up @@ -69,8 +67,6 @@

## Lower Priority

- Rename `Lock::lock()` and `Lock::unlock()` to `Lock::acquire()` and `Lock::release()`?
- Implement system suspend that turns off the screen
- The boot button on some devices can be used as GPIO_NUM_0 at runtime
- Localize all apps
- Support hot-plugging SD card (note: this is not possible if they require the CS pin hack)
Expand Down
4 changes: 4 additions & 0 deletions Platforms/PlatformEsp32/Bindings/espressif,esp32-i2c.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,20 @@ compatible: "espressif,esp32-i2c"
properties:
port:
type: int
required: true
description: |
The port number, defined by i2c_port_t.
Depending on the hardware, these values are available: I2C_NUM_0, I2C_NUM_1, LP_I2C_NUM_0
clock-frequency:
type: int
required: true
description: Initial clock frequency in Hz
pin-sda:
type: int
required: true
pin-scl:
type: int
required: true
pin-sda-pull-up:
type: bool
description: enable internal pull-up resistor for SDA pin
Expand Down
6 changes: 3 additions & 3 deletions Platforms/PlatformEsp32/Bindings/espressif,esp32-i2s.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ properties:
description: WS pin
pin-data-out:
type: int
required: true
description: DATA OUT pin
pin-data-in:
type: int
required: true
required: false
description: DATA IN pin
pin-mclk:
type: int
required: true
required: false
default: GPIO_PIN_NONE
description: MCLK pin
3 changes: 3 additions & 0 deletions Platforms/PlatformEsp32/Bindings/espressif,esp32-spi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@ properties:
description: Clock pin
pin-wp:
type: int
default: GPIO_PIN_NONE
description: WP (Data 2) pin
pin-hd:
type: int
default: GPIO_PIN_NONE
description: HD (Data 3) pin
max-transfer-size:
type: int
default: 0
description: |
Data transfer size limit in bytes.
0 means the platform decides the limit.
2 changes: 2 additions & 0 deletions Platforms/PlatformEsp32/Bindings/espressif,esp32-uart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ properties:
description: RX pin
pin-cts:
type: int
default: GPIO_PIN_NONE
description: CTS pin
pin-rts:
type: int
default: GPIO_PIN_NONE
description: RTS pin
Loading