dts: remove legacy macro support

The legacy macros were first deprecated in Zephyr v2.3. Now that
Zephyr v2.4 has been released, that makes two releases where these
macros have been deprecated, so it's OK to remove them.

This leaves support for legacy binding syntax in place. Removing that
is left to future work.

We need to update various pieces of documentation related to flash
partitions that never got updated when the new API was introduced.
Consolidate this information in the flash_map.h API reference page,
since that's really where users will run into it. This also gives us
the opportunity to improve this documentation.

Adjust a couple of kconfigfunctions.py and sanitycheck bits to use
non-legacy edtlib APIs.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2020-10-05 20:02:13 -07:00 committed by Maureen Helm
commit 8165008f44
37 changed files with 202 additions and 2873 deletions

View file

@ -342,13 +342,6 @@ config MAKEFILE_EXPORTS
Generates a file with build information that can be read by Generates a file with build information that can be read by
third party Makefile-based build systems. third party Makefile-based build systems.
config LEGACY_DEVICETREE_MACROS
bool "Allow use of legacy devicetree macros"
help
Allows use of legacy devicetree macros which were used in
Zephyr 2.2 and previous versions, rather than the devicetree.h
API introduced during the Zephyr 2.3 development cycle.
config DEPRECATED_ZEPHYR_INT_TYPES config DEPRECATED_ZEPHYR_INT_TYPES
bool "Allow the use of the deprecated zephyr integer types" bool "Allow the use of the deprecated zephyr integer types"
help help

View file

@ -189,7 +189,7 @@ Flash partitions for MCUBoot bootloader
*************************************** ***************************************
The on-board STM32F429ZI MCU has 2MBs of internal flash memory. To use `MCUboot`_, The on-board STM32F429ZI MCU has 2MBs of internal flash memory. To use `MCUboot`_,
define a :ref:`Zephyr partition table <legacy_flash_partitions>` for the flash memory in define a :ref:`Zephyr partition table <flash_map_api>` for the flash memory in
its devicetree file ``nucleo_f429zi.dts``. As a reference, a partition table for its devicetree file ``nucleo_f429zi.dts``. As a reference, a partition table for
MCUBoot is already defined in the devicetree file, with these settings: MCUBoot is already defined in the devicetree file, with these settings:

View file

@ -4,15 +4,11 @@ file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include/generated)
# Zephyr code can configure itself based on a KConfig'uration with the # Zephyr code can configure itself based on a KConfig'uration with the
# header file autoconf.h. There exists an analogous file devicetree_unfixed.h # header file autoconf.h. There exists an analogous file devicetree_unfixed.h
# that allows configuration based on information encoded in DTS, and a similar # that allows configuration based on information encoded in DTS.
# file with legacy contents called devicetree_unfixed_legacy.h.
# #
# Here we call on dtc, the gcc preprocessor, # Here we call on dtc, the gcc preprocessor and
# scripts/dts/gen_defines.py, and scripts/dts/gen_legacy_defines.py to # scripts/dts/gen_defines.py to generate various DT-related files at
# generate various DT-related files at CMake configure-time. # CMake configure-time.
#
# The devicetree.conf file is still needed by some deprecated
# functions in kconfigfunctions.py.
# #
# See the Devicetree user guide in the Zephyr documentation for details. # See the Devicetree user guide in the Zephyr documentation for details.
set(GEN_DEFINES_SCRIPT ${ZEPHYR_BASE}/scripts/dts/gen_defines.py) set(GEN_DEFINES_SCRIPT ${ZEPHYR_BASE}/scripts/dts/gen_defines.py)
@ -25,7 +21,6 @@ set(ZEPHYR_DTS ${PROJECT_BINARY_DIR}/zephyr.dts)
# and should not be made part of the documentation. # and should not be made part of the documentation.
set(EDT_PICKLE ${PROJECT_BINARY_DIR}/edt.pickle) set(EDT_PICKLE ${PROJECT_BINARY_DIR}/edt.pickle)
set(DEVICETREE_UNFIXED_H ${PROJECT_BINARY_DIR}/include/generated/devicetree_unfixed.h) set(DEVICETREE_UNFIXED_H ${PROJECT_BINARY_DIR}/include/generated/devicetree_unfixed.h)
set(DEVICETREE_UNFIXED_LEGACY_H ${PROJECT_BINARY_DIR}/include/generated/devicetree_legacy_unfixed.h)
set(DTS_POST_CPP ${PROJECT_BINARY_DIR}/${BOARD}.dts.pre.tmp) set(DTS_POST_CPP ${PROJECT_BINARY_DIR}/${BOARD}.dts.pre.tmp)
set_ifndef(DTS_SOURCE ${BOARD_DIR}/${BOARD}.dts) set_ifndef(DTS_SOURCE ${BOARD_DIR}/${BOARD}.dts)
@ -225,16 +220,6 @@ if(SUPPORTS_DTS)
--edt-pickle-out ${EDT_PICKLE} --edt-pickle-out ${EDT_PICKLE}
) )
#
# Run gen_legacy_defines.py to create a header file with legacy contents
# and a .conf file.
#
set(CMD_LEGACY_EXTRACT ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/dts/gen_legacy_defines.py
--edt-pickle ${EDT_PICKLE}
--header-out ${DEVICETREE_UNFIXED_LEGACY_H}
)
execute_process( execute_process(
COMMAND ${CMD_EXTRACT} COMMAND ${CMD_EXTRACT}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR} WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
@ -247,15 +232,6 @@ if(SUPPORTS_DTS)
message(STATUS "Generated devicetree_unfixed.h: ${DEVICETREE_UNFIXED_H}") message(STATUS "Generated devicetree_unfixed.h: ${DEVICETREE_UNFIXED_H}")
endif() endif()
execute_process(
COMMAND ${CMD_LEGACY_EXTRACT}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
RESULT_VARIABLE ret
)
if(NOT "${ret}" STREQUAL "0")
message(FATAL_ERROR "gen_legacy_defines.py failed with return code: ${ret}")
endif()
# A file that used to be generated by 'dtc'. zephyr.dts is the new # A file that used to be generated by 'dtc'. zephyr.dts is the new
# equivalent. Will be removed in Zephyr 2.3. # equivalent. Will be removed in Zephyr 2.3.
file(WRITE ${PROJECT_BINARY_DIR}/${BOARD}.dts_compiled file(WRITE ${PROJECT_BINARY_DIR}/${BOARD}.dts_compiled
@ -263,5 +239,4 @@ if(SUPPORTS_DTS)
else() else()
file(WRITE ${DEVICETREE_UNFIXED_H} "/* WARNING. THIS FILE IS AUTO-GENERATED. DO NOT MODIFY! */") file(WRITE ${DEVICETREE_UNFIXED_H} "/* WARNING. THIS FILE IS AUTO-GENERATED. DO NOT MODIFY! */")
file(WRITE ${DEVICETREE_UNFIXED_LEGACY_H} "/* WARNING. THIS FILE IS AUTO-GENERATED. DO NOT MODIFY! */")
endif(SUPPORTS_DTS) endif(SUPPORTS_DTS)

View file

@ -34,8 +34,8 @@ is the boot loader used with Zephyr. The source code itself is hosted in the
In order to use MCUboot with Zephyr you need to take the following into account: In order to use MCUboot with Zephyr you need to take the following into account:
1. You will need to define the :ref:`mcuboot_partitions` required by MCUboot in 1. You will need to define the flash partitions required by MCUboot; see
the :ref:`legacy_flash_partitions`. :ref:`flash_map_api` for details.
2. Your application's :file:`.conf` file needs to enable the 2. Your application's :file:`.conf` file needs to enable the
:option:`CONFIG_BOOTLOADER_MCUBOOT` Kconfig option in order for Zephyr to :option:`CONFIG_BOOTLOADER_MCUBOOT` Kconfig option in order for Zephyr to
be built in an MCUboot-compatible manner be built in an MCUboot-compatible manner

View file

@ -566,218 +566,6 @@ done in the :ref:`blinky-sample`. The application can then be configured in
:ref:`BOARD.dts <devicetree-in-out-files>` files or via :ref:`devicetree :ref:`BOARD.dts <devicetree-in-out-files>` files or via :ref:`devicetree
overlays <use-dt-overlays>`. overlays <use-dt-overlays>`.
.. _dt-migrate-legacy:
Migrate from the legacy macros
******************************
This section shows how to migrate from the :ref:`dt-legacy-macros` to the
:ref:`devicetree.h API <dt-from-c>`. (Please feel free to :ref:`ask for help
<help>` if a use case you need is missing here and existing documentation is
not enough to figure out what to do.)
This DTS is used for examples:
.. literalinclude:: ../../../tests/lib/devicetree/legacy_api/app.overlay
:language: DTS
:start-after: start-after-here
:end-before: end-before-here
The following shows equivalent ways to access this devicetree, using legacy
macros and the new devicetree.h API.
.. warning::
The INST numbers below were carefully chosen to work. Instance numbering
properties have changed in the devicetree.h API compared to the legacy
macros, and are not guaranteed to be the same in all cases. See
:c:func:`DT_INST` for details.
.. code-block:: c
/*
* label
*
* These use the label property in /migration/gpio@1000.
* They all expand to "MGR_GPIO".
*/
/* Legacy: */
DT_VND_GPIO_1000_LABEL
DT_INST_0_VND_GPIO_LABEL
DT_ALIAS_MGR_GPIO_LABEL
/* Use these instead: */
DT_LABEL(DT_PATH(migration, gpio_1000))
DT_LABEL(DT_INST(0, vnd_gpio))
DT_LABEL(DT_ALIAS(mgr_gpio))
DT_LABEL(DT_NODELABEL(migration_gpio))
/*
* reg base addresses and sizes
*
* These use the reg property in /migration/gpio@1000.
* The base addresses all expand to 0x1000, and sizes to 0x2000.
*/
/* Legacy addresses: */
DT_VND_GPIO_1000_BASE_ADDRESS
DT_INST_0_VND_GPIO_BASE_ADDRESS
DT_ALIAS_MGR_GPIO_BASE_ADDRESS
/* Use these instead: */
DT_REG_ADDR(DT_PATH(migration, gpio_1000))
DT_REG_ADDR(DT_INST(0, vnd_gpio))
DT_REG_ADDR(DT_ALIAS(mgr_gpio))
DT_REG_ADDR(DT_NODELABEL(migration_gpio))
/* Legacy sizes: */
DT_VND_GPIO_1000_SIZE
DT_INST_0_VND_GPIO_SIZE
DT_ALIAS_MGR_GPIO_SIZE
/* Use these instead: */
DT_REG_SIZE(DT_PATH(migration, gpio_1000))
DT_REG_SIZE(DT_INST(0, vnd_gpio))
DT_REG_SIZE(DT_ALIAS(mgr_gpio))
DT_REG_SIZE(DT_NODELABEL(migration_gpio))
/*
* interrupts IRQ numbers and priorities
*
* These use the interrupts property in /migration/gpio@1000.
* The interrupt number is 0, and the priority is 1.
*/
/* Legacy interrupt numbers: */
DT_VND_GPIO_1000_IRQ_0
DT_INST_0_VND_GPIO_IRQ_0
DT_ALIAS_MGR_GPIO_IRQ_0
/* Use these instead: */
DT_IRQN(DT_PATH(migration, gpio_1000))
DT_IRQN(DT_INST(0, vnd_gpio))
DT_IRQN(DT_ALIAS(mgr_gpio))
DT_IRQN(DT_NODELABEL(migration_gpio))
/* Legacy priorities: */
DT_VND_GPIO_1000_IRQ_0_PRIORITY,
DT_INST_0_VND_GPIO_IRQ_0_PRIORITY,
DT_ALIAS_MGR_GPIO_IRQ_0_PRIORITY,
/* Use these instead: */
DT_IRQ(DT_PATH(migration, gpio_1000), priority)
DT_IRQ(DT_INST(0, vnd_gpio), priority)
DT_IRQ(DT_ALIAS(mgr_gpio), priority)
DT_IRQ(DT_NODELABEL(migration_gpio), priority)
/*
* Other property access
*
* These use the baud-rate property in /migration/serial@3000.
* They all expand to 115200.
*/
/* Legacy: */
DT_VND_SERIAL_3000_BAUD_RATE
DT_ALIAS_MGR_SERIAL_BAUD_RATE
DT_INST_0_VND_SERIAL_BAUD_RATE
/* Use these instead: */
DT_PROP(DT_PATH(migration, serial_3000), baud_rate)
DT_PROP(DT_ALIAS(mgr_serial), baud_rate)
DT_PROP(DT_NODELABEL(migration_serial), baud_rate)
DT_PROP(DT_INST(0, vnd_serial), baud_rate)
/*
* I2C bus controller label access for an I2C peripheral device.
*
* These are different ways to get the bus controller label property
* from the peripheral device /migration/i2c@1000/i2c-dev-10.
*
* They all expand to "MGR_I2C".
*/
/* Legacy: */
DT_VND_I2C_10000_VND_I2C_DEVICE_10_BUS_NAME
DT_ALIAS_MGR_I2C_DEV_BUS_NAME
DT_INST_0_VND_I2C_DEVICE_BUS_NAME
/* Use these instead (the extra #defines are just for readability): */
#define I2C_DEV_PATH DT_PATH(migration, i2c_10000, i2c_dev_10)
#define I2C_DEV_ALIAS DT_ALIAS(mgr_i2c_dev)
#define I2C_DEV_NODELABEL DT_NODELABEL(mgr_i2c_device)
#define I2C_DEV_INST DT_INST(0, vnd_i2c_device)
DT_LABEL(DT_BUS(I2C_DEV_PATH))
DT_LABEL(DT_BUS(I2C_DEV_ALIAS)))
DT_LABEL(DT_BUS(I2C_DEV_NODELABEL)))
DT_LABEL(DT_BUS(I2C_DEV_INST)))
/*
* SPI device chip-select controller.
*
* These use /migration/spi@2000/spi-dev@0. They all expand to
* "MGR_GPIO", which is the label property of /migration/gpio@1000,
* which is the SPI device's chip select pin GPIO controller. This is
* taken from the parent node's cs-gpios property.
*/
/* Legacy */
DT_VND_SPI_20000_VND_SPI_DEVICE_0_CS_GPIOS_CONTROLLER
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_CONTROLLER
DT_INST_0_VND_SPI_DEVICE_CS_GPIOS_CONTROLLER
/* Use these instead (extra #defines just for readability): */
#define SPI_DEV_PATH DT_PATH(migration, spi_20000, migration_spi_dev_0)
#define SPI_DEV_ALIAS DT_ALIAS(mgr_spi_dev)
#define SPI_DEV_NODELABEL DT_NODELABEL(mgr_spi_device)
#define SPI_DEV_INST DT_INST(0, vnd_spi_device)
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_PATH)
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_ALIAS)
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_NODELABEL)
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_INST)
/*
* SPI device chip-select pin.
*
* These use /migration/spi@2000/spi-dev@0.
* They all expand to 17, which is also from cs-gpios.
*/
/* Legacy: */
DT_VND_SPI_20000_VND_SPI_DEVICE_0_CS_GPIOS_PIN
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_PIN
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_PIN
DT_INST_0_VND_SPI_DEVICE_CS_GPIOS_PIN
/* Use these instead (extra #defines from above): */
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_PATH)
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_ALIAS)
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_NODEPIN)
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_INST)
/*
* SPI device chip-select pin's flags for the gpio.h API.
*
* These use /migration/spi@2000/spi-dev@0. They all expand to
* GPIO_ACTIVE_LOW (technically, its numeric value after
* preprocessing), which is also from cs-gpios.
*/
/* Legacy: */
DT_VND_SPI_20000_VND_SPI_DEVICE_0_CS_GPIOS_FLAGS
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_FLAGS
DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_FLAGS
DT_INST_0_VND_SPI_DEVICE_CS_GPIOS_FLAGS
/* Use these instead (extra #defines from above): */
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_PATH)
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_ALIAS)
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_NODEFLAGS)
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_INST)
.. _dt-trouble: .. _dt-trouble:
Troubleshoot devicetree issues Troubleshoot devicetree issues

View file

@ -15,6 +15,5 @@ development. See :ref:`devicetree_api` for an API reference.
design.rst design.rst
bindings.rst bindings.rst
api-usage.rst api-usage.rst
legacy-macros.rst
howtos.rst howtos.rst
dt-vs-kconfig.rst dt-vs-kconfig.rst

View file

@ -336,6 +336,8 @@ interrupts
.. _Devicetree Specification release v0.3: .. _Devicetree Specification release v0.3:
https://www.devicetree.org/specifications/ https://www.devicetree.org/specifications/
.. _dt-alias-chosen:
Aliases and chosen nodes Aliases and chosen nodes
************************ ************************
@ -514,10 +516,6 @@ These are created in your application's build directory.
The generated macros and additional comments describing the devicetree. The generated macros and additional comments describing the devicetree.
Included by ``devicetree.h``. Included by ``devicetree.h``.
:file:`<build>/zephyr/include/generated/devicetree_legacy_unfixed.h`
The generated :ref:`dt-legacy-macros`.
Included by ``devicetree.h``.
:file:`<build>/zephyr/include/generated/devicetree_fixups.h` :file:`<build>/zephyr/include/generated/devicetree_fixups.h`
The concatenated contents of any :file:`dts_fixup.h` files. The concatenated contents of any :file:`dts_fixup.h` files.
Included by ``devicetree.h``. Included by ``devicetree.h``.

View file

@ -1,106 +0,0 @@
; dt-macro is the top level nonterminal. It defines the possible
; macros generated by gen_defines.py.
;
; A dt-macro starts with uppercase "DT_" followed by either:
;
; - a property-macro, generated for a particular node
; property
; - some other-macro, a catch-all for other types of macros,
; which contain either global information about the tree or
; are special cases
;
; This does *not* cover macros pulled out of DT via Kconfig,
; like CONFIG_SRAM_BASE_ADDRESS, etc.
dt-macro = %s"DT_" ( property-macro / other-macro )
; --------------------------------------------------------------------
; A property-macro is a sequence of:
;
; - node-id: a way to identify a node
; - property-id: a way to identify one of the node's properties
; - property-suf: an optional property-specific suffix
property-macro = node-id "_" property-id ["_" property-suf]
; A node-id is a way to refer to a node within the devicetree.
; There are a few different flavors.
node-id = compat-unit-id / inst-id / alias-id
compat-unit-id = [bus-id-part "_"] compat-id-part "_" unit-addr-id-part
inst-id = %s"INST_" 1*DIGIT "_" compat-id-part
alias-id = %s"ALIAS_" alias-id-part
; Various components of a property-macro are just c-idents,
; which are made of uppercase letters, numbers, and underscores.
;
; This is a problem, because it makes it possible for different nodes
; or properties in a devicetree to generate the same macro twice
; with different values.
bus-id-part = c-ident ; ID for information about a node's bus
compat-id-part = c-ident ; ID for a node's compatible
unit-addr-id-part = c-ident ; ID for a node's unit-address
alias-id-part = c-ident ; ID for an /aliases node property
property-id = c-ident ; ID for a node property -- this also
; covers special cases like "reg",
; "interrupts", and "cs-gpios" for now,
; as they all collide with non-special
; cases.
property-suf = c-ident ; a suffix for part of a property value,
; like an array index or a phandle
; specifier name converted to a c-ident
; --------------------------------------------------------------------
; An other-macro is a grab bag for everything that isn't a
; property-macro. It reuses some of the nonterminals (namely node-id
; and compat-id-part) defined above.
other-macro = existence-flag / bus-macro / flash-macro / chosen-macro
existence-flag = compat-existence-flag / inst-existence-flag
compat-flag = %s"COMPAT_" c-ident
inst-flag = %s"INST_" 1*DIGIT "_" c-ident
bus-macro = bus-name-macro / on-bus-macro
bus-name-macro = node-id %s"_BUS_NAME"
on-bus-macro = compat-id-part %s"_BUS_" bus-name
bus-name = c-ident ; a bus name ("i2c") to a DT C
; identifier ("I2C")
flash-macro = %s"FLASH_AREA_" node-label-ident "_" flash-suf
flash-suf = %s"ID" / %s"READ_ONLY" / (%s"OFFSET" ["_" 1*DIGIT]) /
(%s"SIZE" ["_" 1*DIGIT]) / %s"DEV"
; Macros generated from /chosen node properties.
chosen-macro = chosen-flash /
%s"CODE_PARTITION_OFFSET" / %s"CODE_PARTITION_SIZE" /
%s"CCM_BASE_ADDRESS" / %s"CCM_SIZE" /
%s"DTCM_BASE_ADDRESS" / %s"DTCM_SIZE" /
%s"IPC_SHM_BASE_ADDRESS" / %s"IPC_SHM_SIZE"
; These come from the /chosen/zephyr,flash property.
chosen-flash = %s"FLASH_BASE_ADDRESS" /
%s"FLASH_SIZE" /
%s"FLASH_ERASE_BLOCK_SIZE" /
%s"FLASH_WRITE_BLOCK_SIZE"
; --------------------------------------------------------------------
; Helper definitions.
; A c-ident is one or more:
; - uppercase letters (A-Z)
; - numbers (0-9)
; - underscores ("_")
;
; They are the result of converting names or combinations of names
; from devicetree to a valid component of a C identifier by
; uppercasing letters and converting non-alphanumeric characters to
; underscores.
c-ident = 1*( UPPER / DIGIT / "_" )
; a node's "label" property value, as an identifier
node-label-ident = c-ident
; "uppercase ASCII letter" turns out to be pretty annoying to specify
; in RFC-7405 syntax.
;
; This is just ASCII letters A (0x41) through Z (0x5A).
UPPER = %x41-5A

File diff suppressed because it is too large Load diff

View file

@ -211,7 +211,8 @@ Fixed flash partitions
These conveniences may be used for the special-purpose ``fixed-partitions`` These conveniences may be used for the special-purpose ``fixed-partitions``
compatible used to encode information about flash memory partitions in the compatible used to encode information about flash memory partitions in the
device tree. device tree. See :zephyr_file:`dts/bindings/mtd/partition.yaml` for this
compatible's binding.
.. doxygengroup:: devicetree-fixed-partition .. doxygengroup:: devicetree-fixed-partition
:project: Zephyr :project: Zephyr

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*/
/* start-after-here */
/ {
soc {
flashctrl: flash-controller@deadbeef {
flash0: flash@0 {
compatible = "soc-nv-flash";
reg = <0x0 0x100000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <0x1>;
#size-cells = <0x1>;
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x0 0x10000>;
read-only;
};
storage_partition: partition@1e000 {
label = "storage";
reg = <0x1e000 0x2000>;
};
slot0_partition: partition@20000 {
label = "image-0";
reg = <0x20000 0x60000>;
};
slot1_partition: partition@80000 {
label = "image-1";
reg = <0x80000 0x60000>;
};
scratch_partition: partition@e0000 {
label = "image-scratch";
reg = <0xe0000 0x20000>;
};
};
};
};
};
};

View file

@ -1,36 +1,96 @@
.. _flash_map_api: .. _flash_map_api:
Flash map (flash_map) Flash map
########################## #########
Flash map is a way for storing flash partitioning information in one central The ``<storage/flash_map.h>`` API allows accessing information about device
location in flash_area structures array form. flash partitions via :c:struct:`flash_area` structures.
Flash map is generated from DTS based on content of :ref:`legacy_flash_partitions` Each ``struct flash_area`` describes a flash partition. The API provides access
nodes. to a "flash map", which contains predefined flash areas accessible via globally
The flash_area API provides a way to access data in the flash map. unique ID numbers. You can also create ``flash_area`` structures at runtime for
The flash_area_open() API is the interface for obtaining the flash partitions application-specific purposes.
flash_area from the flash map.
The ``flash_area`` structure contains the name of the flash device the
partition is part of; this name can be passed to :c:func:`device_get_binding`
to get the corresponding :c:struct:`device` structure which can be read and
written to using the :ref:`flash API <flash_api>`. The ``flash_area`` also
contains the start offset and size of the partition within the flash memory the
device represents.
Flash Area API (flash_area) The flash_map.h API provides functions for operating on a ``flash_area``. The
########################### main examples are :c:func:`flash_area_read` and :c:func:`flash_area_write`.
These functions are basically wrappers around the flash API with input
parameter range checks. Not all flash APIs have flash_map.h wrappers, but
:c:func:`flash_area_get_device` allows easily retrieving the ``struct device``
from a ``struct flash_area``.
The flash_area concept combines methods for operating on a flash chunk Use :c:func:`flash_area_open()` to access a ``struct flash_area``. This
together with a description of this chunk. Its methods are basically wrappers function takes a flash area ID number and returns a pointer to the flash area
around the flash API, with input parameter range checks. Not all flash structure. The ID number for a flash area can be obtained from a human-readable
operation are wrapped so an API call to retrieve the flash area driver is "label" using :c:macro:`FLASH_AREA_ID`; these labels are obtained from the
included as well. The flash area methods are designed to be used along with devicetree as described below.
the flash_area structures of flash_map and user-specific flash_areas, with
the exception of the area_open API used to fetch a flash_area from
the flash_map.
Relationship with Devicetree
****************************
The flash_map.h API uses data generated from the :ref:`devicetree_api`, in
particular its :ref:`devicetree-flash-api`. Zephyr additionally has some
partitioning conventions used for :ref:`dfu` via the MCUboot bootloader, as
well as defining partitions usable by :ref:`file systems <file_system_api>` or
other nonvolatile :ref:`storage <storage_reference>`.
Here is an example devicetree fragment which uses fixed flash partitions for
both MCUboot and a storage partition. Some details were left out for clarity.
.. literalinclude:: example_fragment.dts
:language: DTS
:start-after: start-after-here
The ``boot_partition``, ``slot0_partition``, ``slot1_partition``, and
``scratch_partition`` nodes are defined for MCUboot, though not all MCUboot
configurations require all of them to be defined. See the `MCUboot
documentation`_ for more details.
The ``storage_partition`` node is defined for use by a file system or other
nonvolatile storage API.
.. _MCUboot documentation: https://mcuboot.com/
To get a numeric flash area ID from one of the child nodes of the
``partitions`` node:
#. take the node's ``label`` property value
#. lowercase it
#. convert all special characters to underscores (``_``)
#. pass the result **without quotes** to ``FLASH_AREA_ID()``
For example, the ``flash_area`` ID number for ``slot0_partition`` is
``FLASH_AREA_ID(image_0)``.
The same rules apply for other macros which take a "label", such as
:c:macro:`FLASH_AREA_OFFSET` and :c:macro:`FLASH_AREA_SIZE`. For example,
``FLASH_AREA_OFFSET(image_0)`` would return the start offset for
``slot0_partition`` within its flash device. This is determined by the node's
:ref:`devicetree-reg-property`, and in this case is 0x20000.
To get a pointer to the flash area structure and do something with it starting
with a devicetree label like ``"image-0"``, use something like this:
.. code-block:: c
struct flash_area *my_area;
int err = flash_area_open(FLASH_AREA_ID(image_0), &my_area);
if (err != 0) {
handle_the_error(err);
} else {
flash_area_read(my_area, ...);
}
API Reference API Reference
************* *************
flash_area API
==============
.. doxygengroup:: flash_area_api .. doxygengroup:: flash_area_api
:project: Zephyr :project: Zephyr
:members:

View file

@ -133,8 +133,12 @@ Deprecated in this release
* See :ref:`dt-from-c` for a high-level guide to the new API, and * See :ref:`dt-from-c` for a high-level guide to the new API, and
:ref:`devicetree_api` for an API reference. :ref:`devicetree_api` for an API reference.
* Use of the legacy macros now requires explicitly enabling * Use of the legacy macros now requires explicitly enabling
:option:`CONFIG_LEGACY_DEVICETREE_MACROS`. See :ref:`dt-legacy-macros` for ``CONFIG_LEGACY_DEVICETREE_MACROS``. See `the Zephyr v2.3 legacy devicetree
more information, including a link to a migration guide to the new API. macro page`_ for more information, including a link to a migration guide to
the new API.
.. _the Zephyr v2.3 legacy devicetree macro page:
https://docs.zephyrproject.org/2.3.0/guides/dts/legacy-macros.html#dt-legacy-macros
* Other * Other
@ -666,9 +670,12 @@ Build and Infrastructure
* Devicetree * Devicetree
* A new :ref:`devicetree_api` was added. This API is not generated, but is * A new :ref:`devicetree_api` was added. This API is not generated, but is
still included via ``<devicetree.h>``. The :ref:`dt-legacy-macros` are now still included via ``<devicetree.h>``.
deprecated; users should replace the generated macros with new API. The
:ref:`dt-howtos` page has been extended for the new API, and a new See `the Zephyr v2.3 legacy devicetree macro page`_ for more information,
including a link to a migration guide to the new API.
The :ref:`dt-howtos` page has been extended for the new API, and a new
:ref:`dt-from-c` API usage guide was also added. :ref:`dt-from-c` API usage guide was also added.
Libraries / Subsystems Libraries / Subsystems

View file

@ -190,6 +190,11 @@ Build and Infrastructure
* Devicetree * Devicetree
* :c:macro:`DT_ENUM_IDX_OR`: new macro * :c:macro:`DT_ENUM_IDX_OR`: new macro
* Support for legacy devicetree macros via
``CONFIG_LEGACY_DEVICETREE_MACROS`` was removed. All devicetree-based code
should be using the new devicetree API introduced in Zephyr 2.3 and
documented in :ref:`dt-from-c`. Information on flash partitions has moved
to :ref:`flash_map_api`.
Libraries / Subsystems Libraries / Subsystems
********************** **********************

View file

@ -16,20 +16,7 @@
#ifndef DEVICETREE_H #ifndef DEVICETREE_H
#define DEVICETREE_H #define DEVICETREE_H
#ifdef _LINKER
/*
* Linker scripts include this file too, and autoconf.h isn't
* automatically included for those files the way it is for C source
* files. Make sure we pull it in before using
* CONFIG_LEGACY_DEVICETREE_MACROS in that case.
*/
#include <autoconf.h>
#endif
#include <devicetree_unfixed.h> #include <devicetree_unfixed.h>
#ifdef CONFIG_LEGACY_DEVICETREE_MACROS
#include <devicetree_legacy_unfixed.h>
#endif
#include <devicetree_fixups.h> #include <devicetree_fixups.h>
#include <sys/util.h> #include <sys/util.h>

View file

@ -14,29 +14,22 @@
#define ZEPHYR_INCLUDE_STORAGE_FLASH_MAP_H_ #define ZEPHYR_INCLUDE_STORAGE_FLASH_MAP_H_
/** /**
* @brief Abstraction over flash area and its driver which helps to operate on * @brief Abstraction over flash partitions/areas and their drivers
* flash regions easily and effectively.
* *
* @defgroup flash_area_api flash area Interface * @defgroup flash_area_api flash area Interface
* @{ * @{
*/ */
/*
* This API makes it possible to operate on flash areas easily and
* effectively.
*
* The system contains global data about flash areas. Every area
* contains an ID number, offset, and length.
*/
/** /**
* *
* Provides abstraction of flash regions for type of use,
* for example, where's my image?
*
* System will contain a map which contains flash areas. Every
* region will contain flash identifier, offset within flash, and length.
*
* 1. This system map could be in a file within filesystem (Initializer
* must know/figure out where the filesystem is at).
* 2. Map could be at fixed location for project (compiled to code)
* 3. Map could be at specific place in flash (put in place at mfg time).
*
* Note that the map you use must be valid for BSP it's for,
* match the linker scripts when platform executes from flash,
* and match the target offset specified in download script.
*/ */
#include <zephyr/types.h> #include <zephyr/types.h>
#include <stddef.h> #include <stddef.h>
@ -46,22 +39,32 @@
extern "C" { extern "C" {
#endif #endif
#define SOC_FLASH_0_ID 0 /** device_id for SoC flash memory driver */ /** Provided for compatibility with MCUboot */
#define SPI_FLASH_0_ID 1 /** device_id for external SPI flash driver */ #define SOC_FLASH_0_ID 0
/** Provided for compatibility with MCUboot */
#define SPI_FLASH_0_ID 1
/** /**
* @brief Structure for store flash partition data * @brief Flash partition
* *
* It is used as the flash_map array entry or stand-alone user data. Structure * This structure represents a fixed-size partition on a flash device.
* contains all data needed to operate on the flash partitions. * Each partition contains one or more flash sectors.
*/ */
struct flash_area { struct flash_area {
uint8_t fa_id; /** ID of flash area */ /** ID number */
uint8_t fa_id;
/** Provided for compatibility with MCUboot */
uint8_t fa_device_id; uint8_t fa_device_id;
uint16_t pad16; uint16_t pad16;
off_t fa_off; /** flash partition offset */ /** Start offset from the beginning of the flash device */
size_t fa_size; /** flash partition size */ off_t fa_off;
const char *fa_dev_name; /** flash device name */ /** Total size */
size_t fa_size;
/**
* Name of the flash device, suitable for passing to
* device_get_binding().
*/
const char *fa_dev_name;
}; };
/** /**
@ -71,8 +74,10 @@ struct flash_area {
* consumes much less RAM than @ref flash_area * consumes much less RAM than @ref flash_area
*/ */
struct flash_sector { struct flash_sector {
off_t fs_off; /** flash sector offset */ /** Sector offset from the beginning of the flash device */
size_t fs_size; /** flash sector size */ off_t fs_off;
/** Sector size in bytes */
size_t fs_size;
}; };
#if defined(CONFIG_FLASH_AREA_CHECK_INTEGRITY) #if defined(CONFIG_FLASH_AREA_CHECK_INTEGRITY)

View file

@ -18,7 +18,7 @@ Requirements
************ ************
The partition labeled "storage" will be used for the file system; see The partition labeled "storage" will be used for the file system; see
:ref:`legacy_flash_partitions`. If that area does not already have a :ref:`flash_map_api`. If that area does not already have a
compatible littlefs file system its contents will be replaced by an compatible littlefs file system its contents will be replaced by an
empty file system. You will see diagnostics like this:: empty file system. You will see diagnostics like this::

View file

@ -183,8 +183,7 @@ Flashing the sample image
************************* *************************
Upload the :file:`zephyr.signed.bin` file from the previous to image slot-0 of your Upload the :file:`zephyr.signed.bin` file from the previous to image slot-0 of your
board. The location of image slot-0 varies by board, as described in board. See :ref:`flash_map_api` for details on flash partitioning.
:ref:`mcuboot_partitions`.
To upload the initial image file to an empty slot-0, we simply use ``west flash`` To upload the initial image file to an empty slot-0, we simply use ``west flash``
like normal. ``west flash`` will automatically detect slot-0 address and confirm like normal. ``west flash`` will automatically detect slot-0 address and confirm

View file

@ -150,8 +150,8 @@ Step 6: Flash the first image
============================= =============================
Upload the :file:`signed.bin` file from Step 4 to image slot-0 of your Upload the :file:`signed.bin` file from Step 4 to image slot-0 of your
board. The location of image slot-0 varies by board, as described in board. The location of the slot 0 image varies by board; see
:ref:`mcuboot_partitions`. For the frdm_k64f, slot-0 is located at address :ref:`flash_map_api` for details. For the frdm_k64f, slot-0 is located at address
``0xc000``. ``0xc000``.
Using :file:`pyocd` you don't need to specify the slot-0 starting address. Using :file:`pyocd` you don't need to specify the slot-0 starting address.

View file

@ -14,7 +14,7 @@ Requirements
This project requires an USB device driver. Currently, the USB DFU This project requires an USB device driver. Currently, the USB DFU
class provided by the Zephyr project depends on DFU image manager and class provided by the Zephyr project depends on DFU image manager and
partition layout. Refer to :ref:`legacy_flash_partitions` for details about partition layout. Refer to :ref:`flash_map_api` for details about
partition layout. You SoC must run MCUboot as the stage 1 bootloader. partition layout. You SoC must run MCUboot as the stage 1 bootloader.
This sample is built as an application for the MCUboot bootloader. This sample is built as an application for the MCUboot bootloader.

View file

@ -100,27 +100,6 @@ class EDT:
nodes: nodes:
A list of Node objects for the nodes that appear in the devicetree A list of Node objects for the nodes that appear in the devicetree
compat2enabled:
A collections.defaultdict that maps each 'compatible' string that appears
on some enabled Node to a list of enabled Nodes.
For example, edt.compat2enabled["bar"] would include the 'foo' and 'bar'
nodes below.
foo {
compatible = "bar";
status = "okay";
...
};
bar {
compatible = "foo", "bar", "baz";
status = "okay";
...
};
This exists only for the sake of gen_legacy_defines.py. It will probably
be removed following the Zephyr 2.3 release.
compat2nodes: compat2nodes:
A collections.defaultdict that maps each 'compatible' string that appears A collections.defaultdict that maps each 'compatible' string that appears
on some Node to a list of Nodes with that compatible. on some Node to a list of Nodes with that compatible.
@ -524,7 +503,6 @@ class EDT:
self.label2node = OrderedDict() self.label2node = OrderedDict()
self.dep_ord2node = OrderedDict() self.dep_ord2node = OrderedDict()
self.compat2enabled = defaultdict(list)
self.compat2nodes = defaultdict(list) self.compat2nodes = defaultdict(list)
self.compat2okay = defaultdict(list) self.compat2okay = defaultdict(list)
@ -535,9 +513,6 @@ class EDT:
for compat in node.compats: for compat in node.compats:
self.compat2nodes[compat].append(node) self.compat2nodes[compat].append(node)
if node.enabled:
self.compat2enabled[compat].append(node)
if node.status == "okay": if node.status == "okay":
self.compat2okay[compat].append(node) self.compat2okay[compat].append(node)
@ -703,8 +678,8 @@ class Node:
A non-negative integer value such that the value for a Node is A non-negative integer value such that the value for a Node is
less than the value for all Nodes that depend on it. less than the value for all Nodes that depend on it.
The ordinal is defined for all Nodes including those that are not The ordinal is defined for all Nodes, and is unique among nodes in its
'enabled', and is unique among nodes in its EDT 'nodes' list. EDT 'nodes' list.
required_by: required_by:
A list with the nodes that directly depend on the node A list with the nodes that directly depend on the node
@ -717,12 +692,6 @@ class Node:
has no status property set. If the node's status property is "ok", has no status property set. If the node's status property is "ok",
it is converted to "okay" for consistency. it is converted to "okay" for consistency.
enabled:
True unless the node has 'status = "disabled"'
This exists only for the sake of gen_legacy_defines.py. It will probably
be removed following the Zephyr 2.3 release.
read_only: read_only:
True if the node has a 'read-only' property, and False otherwise True if the node has a 'read-only' property, and False otherwise
@ -866,11 +835,6 @@ class Node:
return as_string return as_string
@property
def enabled(self):
"See the class docstring"
return "status" not in self._node.props or self.status != "disabled"
@property @property
def read_only(self): def read_only(self):
"See the class docstring" "See the class docstring"
@ -1182,7 +1146,7 @@ class Node:
prop = node.props.get(name) prop = node.props.get(name)
if not prop: if not prop:
if required and self.enabled: if required and self.status == "okay":
_err("'{}' is marked as required in 'properties:' in {}, but " _err("'{}' is marked as required in 'properties:' in {}, but "
"does not appear in {!r}".format( "does not appear in {!r}".format(
name, self.binding_path, node)) name, self.binding_path, node))

View file

@ -1,827 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2019 Nordic Semiconductor ASA
# Copyright (c) 2019 Linaro Limited
# SPDX-License-Identifier: BSD-3-Clause
# This script is similar to gen_defines.py, but is for the legacy
# macro syntax used in e.g. Zephyr 2.2.
#
# It should be considered frozen code. New macro-related functionality
# should be done by modifying the macro namespaces managed by
# gen_defines.py.
import argparse
import os
import pathlib
import pickle
# Set this to True to generated deprecated macro warnings. Since this
# entire file is deprecated and must be explicitly enabled with
# CONFIG_LEGACY_DEVICETREE_MACROS, this was turned off by default
# shortly before the v2.3 release (this was the least impactful way to
# do it, which resulted in the smallest and least-risky patch).
DEPRECATION_MESSAGES = False
def main():
global header_file
global flash_area_num
args = parse_args()
with open(args.edt_pickle, 'rb') as f:
edt = pickle.load(f)
header_file = open(args.header_out, "w", encoding="utf-8")
flash_area_num = 0
write_top_comment(edt)
for node in sorted(edt.nodes, key=lambda node: node.dep_ordinal):
write_node_comment(node)
# Flash partition nodes are handled as a special case. It
# would be nicer if we had bindings that would let us
# avoid that, but this will do for now.
if node.name.startswith("partition@"):
write_flash_partition(node, flash_area_num)
flash_area_num += 1
if node.enabled and node.matching_compat:
write_regs(node)
write_irqs(node)
write_props(node)
write_clocks(node)
write_spi_dev(node)
write_bus(node)
write_existence_flags(node)
out_comment("Compatibles appearing on enabled nodes")
for compat in sorted(edt.compat2enabled):
#define DT_COMPAT_<COMPAT> 1
out(f"COMPAT_{str2ident(compat)}", 1)
# Definitions derived from /chosen nodes
write_addr_size(edt, "zephyr,ccm", "CCM")
write_addr_size(edt, "zephyr,dtcm", "DTCM")
write_addr_size(edt, "zephyr,ipc_shm", "IPC_SHM")
write_flash(edt)
header_file.close()
def parse_args():
# Returns parsed command-line arguments
parser = argparse.ArgumentParser()
parser.add_argument("--edt-pickle", required=True,
help="pickle file containing EDT object")
parser.add_argument("--header-out", required=True,
help="path to write header to")
return parser.parse_args()
def write_top_comment(edt):
# Writes an overview comment with misc. info at the top of the header and
# configuration file
s = f"""\
Generated by gen_legacy_defines.py
DTS input file:
{edt.dts_path}
Directories with bindings:
{", ".join(map(relativize, edt.bindings_dirs))}
Nodes in dependency order (ordinal and path):
"""
for scc in edt.scc_order:
if len(scc) > 1:
err("cycle in devicetree involving "
+ ", ".join(node.path for node in scc))
s += f" {scc[0].dep_ordinal:<3} {scc[0].path}\n"
s += """
Definitions derived from these nodes in dependency order are next,
followed by tree-wide information (active compatibles, chosen nodes,
etc.).
"""
out_comment(s, blank_before=False)
def write_node_comment(node):
# Writes a comment describing 'node' to the header and configuration file
s = f"""\
Devicetree node:
{node.path}
"""
if node.matching_compat:
if node.binding_path:
s += f"""
Binding (compatible = {node.matching_compat}):
{relativize(node.binding_path)}
"""
else:
s += f"""
Binding (compatible = {node.matching_compat}):
No yaml (bindings inferred from properties)
"""
else:
s += "\nNo matching binding.\n"
s += f"\nDependency Ordinal: {node.dep_ordinal}\n"
if node.depends_on:
s += "\nRequires:\n"
for dep in node.depends_on:
s += f" {dep.dep_ordinal:<3} {dep.path}\n"
if node.required_by:
s += "\nSupports:\n"
for req in node.required_by:
s += f" {req.dep_ordinal:<3} {req.path}\n"
if node.description:
# Indent description by two spaces
s += "\nDescription:\n" + \
"\n".join(" " + line for line in
node.description.splitlines()) + \
"\n"
if not node.enabled:
s += "\nNode is disabled.\n"
out_comment(s)
def relativize(path):
# If 'path' is within $ZEPHYR_BASE, returns it relative to $ZEPHYR_BASE,
# with a "$ZEPHYR_BASE/..." hint at the start of the string. Otherwise,
# returns 'path' unchanged.
zbase = os.getenv("ZEPHYR_BASE")
if zbase is None:
return path
try:
return str("$ZEPHYR_BASE" / pathlib.Path(path).relative_to(zbase))
except ValueError:
# Not within ZEPHYR_BASE
return path
def write_regs(node):
# Writes address/size output for the registers in the node's 'reg' property
def write_reg(reg, base_ident, val):
# Drop '_0' from the identifier if there's a single register, for
# backwards compatibility
if len(reg.node.regs) > 1:
ident = f"{base_ident}_{reg.node.regs.index(reg)}"
else:
ident = base_ident
out_node(node, ident, val,
# Name alias from 'reg-names = ...'
f"{str2ident(reg.name)}_{base_ident}" if reg.name else None)
for reg in node.regs:
write_reg(reg, "BASE_ADDRESS", hex(reg.addr))
if reg.size:
write_reg(reg, "SIZE", reg.size)
def write_props(node):
# Writes any properties defined in the "properties" section of the binding
# for the node
for prop in node.props.values():
if not should_write(prop):
continue
if prop.description is not None:
out_comment(prop.description, blank_before=False)
ident = str2ident(prop.name)
if prop.type == "boolean":
out_node(node, ident, 1 if prop.val else 0)
elif prop.type == "string":
out_node_s(node, ident, prop.val)
elif prop.type == "int":
out_node(node, ident, prop.val)
elif prop.type == "array":
for i, val in enumerate(prop.val):
out_node(node, f"{ident}_{i}", val)
out_node_init(node, ident, prop.val)
elif prop.type == "string-array":
for i, val in enumerate(prop.val):
out_node_s(node, f"{ident}_{i}", val)
elif prop.type == "uint8-array":
out_node_init(node, ident,
[f"0x{b:02x}" for b in prop.val])
else: # prop.type == "phandle-array"
write_phandle_val_list(prop)
# Generate DT_..._ENUM if there's an 'enum:' key in the binding
if prop.enum_index is not None:
out_node(node, ident + "_ENUM", prop.enum_index)
def should_write(prop):
# write_props() helper. Returns True if output should be generated for
# 'prop'.
# Skip #size-cell and other property starting with #. Also skip mapping
# properties like 'gpio-map'.
if prop.name[0] == "#" or prop.name.endswith("-map"):
return False
# See write_clocks()
if prop.name == "clocks":
return False
# For these, Property.val becomes an edtlib.Node, a list of edtlib.Nodes,
# or None. Nothing is generated for them at the moment.
if prop.type in {"phandle", "phandles", "path", "compound"}:
return False
# Skip properties that we handle elsewhere
if prop.name in {
"reg", "compatible", "status", "interrupts",
"interrupt-controller", "gpio-controller"
}:
return False
return True
def write_bus(node):
# Generate bus-related #defines
if not node.bus_node:
return
if node.bus_node.label is None:
err(f"missing 'label' property on bus node {node.bus_node!r}")
# #define DT_<DEV-IDENT>_BUS_NAME <BUS-LABEL>
out_node_s(node, "BUS_NAME", str2ident(node.bus_node.label), "Macro is deprecated")
for compat in node.compats:
# #define DT_<COMPAT>_BUS_<BUS-TYPE> 1
out(f"{str2ident(compat)}_BUS_{str2ident(node.on_bus)}", 1)
def write_existence_flags(node):
# Generate #defines of the form
#
# #define DT_INST_<instance no.>_<compatible string> 1
#
# for enabled nodes. These are flags for which devices exist.
for compat in node.compats:
instance_no = node.edt.compat2enabled[compat].index(node)
out(f"INST_{instance_no}_{str2ident(compat)}", 1)
def node_ident(node):
# Returns an identifier for 'node'. Used e.g. when building macro names.
# TODO: Handle PWM on STM
# TODO: Better document the rules of how we generate things
ident = ""
if node.bus_node:
if node.bus_node.unit_addr is not None:
ident += "{}_{:X}_".format(
str2ident(node.bus_node.matching_compat), node.bus_node.unit_addr)
else:
ident += str2ident(node.bus_node.matching_compat)
ident += f"{str2ident(node.matching_compat)}_"
if node.unit_addr is not None:
ident += f"{node.unit_addr:X}"
elif node.parent.unit_addr is not None:
ident += f"{node.parent.unit_addr:X}_{str2ident(node.name)}"
else:
# This is a bit of a hack
ident += str2ident(node.name)
return ident
def node_aliases(node):
# Returns a list of aliases for 'node', used e.g. when building macro names
return node_path_aliases(node) + node_instance_aliases(node)
def node_path_aliases(node):
# Returns a list of aliases for 'node', based on the aliases registered for
# it in the /aliases node. Used e.g. when building macro names.
if node.matching_compat is None:
return []
compat_s = str2ident(node.matching_compat)
aliases = []
for alias in node.aliases:
aliases.append(f"ALIAS_{str2ident(alias)}")
# TODO: See if we can remove or deprecate this form
aliases.append(f"{compat_s}_{str2ident(alias)}")
return aliases
def node_instance_aliases(node):
# Returns a list of aliases for 'node', based on the compatible string and
# the instance number (each node with a particular compatible gets its own
# instance number, starting from zero).
#
# This is a list since a node can have multiple 'compatible' strings, each
# with their own instance number.
res = []
for compat in node.compats:
instance_no = node.edt.compat2enabled[compat].index(node)
res.append(f"INST_{instance_no}_{str2ident(compat)}")
return res
def write_addr_size(edt, prop_name, prefix):
# Writes <prefix>_BASE_ADDRESS and <prefix>_SIZE for the node pointed at by
# the /chosen property named 'prop_name', if it exists
node = edt.chosen_node(prop_name)
if not node:
return
if not node.regs:
err("missing 'reg' property in node pointed at by "
f"/chosen/{prop_name} ({node!r})")
out_comment(f"/chosen/{prop_name} ({node.path})")
out(f"{prefix}_BASE_ADDRESS", hex(node.regs[0].addr))
out(f"{prefix}_SIZE", node.regs[0].size//1024)
def write_flash(edt):
# Writes chosen and tree-wide flash-related output
write_flash_node(edt)
write_code_partition(edt)
if flash_area_num != 0:
out_comment("Number of flash partitions")
out("FLASH_AREA_NUM", flash_area_num)
def write_flash_node(edt):
# Writes output for the top-level flash node pointed at by
# zephyr,flash in /chosen
node = edt.chosen_node("zephyr,flash")
out_comment(f"/chosen/zephyr,flash ({node.path if node else 'missing'})")
if not node:
# No flash node. Write dummy values.
out("FLASH_BASE_ADDRESS", 0)
out("FLASH_SIZE", 0)
return
if len(node.regs) != 1:
err("expected zephyr,flash to have a single register, has "
f"{len(node.regs)}")
if node.on_bus == "spi" and len(node.bus_node.regs) == 2:
reg = node.bus_node.regs[1] # QSPI flash
else:
reg = node.regs[0]
out("FLASH_BASE_ADDRESS", hex(reg.addr))
if reg.size:
out("FLASH_SIZE", reg.size//1024)
if "erase-block-size" in node.props:
out("FLASH_ERASE_BLOCK_SIZE", node.props["erase-block-size"].val)
if "write-block-size" in node.props:
out("FLASH_WRITE_BLOCK_SIZE", node.props["write-block-size"].val)
def write_code_partition(edt):
# Writes output for the node pointed at by zephyr,code-partition in /chosen
node = edt.chosen_node("zephyr,code-partition")
out_comment("/chosen/zephyr,code-partition "
f"({node.path if node else 'missing'})")
if not node:
# No code partition. Write dummy values.
out("CODE_PARTITION_OFFSET", 0)
out("CODE_PARTITION_SIZE", 0)
return
if not node.regs:
err(f"missing 'regs' property on {node!r}")
out("CODE_PARTITION_OFFSET", node.regs[0].addr)
out("CODE_PARTITION_SIZE", node.regs[0].size)
def write_flash_partition(partition_node, index):
if partition_node.label is None:
err(f"missing 'label' property on {partition_node!r}")
# Generate label-based identifiers
write_flash_partition_prefix(
"FLASH_AREA_" + str2ident(partition_node.label), partition_node, index)
# Generate index-based identifiers
write_flash_partition_prefix(f"FLASH_AREA_{index}", partition_node, index)
def write_flash_partition_prefix(prefix, partition_node, index):
# write_flash_partition() helper. Generates identifiers starting with
# 'prefix'.
out(f"{prefix}_ID", index)
out(f"{prefix}_READ_ONLY", 1 if partition_node.read_only else 0)
for i, reg in enumerate(partition_node.regs):
# Also add aliases that point to the first sector (TODO: get rid of the
# aliases?)
out(f"{prefix}_OFFSET_{i}", reg.addr,
aliases=[f"{prefix}_OFFSET"] if i == 0 else [])
out(f"{prefix}_SIZE_{i}", reg.size,
aliases=[f"{prefix}_SIZE"] if i == 0 else [])
controller = partition_node.flash_controller
if controller.label is not None:
out_s(f"{prefix}_DEV", controller.label)
def write_irqs(node):
# Writes IRQ num and data for the interrupts in the node's 'interrupt'
# property
def irq_name_alias(irq, cell_name):
if not irq.name:
return None
alias = f"IRQ_{str2ident(irq.name)}"
if cell_name != "irq":
alias += f"_{str2ident(cell_name)}"
return alias
def map_arm_gic_irq_type(irq, irq_num):
# Maps ARM GIC IRQ (type)+(index) combo to linear IRQ number
if "type" not in irq.data:
err(f"Expected binding for {irq.controller!r} to have 'type' in "
"interrupt-cells")
irq_type = irq.data["type"]
if irq_type == 0: # GIC_SPI
return irq_num + 32
if irq_type == 1: # GIC_PPI
return irq_num + 16
err(f"Invalid interrupt type specified for {irq!r}")
def encode_zephyr_multi_level_irq(irq, irq_num):
# See doc/reference/kernel/other/interrupts.rst for details
# on how this encoding works
irq_ctrl = irq.controller
# Look for interrupt controller parent until we have none
while irq_ctrl.interrupts:
irq_num = (irq_num + 1) << 8
if "irq" not in irq_ctrl.interrupts[0].data:
err(f"Expected binding for {irq_ctrl!r} to have 'irq' in "
"interrupt-cells")
irq_num |= irq_ctrl.interrupts[0].data["irq"]
irq_ctrl = irq_ctrl.interrupts[0].controller
return irq_num
for irq_i, irq in enumerate(node.interrupts):
for cell_name, cell_value in irq.data.items():
ident = f"IRQ_{irq_i}"
if cell_name == "irq":
if "arm,gic" in irq.controller.compats:
cell_value = map_arm_gic_irq_type(irq, cell_value)
cell_value = encode_zephyr_multi_level_irq(irq, cell_value)
else:
ident += f"_{str2ident(cell_name)}"
out_node(node, ident, cell_value,
name_alias=irq_name_alias(irq, cell_name))
def write_spi_dev(node):
# Writes SPI device GPIO chip select data if there is any
cs_gpio = node.spi_cs_gpio
if cs_gpio is not None:
write_phandle_val_list_entry(node, cs_gpio, None, "CS_GPIOS")
def write_phandle_val_list(prop):
# Writes output for a phandle/value list, e.g.
#
# pwms = <&pwm-ctrl-1 10 20
# &pwm-ctrl-2 30 40>;
#
# prop:
# phandle/value Property instance.
#
# If only one entry appears in 'prop' (the example above has two), the
# generated identifier won't get a '_0' suffix, and the '_COUNT' and
# group initializer are skipped too.
#
# The base identifier is derived from the property name. For example, 'pwms = ...'
# generates output like this:
#
# #define <node prefix>_PWMS_CONTROLLER_0 "PWM_0" (name taken from 'label = ...')
# #define <node prefix>_PWMS_CHANNEL_0 123 (name taken from *-cells in binding)
# #define <node prefix>_PWMS_0 {"PWM_0", 123}
# #define <node prefix>_PWMS_CONTROLLER_1 "PWM_1"
# #define <node prefix>_PWMS_CHANNEL_1 456
# #define <node prefix>_PWMS_1 {"PWM_1", 456}
# #define <node prefix>_PWMS_COUNT 2
# #define <node prefix>_PWMS {<node prefix>_PWMS_0, <node prefix>_PWMS_1}
# ...
# pwms -> PWMS
# foo-gpios -> FOO_GPIOS
ident = str2ident(prop.name)
initializer_vals = []
for i, entry in enumerate(prop.val):
initializer_vals.append(write_phandle_val_list_entry(
prop.node, entry, i if len(prop.val) > 1 else None, ident))
if len(prop.val) > 1:
out_node(prop.node, ident + "_COUNT", len(initializer_vals))
out_node_init(prop.node, ident, initializer_vals)
def write_phandle_val_list_entry(node, entry, i, ident):
# write_phandle_val_list() helper. We could get rid of it if it wasn't for
# write_spi_dev(). Adds 'i' as an index to identifiers unless it's None.
#
# 'entry' is an edtlib.ControllerAndData instance.
#
# Returns the identifier for the macro that provides the
# initializer for the entire entry.
initializer_vals = []
if entry.controller.label is not None:
ctrl_ident = ident + "_CONTROLLER" # e.g. PWMS_CONTROLLER
if entry.name:
name_alias = f"{str2ident(entry.name)}_{ctrl_ident}"
else:
name_alias = None
# Ugly backwards compatibility hack. Only add the index if there's
# more than one entry.
if i is not None:
ctrl_ident += f"_{i}"
initializer_vals.append(quote_str(entry.controller.label))
out_node_s(node, ctrl_ident, entry.controller.label, name_alias)
for cell, val in entry.data.items():
cell_ident = f"{ident}_{str2ident(cell)}" # e.g. PWMS_CHANNEL
if entry.name:
# From e.g. 'pwm-names = ...'
name_alias = f"{str2ident(entry.name)}_{cell_ident}"
else:
name_alias = None
# Backwards compatibility (see above)
if i is not None:
cell_ident += f"_{i}"
out_node(node, cell_ident, val, name_alias)
initializer_vals += entry.data.values()
initializer_ident = ident
if entry.name:
name_alias = f"{initializer_ident}_{str2ident(entry.name)}"
else:
name_alias = None
if i is not None:
initializer_ident += f"_{i}"
return out_node_init(node, initializer_ident, initializer_vals, name_alias)
def write_clocks(node):
# Writes clock information.
#
# Most of this ought to be handled in write_props(), but the identifiers
# that get generated for 'clocks' are inconsistent with the with other
# 'phandle-array' properties.
#
# See https://github.com/zephyrproject-rtos/zephyr/pull/19327#issuecomment-534081845.
if "clocks" not in node.props:
return
for clock_i, clock in enumerate(node.props["clocks"].val):
controller = clock.controller
if controller.label is not None:
out_node_s(node, "CLOCK_CONTROLLER", controller.label)
for name, val in clock.data.items():
if clock_i == 0:
clk_name_alias = "CLOCK_" + str2ident(name)
else:
clk_name_alias = None
out_node(node, f"CLOCK_{str2ident(name)}_{clock_i}", val,
name_alias=clk_name_alias)
if "fixed-clock" not in controller.compats:
continue
if "clock-frequency" not in controller.props:
err(f"{controller!r} is a 'fixed-clock' but lacks a "
"'clock-frequency' property")
out_node(node, "CLOCKS_CLOCK_FREQUENCY",
controller.props["clock-frequency"].val)
def str2ident(s):
# Converts 's' to a form suitable for (part of) an identifier
return s.replace("-", "_") \
.replace(",", "_") \
.replace("@", "_") \
.replace("/", "_") \
.replace(".", "_") \
.replace("+", "PLUS") \
.upper()
def out_node(node, ident, val, name_alias=None, deprecation_msg=None):
# Writes a
#
# <node prefix>_<ident> = <val>
#
# assignment, along with a set of
#
# <node alias>_<ident>
#
# aliases, for each path/instance alias for the node. If 'name_alias' (a
# string) is passed, then these additional aliases are generated:
#
# <node prefix>_<name alias>
# <node alias>_<name alias> (for each node alias)
#
# 'name_alias' is used for reg-names and the like.
#
# If a 'deprecation_msg' string is passed, the generated identifiers will
# generate a warning if used, via __WARN(<deprecation_msg>)).
#
# Returns the identifier used for the macro that provides the value
# for 'ident' within 'node', e.g. DT_MFG_MODEL_CTL_GPIOS_PIN.
node_prefix = node_ident(node)
aliases = [f"{alias}_{ident}" for alias in node_aliases(node)]
if name_alias is not None:
aliases.append(f"{node_prefix}_{name_alias}")
aliases += [f"{alias}_{name_alias}" for alias in node_aliases(node)]
return out(f"{node_prefix}_{ident}", val, aliases, deprecation_msg)
def out_node_s(node, ident, s, name_alias=None, deprecation_msg=None):
# Like out_node(), but emits 's' as a string literal
#
# Returns the generated macro name for 'ident'.
return out_node(node, ident, quote_str(s), name_alias, deprecation_msg)
def out_node_init(node, ident, elms, name_alias=None, deprecation_msg=None):
# Like out_node(), but generates an {e1, e2, ...} initializer with the
# elements in the iterable 'elms'.
#
# Returns the generated macro name for 'ident'.
return out_node(node, ident, "{" + ", ".join(map(str, elms)) + "}",
name_alias, deprecation_msg)
def out_s(ident, val):
# Like out(), but puts quotes around 'val' and escapes any double
# quotes and backslashes within it
#
# Returns the generated macro name for 'ident'.
return out(ident, quote_str(val))
def out(ident, val, aliases=(), deprecation_msg=None):
# Writes '#define <ident> <val>' to the header and '<ident>=<val>' to the
# the configuration file.
#
# Also writes any aliases listed in 'aliases' (an iterable). For the
# header, these look like '#define <alias> <ident>'. For the configuration
# file, the value is just repeated as '<alias>=<val>' for each alias.
#
# See out_node() for the meaning of 'deprecation_msg'.
#
# Returns the generated macro name for 'ident'.
out_define(ident, val, deprecation_msg, header_file)
primary_ident = f"DT_{ident}"
d_msg = deprecation_msg
for alias in aliases:
if alias != ident:
if alias.startswith("INST_"):
deprecation_msg = "Macro is deprecated"
out_define(alias, "DT_" + ident, deprecation_msg, header_file)
deprecation_msg = d_msg
return primary_ident
def out_define(ident, val, deprecation_msg, out_file):
# out() helper for writing a #define. See out_node() for the meaning of
# 'deprecation_msg'.
s = f"#define DT_{ident:40}"
if DEPRECATION_MESSAGES and deprecation_msg:
s += fr' __WARN("{deprecation_msg}")'
s += f" {val}"
print(s, file=out_file)
def out_comment(s, blank_before=True):
# Writes 's' as a comment to the header and configuration file. 's' is
# allowed to have multiple lines. blank_before=True adds a blank line
# before the comment.
if blank_before:
print(file=header_file)
if "\n" in s:
# Format multi-line comments like
#
# /*
# * first line
# * second line
# *
# * empty line before this line
# */
res = ["/*"]
for line in s.splitlines():
# Avoid an extra space after '*' for empty lines. They turn red in
# Vim if space error checking is on, which is annoying.
res.append(" *" if not line.strip() else " * " + line)
res.append(" */")
print("\n".join(res), file=header_file)
else:
# Format single-line comments like
#
# /* foo bar */
print("/* " + s + " */", file=header_file)
def escape(s):
# Backslash-escapes any double quotes and backslashes in 's'
# \ must be escaped before " to avoid double escaping
return s.replace("\\", "\\\\").replace('"', '\\"')
def quote_str(s):
# Puts quotes around 's' and escapes any double quotes and
# backslashes within it
return f'"{escape(s)}"'
def err(s):
raise Exception(s)
if __name__ == "__main__":
main()

View file

@ -311,31 +311,6 @@
default-not-used = <234>; default-not-used = <234>;
}; };
//
// For testing EDT.compat2enabled
//
compat2enabled {
foo-1 {
status = "okay";
compatible = "compat2enabled";
};
foo-disabled {
status = "disabled";
compatible = "compat2enabled";
};
foo-2 {
// No 'status', which is also treated as enabled
compatible = "compat2enabled";
};
// Should not create an entry in compat2enabled, since all nodes
// with the compatible are disabled
bar {
status = "disabled";
compatible = "compat2enabled-disabled";
};
};
// //
// For testing 'bus:' and 'on-bus:' // For testing 'bus:' and 'on-bus:'
// //

View file

@ -155,15 +155,6 @@ def test_child_binding():
assert str(grandchild.description) == "grandchild node" assert str(grandchild.description) == "grandchild node"
assert str(grandchild.props) == "OrderedDict([('grandchild-prop', <Property, name: grandchild-prop, type: int, value: 2>)])" assert str(grandchild.props) == "OrderedDict([('grandchild-prop', <Property, name: grandchild-prop, type: int, value: 2>)])"
def test_compat2enabled():
'''Test EDT.compat2enabled'''
edt = edtlib.EDT("test.dts", ["test-bindings"])
assert str(edt.compat2enabled["compat2enabled"]) == \
"[<Node /compat2enabled/foo-1 in 'test.dts', no binding>, <Node /compat2enabled/foo-2 in 'test.dts', no binding>]"
assert "compat2enabled-disabled" not in edt.compat2enabled
def test_props(): def test_props():
'''Test Node.props (derived from DT and 'properties:' in the binding)''' '''Test Node.props (derived from DT and 'properties:' in the binding)'''
edt = edtlib.EDT("test.dts", ["test-bindings"]) edt = edtlib.EDT("test.dts", ["test-bindings"])

View file

@ -72,7 +72,7 @@ def dt_chosen_enabled(kconf, _, chosen):
return "n" return "n"
node = edt.chosen_node(chosen) node = edt.chosen_node(chosen)
return "y" if node and node.enabled else "n" return "y" if node and node.status == "okay" else "n"
def dt_chosen_path(kconf, _, chosen): def dt_chosen_path(kconf, _, chosen):
@ -117,7 +117,7 @@ def dt_node_enabled(kconf, name, node):
except edtlib.EDTError: except edtlib.EDTError:
return "n" return "n"
return "y" if node and node.enabled else "n" return "y" if node and node.status == "okay" else "n"
def dt_nodelabel_enabled(kconf, _, label): def dt_nodelabel_enabled(kconf, _, label):
@ -132,7 +132,7 @@ def dt_nodelabel_enabled(kconf, _, label):
node = edt.label2node.get(label) node = edt.label2node.get(label)
return "y" if node and node.enabled else "n" return "y" if node and node.status == "okay" else "n"
def _node_reg_addr(node, index, unit): def _node_reg_addr(node, index, unit):
@ -350,13 +350,13 @@ def dt_node_int_prop(kconf, name, path, prop):
def dt_compat_enabled(kconf, _, compat): def dt_compat_enabled(kconf, _, compat):
""" """
This function takes a 'compat' and returns "y" if we find an "enabled" This function takes a 'compat' and returns "y" if we find a status "okay"
compatible node in the EDT otherwise we return "n" compatible node in the EDT otherwise we return "n"
""" """
if doc_mode or edt is None: if doc_mode or edt is None:
return "n" return "n"
return "y" if compat in edt.compat2enabled else "n" return "y" if compat in edt.compat2okay else "n"
def dt_compat_on_bus(kconf, _, compat, bus): def dt_compat_on_bus(kconf, _, compat, bus):
@ -367,7 +367,7 @@ def dt_compat_on_bus(kconf, _, compat, bus):
if doc_mode or edt is None: if doc_mode or edt is None:
return "n" return "n"
for node in edt.compat2enabled[compat]: for node in edt.compat2okay[compat]:
if node.on_bus is not None and node.on_bus == bus: if node.on_bus is not None and node.on_bus == bus:
return "y" return "y"
@ -383,7 +383,7 @@ def dt_nodelabel_has_compat(kconf, _, label, compat):
if doc_mode or edt is None: if doc_mode or edt is None:
return "n" return "n"
for node in edt.compat2enabled[compat]: for node in edt.compat2okay[compat]:
if label in node.labels: if label in node.labels:
return "y" return "y"

View file

@ -224,20 +224,20 @@ def ast_expr(ast, env, edt):
elif ast[0] == "dt_compat_enabled": elif ast[0] == "dt_compat_enabled":
compat = ast[1][0] compat = ast[1][0]
for node in edt.nodes: for node in edt.nodes:
if compat in node.compats and node.enabled: if compat in node.compats and node.status == "okay":
return True return True
return False return False
elif ast[0] == "dt_alias_exists": elif ast[0] == "dt_alias_exists":
alias = ast[1][0] alias = ast[1][0]
for node in edt.nodes: for node in edt.nodes:
if alias in node.aliases and node.enabled: if alias in node.aliases and node.status == "okay":
return True return True
return False return False
elif ast[0] == "dt_compat_enabled_with_alias": elif ast[0] == "dt_compat_enabled_with_alias":
compat = ast[1][0] compat = ast[1][0]
alias = ast[1][1] alias = ast[1][1]
for node in edt.nodes: for node in edt.nodes:
if node.enabled and alias in node.aliases and node.matching_compat == compat: if node.status == "okay" and alias in node.aliases and node.matching_compat == compat:
return True return True
return False return False

View file

@ -1,8 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(test_dts)
target_sources(app PRIVATE src/main.c)

View file

@ -1,9 +0,0 @@
/*
* Copyright (c) 2020, Linaro Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
&storage_partition {
status = "disabled";
};

View file

@ -1 +0,0 @@
CONFIG_LEGACY_DEVICETREE_MACROS=y

View file

@ -1,25 +0,0 @@
/*
* Copyright (c) 2020 Linaro
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <sys/printk.h>
#include <storage/flash_map.h>
void main(void)
{
BUILD_ASSERT(DT_FLASH_AREA_MCUBOOT_ID == FLASH_AREA_ID(mcuboot),
"FLASH AREA ID mismatch for MCUBOOT partition");
/* disabled status is ignored for partitions */
BUILD_ASSERT(DT_FLASH_AREA_STORAGE_ID == FLASH_AREA_ID(storage),
"FLASH AREA ID mismatch for STORAGE partition");
BUILD_ASSERT(DT_FLASH_AREA_IMAGE_0_ID == FLASH_AREA_ID(image_0),
"FLASH AREA ID mismatch for IMAGE_0 partition");
BUILD_ASSERT(DT_FLASH_AREA_IMAGE_1_ID == FLASH_AREA_ID(image_1),
"FLASH AREA ID mismatch for IMAGE_1 partition");
BUILD_ASSERT(DT_FLASH_AREA_IMAGE_SCRATCH_ID ==
FLASH_AREA_ID(image_scratch),
"FLASH AREA ID mismatch for IMAGE_SCRATCH partition");
}

View file

@ -1,4 +0,0 @@
tests:
dts.build.build:
build_only: true
platform_allow: frdm_k64f

View file

@ -1,9 +0,0 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.13.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(devicetree)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -1,5 +0,0 @@
Test cases that ensure the legacy macros match the results for the
devicetree.h API. This directory is safe to remove once the legacy
devicetree macros documented here are no longer supported:
https://docs.zephyrproject.org/latest/guides/dts/legacy-macros.html

View file

@ -1,95 +0,0 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*
* Application overlay for testing the legacy devicetree macros can
* be migrated to the devicetree.h API.
*
* Names in this file should be chosen in a way that won't conflict
* with real-world devicetree nodes, to allow these tests to run on
* (and be extended to test) real hardware.
*/
#include <dt-bindings/gpio/gpio.h>
/*
* The "start-after..." and "end-before..." comments are because
* this file is included in the migration HOWTO.
*/
/* start-after-here */
/ {
aliases {
mgr-gpio = &migration_gpio;
mgr-serial = &migration_serial;
mgr-i2c-dev = &mgr_i2c_device;
mgr-spi-dev = &mgr_spi_device;
};
migration {
#address-cells = <0x1>;
#size-cells = <0x1>;
interrupt-parent = <&migration_intc>;
migration_gpio: gpio@1000 {
compatible = "vnd,gpio";
gpio-controller;
reg = <0x1000 0x2000>;
interrupts = <0 1>;
#gpio-cells = <0x2>;
label = "MGR_GPIO";
};
migration_serial: serial@3000 {
compatible = "vnd,serial";
reg = <0x3000 0x1000>;
interrupts = <2 1>;
label = "MGR_SERIAL";
baud-rate = <115200>;
};
i2c@10000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "vnd,i2c";
reg = < 0x10000 0x1000 >;
label = "MGR_I2C";
clock-frequency = <100000>;
interrupts = <10 1>;
mgr_i2c_device: i2c-dev@10 {
compatible = "vnd,i2c-device";
label = "MGR_I2C_DEV";
reg = <0x10>;
};
};
spi@20000 {
#address-cells = < 1 >;
#size-cells = < 0 >;
compatible = "vnd,spi";
reg = <0x20000 0x1000>;
interrupts = <20 1>;
label = "MGR_SPI";
clock-frequency = <2000000>;
cs-gpios = <&migration_gpio 17 GPIO_ACTIVE_LOW>;
mgr_spi_device: spi-dev@0 {
compatible = "vnd,spi-device";
label = "MGR_SPI_DEV";
reg = <0>;
spi-max-frequency = <2000000>;
};
};
migration_intc: interrupt-controller@30000 {
compatible = "vnd,intc";
reg = <0x30000 0x1000>;
label = "MGR_INTC";
interrupt-controller;
#interrupt-cells = <2>;
};
};
};
/* end-before-here */

View file

@ -1,2 +0,0 @@
CONFIG_ZTEST=y
CONFIG_LEGACY_DEVICETREE_MACROS=y

View file

@ -1,207 +0,0 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <devicetree.h>
/*
* We test most common properties (label, reg, interrupts) on just the
* GPIO node, since they work the same way on all nodes.
*/
static void test_gpio(void)
{
/* label */
zassert_equal(DT_VND_GPIO_1000_LABEL,
DT_LABEL(DT_PATH(migration, gpio_1000)),
"");
zassert_equal(DT_INST_0_VND_GPIO_LABEL,
DT_LABEL(DT_INST(0, vnd_gpio)),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_LABEL,
DT_LABEL(DT_ALIAS(mgr_gpio)),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_LABEL,
DT_LABEL(DT_NODELABEL(migration_gpio)),
"");
/* reg base address */
zassert_equal(DT_VND_GPIO_1000_BASE_ADDRESS,
DT_REG_ADDR(DT_PATH(migration, gpio_1000)),
"");
zassert_equal(DT_INST_0_VND_GPIO_BASE_ADDRESS,
DT_REG_ADDR(DT_INST(0, vnd_gpio)),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_BASE_ADDRESS,
DT_REG_ADDR(DT_ALIAS(mgr_gpio)),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_BASE_ADDRESS,
DT_REG_ADDR(DT_NODELABEL(migration_gpio)),
"");
/* reg size */
zassert_equal(DT_VND_GPIO_1000_SIZE,
DT_REG_SIZE(DT_PATH(migration, gpio_1000)),
"");
zassert_equal(DT_INST_0_VND_GPIO_SIZE,
DT_REG_SIZE(DT_INST(0, vnd_gpio)),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_SIZE,
DT_REG_SIZE(DT_ALIAS(mgr_gpio)),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_SIZE,
DT_REG_SIZE(DT_NODELABEL(migration_gpio)),
"");
/* irq number */
zassert_equal(DT_VND_GPIO_1000_IRQ_0,
DT_IRQN(DT_PATH(migration, gpio_1000)),
"");
zassert_equal(DT_INST_0_VND_GPIO_IRQ_0,
DT_IRQN(DT_INST(0, vnd_gpio)),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_IRQ_0,
DT_IRQN(DT_ALIAS(mgr_gpio)),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_IRQ_0,
DT_IRQN(DT_NODELABEL(migration_gpio)),
"");
/* irq priority */
zassert_equal(DT_VND_GPIO_1000_IRQ_0_PRIORITY,
DT_IRQ(DT_PATH(migration, gpio_1000), priority),
"");
zassert_equal(DT_INST_0_VND_GPIO_IRQ_0_PRIORITY,
DT_IRQ(DT_INST(0, vnd_gpio), priority),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_IRQ_0_PRIORITY,
DT_IRQ(DT_ALIAS(mgr_gpio), priority),
"");
zassert_equal(DT_ALIAS_MGR_GPIO_IRQ_0_PRIORITY,
DT_IRQ(DT_NODELABEL(migration_gpio), priority),
"");
}
/*
* The serial device is how we test specific properties.
*/
static void test_serial(void)
{
zassert_equal(DT_VND_SERIAL_3000_BAUD_RATE,
DT_PROP(DT_PATH(migration, serial_3000), baud_rate),
"");
zassert_equal(DT_ALIAS_MGR_SERIAL_BAUD_RATE,
DT_PROP(DT_ALIAS(mgr_serial), baud_rate),
"");
zassert_equal(DT_ALIAS_MGR_SERIAL_BAUD_RATE,
DT_PROP(DT_NODELABEL(migration_serial), baud_rate),
"");
zassert_equal(DT_INST_0_VND_SERIAL_BAUD_RATE,
DT_PROP(DT_INST(0, vnd_serial), baud_rate),
"");
}
/*
* The I2C and SPI devices are used to test inter-device relationships.
*/
#define I2C_DEV_PATH DT_PATH(migration, i2c_10000, i2c_dev_10)
#define I2C_DEV_ALIAS DT_ALIAS(mgr_i2c_dev)
#define I2C_DEV_NODELABEL DT_NODELABEL(mgr_i2c_device)
#define I2C_DEV_INST DT_INST(0, vnd_i2c_device)
static void test_i2c_device(void)
{
/* Bus controller name */
zassert_true(!strcmp(DT_VND_I2C_10000_VND_I2C_DEVICE_10_BUS_NAME,
DT_LABEL(DT_BUS(I2C_DEV_PATH))),
"");
zassert_true(!strcmp(DT_ALIAS_MGR_I2C_DEV_BUS_NAME,
DT_LABEL(DT_BUS(I2C_DEV_ALIAS))),
"");
zassert_true(!strcmp(DT_ALIAS_MGR_I2C_DEV_BUS_NAME,
DT_LABEL(DT_BUS(I2C_DEV_NODELABEL))),
"");
zassert_true(!strcmp(DT_INST_0_VND_I2C_DEVICE_BUS_NAME,
DT_LABEL(DT_BUS(I2C_DEV_INST))),
"");
}
#define SPI_DEV_PATH DT_PATH(migration, spi_20000, spi_dev_0)
#define SPI_DEV_ALIAS DT_ALIAS(mgr_spi_dev)
#define SPI_DEV_NODELABEL DT_NODELABEL(mgr_spi_device)
#define SPI_DEV_INST DT_INST(0, vnd_spi_device)
static void test_spi_device(void)
{
/* cs-gpios controller label */
zassert_true(
!strcmp(DT_VND_SPI_20000_VND_SPI_DEVICE_0_CS_GPIOS_CONTROLLER,
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_PATH)),
"");
zassert_true(!strcmp(DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_CONTROLLER,
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_ALIAS)),
"");
zassert_true(!strcmp(DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_CONTROLLER,
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_NODELABEL)),
"");
zassert_true(!strcmp(DT_INST_0_VND_SPI_DEVICE_CS_GPIOS_CONTROLLER,
DT_SPI_DEV_CS_GPIOS_LABEL(SPI_DEV_INST)),
"");
/* cs-gpios pin number */
zassert_equal(DT_VND_SPI_20000_VND_SPI_DEVICE_0_CS_GPIOS_PIN,
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_PATH),
"");
zassert_equal(DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_PIN,
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_ALIAS),
"");
zassert_equal(DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_PIN,
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_NODELABEL),
"");
zassert_equal(DT_INST_0_VND_SPI_DEVICE_CS_GPIOS_PIN,
DT_SPI_DEV_CS_GPIOS_PIN(SPI_DEV_INST),
"");
/* cs-gpios GPIO flags */
zassert_equal(DT_VND_SPI_20000_VND_SPI_DEVICE_0_CS_GPIOS_FLAGS,
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_PATH),
"");
zassert_equal(DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_FLAGS,
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_ALIAS),
"");
zassert_equal(DT_ALIAS_MGR_SPI_DEV_CS_GPIOS_FLAGS,
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_NODELABEL),
"");
zassert_equal(DT_INST_0_VND_SPI_DEVICE_CS_GPIOS_FLAGS,
DT_SPI_DEV_CS_GPIOS_FLAGS(SPI_DEV_INST),
"");
}
void test_main(void)
{
ztest_test_suite(devicetree_legacy_api,
ztest_unit_test(test_gpio),
ztest_unit_test(test_serial),
ztest_unit_test(test_i2c_device),
ztest_unit_test(test_spi_device)
);
ztest_run_test_suite(devicetree_legacy_api);
}

View file

@ -1,8 +0,0 @@
tests:
libraries.devicetree.legacy:
tags: devicetree
# We only need this to run on one platform so use native_posix as it
# will mostly likely be the fastest.
integration_platforms:
- native_posix
platform_exclude: pinnacle_100_dvk