| # Copyright (c) 2021-2024 Project CHIP Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| import importlib.util |
| import logging |
| import os |
| from enum import Enum, auto |
| from typing import Optional |
| |
| from .builder import BuilderOutput |
| from .gn import GnBuilder |
| |
| |
| class NxpOsUsed(Enum): |
| FREERTOS = auto() |
| ZEPHYR = auto() |
| |
| def OsEnv(self): |
| if self == NxpOsUsed.ZEPHYR: |
| return 'zephyr' |
| elif self == NxpOsUsed.FREERTOS: |
| return 'freertos' |
| else: |
| raise Exception('Unknown OS type: %r' % self) |
| |
| |
| class NxpBuildSystem(Enum): |
| GN = auto() |
| CMAKE = auto() |
| |
| def BuildSystem(self): |
| if self == NxpBuildSystem.GN: |
| return 'gn' |
| elif self == NxpBuildSystem.CMAKE: |
| return 'cmake' |
| else: |
| raise Exception('Unknown build system: %r' % self) |
| |
| |
| class NxpBoard(Enum): |
| K32W0 = auto() |
| K32W1 = auto() |
| RT1060 = auto() |
| RT1170 = auto() |
| RW61X = auto() |
| RW61X_ETH = auto() |
| MCXW71 = auto() |
| |
| def Name(self, os_env): |
| if self == NxpBoard.K32W0: |
| return 'k32w0x' |
| elif self == NxpBoard.K32W1: |
| return 'k32w1' |
| elif self == NxpBoard.RT1060: |
| return 'rt1060' |
| elif self == NxpBoard.RT1170: |
| return 'rt1170' |
| elif (self == NxpBoard.RW61X) or (self == NxpBoard.RW61X_ETH): |
| if os_env == NxpOsUsed.ZEPHYR: |
| if self == NxpBoard.RW61X_ETH: |
| return 'rd_rw612_bga/rw612/ethernet' |
| else: |
| return 'rd_rw612_bga' |
| else: |
| return 'rw61x' |
| elif self == NxpBoard.MCXW71: |
| return 'mcxw71' |
| else: |
| raise Exception('Unknown board type: %r' % self) |
| |
| def FolderName(self, os_env): |
| if self == NxpBoard.K32W0: |
| return 'k32w0' |
| elif self == NxpBoard.K32W1: |
| return 'k32w1' |
| elif self == NxpBoard.RT1060: |
| return 'rt/rt1060' |
| elif self == NxpBoard.RT1170: |
| return 'rt/rt1170' |
| elif (self == NxpBoard.RW61X) or (self == NxpBoard.RW61X_ETH): |
| if os_env == NxpOsUsed.ZEPHYR: |
| return 'zephyr' |
| else: |
| return 'rt/rw61x' |
| elif self == NxpBoard.MCXW71: |
| return 'mcxw71' |
| else: |
| raise Exception('Unknown board type: %r' % self) |
| |
| |
| class NxpBoardVariant(Enum): |
| RD = auto() |
| FRDM = auto() |
| EVKC = auto() |
| EVKB = auto() |
| |
| def BoardVariantName(self, board): |
| if self == NxpBoardVariant.RD: |
| return "rdrw612bga" |
| elif self == NxpBoardVariant.FRDM: |
| return "frdm" |
| elif self == NxpBoardVariant.EVKC: |
| return "evkcmimxrt1060" |
| |
| |
| class NxpApp(Enum): |
| LIGHTING = auto() |
| CONTACT = auto() |
| ALLCLUSTERS = auto() |
| LAUNDRYWASHER = auto() |
| THERMOSTAT = auto() |
| LOCK_APP = auto() |
| |
| def ExampleName(self): |
| if self == NxpApp.LIGHTING: |
| return 'lighting-app' |
| elif self == NxpApp.CONTACT: |
| return "contact-sensor-app" |
| elif self == NxpApp.ALLCLUSTERS: |
| return "all-clusters-app" |
| elif self == NxpApp.LAUNDRYWASHER: |
| return "laundry-washer-app" |
| elif self == NxpApp.THERMOSTAT: |
| return "thermostat" |
| elif self == NxpApp.LOCK_APP: |
| return "lock-app" |
| else: |
| raise Exception('Unknown app type: %r' % self) |
| |
| def NameSuffix(self): |
| if self == NxpApp.LIGHTING: |
| return 'light-example' |
| elif self == NxpApp.CONTACT: |
| return 'contact-example' |
| elif self == NxpApp.ALLCLUSTERS: |
| return "all-cluster-example" |
| elif self == NxpApp.LAUNDRYWASHER: |
| return "laundry-washer-example" |
| elif self == NxpApp.THERMOSTAT: |
| return "thermostat-example" |
| elif self == NxpApp.LOCK_APP: |
| return "lock-example" |
| else: |
| raise Exception('Unknown app type: %r' % self) |
| |
| def BuildRoot(self, root, board, os_env): |
| return os.path.join(root, 'examples', self.ExampleName(), 'nxp', board.FolderName(os_env)) |
| |
| |
| class NxpLogLevel(Enum): |
| DEFAULT = auto() # default everything |
| ALL = auto() # enable all logging |
| PROGRESS = auto() # progress and above |
| ERROR = auto() # error and above |
| NONE = auto() # no chip_logging at all |
| |
| |
| class NxpBuilder(GnBuilder): |
| |
| def __init__(self, |
| root, |
| runner, |
| app: NxpApp = NxpApp.LIGHTING, |
| board: NxpBoard = NxpBoard.K32W0, |
| board_variant: NxpBoardVariant = None, |
| os_env: NxpOsUsed = NxpOsUsed.FREERTOS, |
| build_system: NxpBuildSystem = NxpBuildSystem.GN, |
| low_power: bool = False, |
| smu2: bool = False, |
| enable_factory_data: bool = False, |
| convert_dac_pk: bool = False, |
| use_fro32k: bool = False, |
| enable_lit: bool = False, |
| enable_rotating_id: bool = False, |
| has_sw_version_2: bool = False, |
| disable_ble: bool = False, |
| enable_thread: bool = False, |
| enable_wifi: bool = False, |
| enable_ethernet: bool = False, |
| enable_shell: bool = False, |
| enable_ota: bool = False, |
| data_model_interface: Optional[str] = None, |
| enable_factory_data_build: bool = False, |
| disable_pairing_autostart: bool = False, |
| iw416_transceiver: bool = False, |
| w8801_transceiver: bool = False, |
| iwx12_transceiver: bool = False, |
| log_level: NxpLogLevel = NxpLogLevel.DEFAULT, |
| ): |
| super(NxpBuilder, self).__init__( |
| root=app.BuildRoot(root, board, os_env), |
| runner=runner) |
| self.code_root = root |
| self.app = app |
| self.board = board |
| self.os_env = os_env |
| self.build_system = build_system |
| self.low_power = low_power |
| self.smu2 = smu2 |
| self.enable_factory_data = enable_factory_data |
| self.convert_dac_pk = convert_dac_pk |
| self.use_fro32k = use_fro32k |
| self.enable_lit = enable_lit |
| self.enable_rotating_id = enable_rotating_id |
| self.has_sw_version_2 = has_sw_version_2 |
| self.disable_ble = disable_ble |
| self.enable_thread = enable_thread |
| self.enable_wifi = enable_wifi |
| self.enable_ethernet = enable_ethernet |
| self.enable_ota = enable_ota |
| self.enable_shell = enable_shell |
| self.data_model_interface = data_model_interface |
| self.enable_factory_data_build = enable_factory_data_build |
| self.disable_pairing_autostart = disable_pairing_autostart |
| self.board_variant = board_variant |
| self.iw416_transceiver = iw416_transceiver |
| self.w8801_transceiver = w8801_transceiver |
| self.iwx12_transceiver = iwx12_transceiver |
| if self.low_power and log_level != NxpLogLevel.NONE: |
| logging.warning("Switching log level to 'NONE' for low power build") |
| log_level = NxpLogLevel.NONE |
| self.log_level = log_level |
| |
| def GnBuildArgs(self): |
| args = [] |
| |
| if self.low_power: |
| args.append('chip_with_low_power=1') |
| if self.board == NxpBoard.K32W0: |
| args.append('chip_pw_tokenizer_logging=false chip_with_OM15082=0') |
| |
| if self.smu2: |
| args.append('use_smu2_static=true use_smu2_dynamic=true') |
| |
| if self.enable_factory_data: |
| args.append('chip_with_factory_data=1') |
| |
| if self.convert_dac_pk: |
| args.append('chip_convert_dac_private_key=1') |
| |
| if self.use_fro32k: |
| args.append('use_fro_32k=1') |
| |
| if self.enable_lit: |
| args.append('chip_enable_icd_lit=true') |
| |
| if self.enable_rotating_id: |
| args.append('chip_enable_rotating_device_id=1 chip_enable_additional_data_advertising=1') |
| |
| if self.log_level == NxpLogLevel.DEFAULT: |
| pass |
| elif self.log_level == NxpLogLevel.ALL: |
| args.append("chip_logging=true") |
| args.append("chip_error_logging=true") |
| args.append("chip_progress_logging=true") |
| args.append("chip_detail_logging=true") |
| args.append("chip_automation_logging=true") |
| elif self.log_level == NxpLogLevel.PROGRESS: |
| args.append("chip_logging=true") |
| args.append("chip_error_logging=true") |
| args.append("chip_progress_logging=true") |
| args.append("chip_detail_logging=false") |
| args.append("chip_automation_logging=false") |
| elif self.log_level == NxpLogLevel.ERROR: |
| args.append("chip_logging=true") |
| args.append("chip_error_logging=true") |
| args.append("chip_progress_logging=false") |
| args.append("chip_detail_logging=false") |
| args.append("chip_automation_logging=false") |
| elif self.log_level == NxpLogLevel.NONE: |
| args.append("chip_logging=false") |
| else: |
| raise Exception("Unknown log level: %r", self.log_level) |
| |
| if self.has_sw_version_2: |
| args.append('nxp_software_version=2') |
| |
| if self.enable_ota: |
| # OTA is enabled by default on kw32 |
| if self.board == NxpBoard.RW61X: |
| args.append('chip_enable_ota_requestor=true no_mcuboot=false') |
| |
| if self.enable_wifi: |
| args.append('chip_enable_wifi=true') |
| |
| if self.disable_ble: |
| args.append('chip_enable_ble=false') |
| |
| if self.enable_shell: |
| args.append('chip_enable_matter_cli=true') |
| |
| if self.enable_thread: |
| # thread is enabled by default on kw32 |
| if self.board == NxpBoard.RW61X: |
| args.append('chip_enable_openthread=true chip_inet_config_enable_ipv4=false') |
| if self.board == NxpBoard.RT1060: |
| args.append('chip_enable_openthread=true chip_inet_config_enable_ipv4=false') |
| if self.board == NxpBoard.RT1170: |
| args.append('chip_enable_openthread=true chip_inet_config_enable_ipv4=false') |
| |
| if self.data_model_interface is not None: |
| args.append(f'chip_use_data_model_interface="{self.data_model_interface}"') |
| |
| if self.board_variant: |
| if self.board == NxpBoard.RT1060: |
| flag_board_variant = "evkname=\\\"%s\\\"" % self.board_variant.BoardVariantName(self.board) |
| args.append(flag_board_variant) |
| if self.board == NxpBoard.RW61X: |
| flag_board_variant = "board_version=\\\"%s\\\"" % self.board_variant.BoardVariantName(self.board) |
| args.append(flag_board_variant) |
| |
| if self.iw416_transceiver: |
| args.append('iw416_transceiver=true') |
| |
| if self.w8801_transceiver: |
| # BLE not supported on this transceiver |
| args.append('w8801_transceiver=true chip_enable_ble=false') |
| |
| if self.iwx12_transceiver: |
| args.append('iwx12_transceiver=true') |
| |
| return args |
| |
| def CmakeBuildFlags(self): |
| flags = [] |
| |
| if self.enable_factory_data: |
| if self.os_env == NxpOsUsed.ZEPHYR: |
| flags.append('-DFILE_SUFFIX=fdata') |
| else: |
| flags.append("-DCONFIG_CHIP_FACTORY_DATA=true") |
| |
| if self.enable_ethernet: |
| if self.os_env == NxpOsUsed.ZEPHYR: |
| flags.append('-DEXTRA_CONF_FILE="prj_ethernet.conf"') |
| |
| if self.has_sw_version_2: |
| flags.append("-DCONFIG_CHIP_DEVICE_SOFTWARE_VERSION=2") |
| flags.append("-DCONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING=\"2.0\"") |
| |
| if self.data_model_interface: |
| # NOTE: this is not supporting "check" |
| enabled = "y" if self.data_model_interface.lower() == "enabled" else "n" |
| flags.append(f"-DCONFIG_USE_CHIP_DATA_MODEL_INTERFACE={enabled}") |
| |
| if self.enable_ota: |
| flags.append("-DCONFIG_CHIP_OTA_REQUESTOR=true") |
| if self.os_env == NxpOsUsed.FREERTOS and self.board == NxpBoard.RW61X: |
| flags.append("-DCONFIG_BOOTLOADER_MCUBOOT=true") |
| |
| if self.disable_ble: |
| flags.append("-DCONFIG_BT=false") |
| |
| if self.enable_wifi: |
| flags.append('-DCONFIG_CHIP_WIFI=true') |
| |
| if self.enable_thread: |
| flags.append("-DCONFIG_NET_L2_OPENTHREAD=true -DCONFIG_CHIP_IPV4=false") |
| |
| if self.enable_factory_data_build: |
| # Generate the factory data binary |
| flags.append("-DCONFIG_CHIP_FACTORY_DATA_BUILD=true -DCONFIG_CHIP_FACTORY_DATA=true") |
| |
| if self.enable_shell: |
| flags.append("-DCONFIG_CHIP_LIB_SHELL=true") |
| |
| if self.disable_pairing_autostart: |
| flags.append('-DCONFIG_CHIP_ENABLE_PAIRING_AUTOSTART=false') |
| |
| if self.board_variant: |
| flag_board_variant = "-DCONFIG_BOARD_VARIANT=\"%s\"" % self.board_variant.BoardVariantName(self.board) |
| flags.append(flag_board_variant) |
| |
| build_flags = " ".join(flags) |
| |
| return build_flags |
| |
| def generate(self): |
| build_flags = self.CmakeBuildFlags() |
| if self.os_env == NxpOsUsed.ZEPHYR: |
| if 'ZEPHYR_NXP_SDK_INSTALL_DIR' not in os.environ: |
| raise Exception("ZEPHYR_NXP_SDK_INSTALL_DIR need to be set") |
| |
| if 'ZEPHYR_NXP_BASE' not in os.environ: |
| raise Exception("ZEPHYR_NXP_BASE need to be set") |
| |
| cmd = 'export ZEPHYR_SDK_INSTALL_DIR="$ZEPHYR_NXP_SDK_INSTALL_DIR"' |
| cmd += '\nexport ZEPHYR_BASE="$ZEPHYR_NXP_BASE"' |
| cmd += '\nunset ZEPHYR_TOOLCHAIN_VARIANT' |
| |
| cmd += '\nwest build -p --cmake-only -b {board_name} -d {out_folder} {example_folder} {build_flags}'.format( |
| board_name=self.board.Name(self.os_env), |
| out_folder=self.output_dir, |
| example_folder=self.app.BuildRoot(self.code_root, self.board, self.os_env), |
| build_flags=build_flags) |
| self._Execute(['bash', '-c', cmd], title='Generating ' + self.identifier) |
| else: |
| cmd = '' |
| # will be used with next sdk version to get sdk path |
| if 'NXP_UPDATE_SDK_SCRIPT_DOCKER' in os.environ: |
| # Dynamic import of a python file to get platforms sdk path |
| spec = importlib.util.spec_from_file_location("None", os.environ['NXP_UPDATE_SDK_SCRIPT_DOCKER']) |
| module = importlib.util.module_from_spec(spec) |
| spec.loader.exec_module(module) |
| |
| for p in module.ALL_PLATFORM_SDK: |
| if p.sdk_name == 'k32w0': |
| cmd += 'export NXP_K32W0_SDK_ROOT="' + str(p.sdk_storage_location_abspath) + '" \n ' |
| elif p.sdk_name == 'common': |
| cmd += 'export NXP_SDK_ROOT="' + str(p.sdk_storage_location_abspath) + '" \n ' |
| |
| if self.build_system == NxpBuildSystem.CMAKE: |
| cmd += ''' |
| cmake -GNinja {build_flags} -H{example_folder} -B{out_folder} |
| '''.format( |
| build_flags=build_flags, |
| example_folder=self.app.BuildRoot(self.code_root, self.board, self.os_env), |
| out_folder=self.output_dir).strip() |
| self._Execute(['bash', '-c', cmd], title='Generating ' + self.identifier) |
| |
| elif self.build_system == NxpBuildSystem.GN: |
| # add empty space at the end to avoid concatenation issue when there is no --args |
| cmd += 'gn gen --check --fail-on-unused-args --export-compile-commands --root=%s ' % self.root |
| |
| extra_args = [] |
| |
| if self.options.pw_command_launcher: |
| extra_args.append('pw_command_launcher="%s"' % self.options.pw_command_launcher) |
| |
| if self.options.enable_link_map_file: |
| extra_args.append('chip_generate_link_map_file=true') |
| |
| if self.options.pregen_dir: |
| extra_args.append('chip_code_pre_generated_directory="%s"' % self.options.pregen_dir) |
| |
| extra_args.extend(self.GnBuildArgs() or []) |
| if extra_args: |
| cmd += ' --args="%s' % ' '.join(extra_args) + '" ' |
| |
| cmd += self.output_dir |
| |
| title = 'Generating ' + self.identifier |
| |
| self._Execute(['bash', '-c', cmd], title=title) |
| |
| def build_outputs(self): |
| name = 'chip-%s-%s' % (self.board.Name(self.os_env), self.app.NameSuffix()) |
| if self.os_env == NxpOsUsed.ZEPHYR: |
| yield BuilderOutput( |
| os.path.join(self.output_dir, 'zephyr', 'zephyr.elf'), |
| f'{name}.elf') |
| if self.options.enable_link_map_file: |
| yield BuilderOutput( |
| os.path.join(self.output_dir, 'zephyr', 'zephyr.map'), |
| f'{name}.map') |
| else: |
| if self.build_system == NxpBuildSystem.GN: |
| yield BuilderOutput( |
| os.path.join(self.output_dir, name), |
| f'{name}.elf') |
| if self.options.enable_link_map_file: |
| yield BuilderOutput( |
| os.path.join(self.output_dir, f'{name}.map'), |
| f'{name}.map') |
| elif self.build_system == NxpBuildSystem.CMAKE: |
| yield BuilderOutput( |
| os.path.join(self.output_dir, 'out/debug', name), |
| f'{name}.elf') |
| if self.options.enable_link_map_file: |
| yield BuilderOutput( |
| os.path.join(self.output_dir, 'out/debug', f'{name}.map'), |
| f'{name}.map') |