Legacy devicetree macros¶
Warning
As of Zephyr v2.3, you can still use these macros if you set
CONFIG_LEGACY_DEVICETREE_MACROS=y
in your application’s Kconfig.
Legacy macro support will be maintained for at least two releases, including
v2.3 itself.
See Devicetree access from C/C++ for a usage guide for the new API, and Migrate from the legacy macros for a migration guide for existing code.
This page describes legacy C preprocessor macros which Zephyr’s build system generates from a devicetree. It assumes you’re familiar with the concepts in Introduction to devicetree and Devicetree bindings.
These macros have somewhat inconsistent names, and their use in new code is discouraged. See Devicetree access from C/C++ for the recommended API.
These macros are generated by the devicetree scripts,
start with DT_
, and use all-uppercase.
Legacy node identifiers¶
Macros generated from individual devicetree nodes or their properties start
with DT_<node>
, where <node>
is a C identifier for the devicetree node.
This section describes the different <node>
values in the legacy macros.
We’ll use the following DTS fragment from the FRDM-K64F board’s devicetree as the main example throughout this section.
/ {
aliases {
i2c-0 = &i2c0;
};
soc {
i2c0: i2c@40066000 {
compatible = "nxp,kinetis-i2c";
reg = <0x40066000 0x1000>;
status = "okay";
/* ... */
fxos8700@1d {
compatible = "nxp,fxos8700";
status = "okay";
/* ... */
};
};
};
};
The binding for the “nxp,fxos8700” compatible property contains this line:
on-bus: i2c
The generated macros for this example can be found in a build directory for the
FXOS8700 sample application built for the frdm_k64f
board, in the file
build/zephyr/include/generated/devicetree_legacy_unfixed.h
.
Here is part of devicetree_legacy_unfixed.h
showing some of the macros
for the node labeled i2c0
. Notice the comment with the node’s path in the
devicetree and its dependency relationships with other nodes.
/*
* Devicetree node:
* /soc/i2c@40066000
*
* Binding (compatible = nxp,kinetis-i2c):
* $ZEPHYR_BASE/dts/bindings/i2c/nxp,kinetis-i2c.yaml
*
* Dependency Ordinal: 66
*
* Requires:
* 6 /soc
* ...
*
* Supports:
* 67 /soc/i2c@40066000/fxos8700@1d
*
* Description:
* Kinetis I2C node
*/
#define DT_NXP_KINETIS_I2C_40066000_BASE_ADDRESS 0x40066000
#define DT_NXP_KINETIS_I2C_40066000_SIZE 4096
#define DT_ALIAS_I2C_0_BASE_ADDRESS DT_NXP_KINETIS_I2C_40066000_BASE_ADDRESS
#define DT_ALIAS_I2C_0_SIZE DT_NXP_KINETIS_I2C_40066000_SIZE
#define DT_INST_0_NXP_KINETIS_I2C_BASE_ADDRESS DT_NXP_KINETIS_I2C_40066000_BASE_ADDRESS
#define DT_INST_0_NXP_KINETIS_I2C_SIZE DT_NXP_KINETIS_I2C_40066000_SIZE
Most macros are generated for individual nodes or their properties. Some macros are generated for “global” information about the entire devicetree.
In this example, the node identifiers for i2c@40066000
are:
NXP_KINETIS_I2C_40066000
ALIAS_I2C_0
INST_0_NXP_KINETIS_I2C
In general, the following DT_<node>
macro prefixes are created for each
node.
DT_(<bus>_)<compatible>_<unit-address>
The node’s compatible property converted to a C identifier, followed by its unit address. If the node has multiple compatible strings, the one for its matching binding is used.
If the node appears on a bus (and therefore has
on-bus:
in its binding, likefxos8700@1d
does), then the compatible string and unit address of the bus node is put before the compatible string for the node itself. If the node does not appear on a bus (noon-bus:
in the binding, likei2c@40066000
) then there will be no<bus>_
portion in the node identifier.The
i2c@40066000
node identifier isNXP_KINETIS_I2C_40066000
:there is no
<bus>_
portion<compatible>
isNXP_KINETIS_I2C
, which is its compatible"nxp,kinetis-i2c"
converted to a C identifier by uppercasing and replacing non-alphanumeric characters with underscores<unit-address>
is40066000
The
fxos8700@1d
node identifier isNXP_KINETIS_I2C_40066000_NXP_FXOS8700_1D
:<bus>
isNXP_KINETIS_I2C_40066000
<compatible>
isNXP_FXOS8700
<unit-address>
is1D
If the node has no unit address, the unit address of the parent node plus the node’s name converted to a C identifier is used for
<unit-address>
instead. If the parent node has no unit address either, the name of the node is used as a fallback.For example, take this DTS fragment:
ethernet@400c0004 { compatible = "nxp,kinetis-ethernet"; reg = <0x400c0004 0x620>; status = "okay"; ptp { compatible = "nxp,kinetis-ptp"; status = "okay"; interrupts = <0x52 0x0>; }; };
The
ptp
node identifier isNXP_KINETIS_PTP_400C0004_PTP
:there is no
<bus>_
portion<compatible>
isNXP_KINETIS_PTP
<unit-address>
is400C0004_PTP
, which combines its parent’s unit address and the node’s name converted to a C identifier
Here is another example DTS fragment.
soc { temp1 { compatible = "nxp,kinetis-temperature"; status = "okay"; }; };
The
temp1
node identifier isNXP_KINETIS_TEMPERATURE_TEMP1
:there is no
<bus>_
portion<compatible>
isNXP_KINETIS_TEMPERATURE
<unit-address>
is the fallback valueTEMP1
, because neither the node nor its parent have a unit address
DT_INST_<instance-number>_<compatible>
An instance number for the node, combined with its compatible converted to a C identifier.
The instance number is a unique index among all enabled (
status = "okay"
) nodes that have a particular compatible string, starting from zero.The
i2c@40066000
node identifier in the main example isINST_0_NXP_KINETIS_I2C
:<instance-number>
is 0 because it was the first node with compatible “nxp,kinetis-i2c” that the devicetree scripts happened to discover as they walked the tree<compatible>
isNXP_KINETIS_I2C
As another example, if there are two enabled nodes that have
compatible = "foo,uart"
, then these node identifiers get generated:INST_0_FOO_UART INST_1_FOO_UART
Warning
Instance numbers are simple indexes among enabled nodes with the same compatible. They in no way reflect any numbering scheme that might exist in SoC documentation, node labels or unit addresses, or properties of the /aliases node.
There is no guarantee that the same node will have the same instance number between application builds. The only guarantee is that instance numbers will start at 0, be contiguous, and be assigned for each enabled node with a matching compatible.
DT_ALIAS_<alias>
Generated from the names of any properties in the
/aliases
node. See aliases and chosen nodes for an overview.Here is a simple example.
/ { aliases { uart-1 = &my_uart; }; my_uart: uart@12345 { /* ... */ }; };
The
uart@12345
node identifier isALIAS_UART_1
:<alias>
isUART_1
by uppercasinguart-1
and replacing non-alphanumeric characters with underscores. The alias refers touart@12345
using its labelmy_uart
.For such a simple concept, dealing with aliases can be surprisingly tricky due to multiple names which have only minor differences.
For a real-world example, the
i2c@40066000
node’s alias identifier in the main example isALIAS_I2C_0
:<alias>
isI2C_0
because the propertyi2c-0 = &i2c0;
in the/aliases
node “points at”i2c@40066000
using its labeli2c0
. The alias namei2c-0
is converted to C identifierI2C_0
.The differences between
i2c-0
,&i2c0
,i2c0
, andi2c@40006000
in this example are very subtle and can be quite confusing at first. Here is some more clarification:i2c-0
is the name of a property in the/aliases
node; this is the alias name&i2c0
is that property’s value, which is the phandle of the the node with labeli2c0
i2c@40006000
is the name of the node which happens to have labeli2c0
in this example
See the devicetree specification for full details.
Note
Another
DT_<compatible>_<alias>
form is also generated for aliases. For the example above, assuming the compatible string for the&uart1
node is"foo,uart"
, this givesDT_FOO_UART_UART_1
.
Macros generated for properties¶
Macros for node property values have the form DT_<node>_<property>
, where
<node>
is a node identifier and <property>
identifies the property. The macros generated for a property usually depend on
its type:
key in the matching devicetree binding.
The following general purpose rules apply in most cases:
However, some “special” properties get individual treatment:
No macros are currently generated for properties with type phandle
,
phandles
, path
, or compound
.
Generic property macros¶
This section documents the macros generated for non-“special” properties by
example. These properties are handled based on their devicetree binding
type:
keys.
In the generic case, the <property>
portion of a DT_<node>_<property>
macro begins with the property’s name converted to a C identifier by
uppercasing it and replacing non-alphanumeric characters with underscores. For
example, a baud-rate
property has a <property>
portion that starts with
BAUD_RATE
.
The table below gives the values generated for simple types. Note that an index
is added at the end of identifiers generated from properties with array
or
string-array
type, and that array
properties generate an additional
compound initializer ({ ... }
).
Type |
Property and value |
Generated macros |
---|---|---|
|
|
|
|
|
#define DT_<node>_FOO_0 1 #define DT_<node>_FOO_1 2 #define DT_<node>_FOO {1, 2} |
|
|
|
|
|
#define DT_<node>_FOO_0 "bar" #define DT_<node>_FOO_1 "baz" |
|
|
|
For type: boolean
, the generated macro is set to 1 if the property exists
on the node, and to 0 otherwise:
#define DT_<node>_FOO 0/1
For non-boolean types the property macros are not generated if the binding’s
category
is optional
and the property is not present in the devicetree
source.
Properties with type phandle-array
¶
The generation for properties with type phandle-array
is the most complex.
To understand it, it is a good idea to first go through the documentation for
phandle-array
in Devicetree bindings.
Take the following devicetree nodes and binding contents as an example:
pwm_ctrl_0: pwm-controller-0 {
label = "pwm-0";
#pwm-cells = <2>;
/* ... */
};
pwm_ctrl_1: pwm-controller-1 {
label = "pwm-1";
#pwm-cells = <2>;
/* ... */
};
pwm-cells
declaration in binding for vendor,pwm-controller
¶pwm-cells:
- channel
- period
Assume the property assignment looks like this:
pwm-user@0 {
status = "okay";
pwms = <&pwm_ctrl_0 1 10>, <&pwm_ctrl_1 2 20>;
pwm-names = "first", "second";
/* ... */
};
These macros then get generated.
#define DT_<node>_PWMS_CONTROLLER_0 "pwm-0"
#define DT_<node>_PWMS_CHANNEL_0 1
#define DT_<node>_PWMS_PERIOD_0 10
#define DT_<node>_PWMS_CONTROLLER_1 "pwm-1"
#define DT_<node>_PWMS_CHANNEL_1 2
#define DT_<node>_PWMS_PERIOD_1 20
#define DT_<node>_PWMS_NAMES_0 "first"
#define DT_<node>_PWMS_NAMES_1 "second"
#define DT_<node>_FIRST_PWMS_CONTROLLER DT_<node>_PWMS_CONTROLLER_0
#define DT_<node>_FIRST_PWMS_CHANNEL DT_<node>_PWMS_CHANNEL_0
#define DT_<node>_FIRST_PWMS_PERIOD DT_<node>_PWMS_PERIOD_0
#define DT_<node>_SECOND_PWMS_CONTROLLER DT_<node>_PWMS_CONTROLLER_1
#define DT_<node>_SECOND_PWMS_CHANNEL DT_<node>_PWMS_CHANNEL_1
#define DT_<node>_SECOND_PWMS_PERIOD DT_<node>_PWMS_PERIOD_1
/* Initializers */
#define DT_<node>_PWMS_0 {"pwm-0", 1, 10}
#define DT_<node>_PWMS_1 {"pwm-1", 2, 20}
#define DT_<node>_PWMS {DT_<node>_PWMS_0, DT_<node>_PWMS_1}
#define DT_<node>_PWMS_COUNT 2
Macros with a *_0
suffix deal with the first entry in pwms
(<&pwm_ctrl_0 1 10>
). Macros with a *_1
suffix deal with the second
entry (<&pwm_ctrl_1 2 20>
). The index suffix is only added if there’s more
than one entry in the property.
The DT_<node>_PWMS_CONTROLLER(_<index>)
macros are set to the string from
the label
property of the referenced controller. The
DT_<node>_PWMS_CHANNEL(_<index>)
and DT_<node>_PWMS_PERIOD(_<index>)
macros are set to the values of the corresponding cells in the pwms
property, with macro names generated from the strings in pwm-cells:
in
the binding for the controller.
The macros in the /* Initializers */
section provide the same information
as DT_<node>_PWMS_{CONTROLLER,CHANNEL,PERIOD}(_<index>)
, except as compound
initializers that can be used to initialize C struct
variables.
If a pwm-names
property exists on the same node as pwms
(more
generally, if a foo-names
property is defined next to a foo
property
with type phandle-array
), it gives a list of strings that name each entry
in pwms
. The names are used to generate extra macro names with the name
instead of an index, like DT_<node>_FIRST_PWMS_CONTROLLER
above.
Properties with enum:
in the binding¶
Properties declared with an enum:
key in their binding generate a macro
that gives the the zero-based index of the property’s value in the enum:
list.
Take this binding declaration as an example:
properties:
foo:
type: string
enum:
- one
- two
- three
The property foo = "three"
then generates this macro:
#define DT_<node>_FOO_ENUM 2
reg
property macros¶
reg
properties generate the macros DT_<node>_BASE_ADDRESS(_<index>)
and
DT_<node>_SIZE(_<index>)
. <index>
is a numeric index starting from 0,
which is only added if there’s more than one register defined in reg
.
For example, the reg = <0x4004700 0x1060>
assignment in the example
devicetree above gives these macros:
#define DT_<node>_BASE_ADDRESS 0x40047000
#define DT_<node>_SIZE 4192
Note
The length of the address and size portions of reg
is determined from
the #address-cells
and #size-cells
properties. See the devicetree
specification for more information.
In this case, both #address-cells
and #size-cells
are 1, and there’s
just a single register in reg
. Four numbers would give two registers.
If a reg-names
property exists on the same node as reg
, it gives a list
of strings that names each register in reg
. The names are used to generate
extra macros. For example, reg-names = "foo"
together with the example node
generates these macros:
#define DT_<node>_FOO_BASE_ADDRESS 0x40047000
#define DT_<node>_FOO_SIZE 4192
interrupts
property macros¶
Take these devicetree nodes as an example:
timer@123 {
interrupts = <1 5 2 6>;
interrupt-parent = <&intc>;
/* ... */
};
intc: interrupt-controller { /* ... */ };
Assume that the binding for interrupt-controller
has these lines:
interrupt-cells:
- irq
- priority
Then these macros get generated for timer@123
:
#define DT_<node>_IRQ_0 1
#define DT_<node>_IRQ_0_PRIORITY 5
#define DT_<node>_IRQ_1 2
#define DT_<node>_IRQ_1_PRIORITY 6
These macros have the the format DT_<node>_IRQ_<index>(_<name>)
, where
<node>
is the node identifier for timer@123
, <index>
is an index
that identifies the particular interrupt, and <name>
is the identifier for
the cell value (a number within interrupts = <...>
), taken from the
binding.
Bindings for interrupt controllers are expected to declare a cell named irq
in interrupt-cells
, giving the interrupt number. The _<name>
suffix is
skipped for macros generated from irq
cells, which is why there’s e.g. a
DT_<node>_IRQ_0
macro and no DT_<node>_IRQ_0_IRQ
macro.
If the interrupt controller in turn generates other interrupts, Zephyr uses a multi-level interrupt encoding for the interrupt numbers at each level. See Multi-level Interrupt handling for more information.
There is also hard-coded logic for mapping Arm GIC interrupts to linear IRQ numbers. See the source code for details.
Additional macros that use names instead of indices for interrupts can be
generated by including an interrupt-names
property on the
interrupt-generating node. For example, this node:
timer@456 {
interrupts = <10 50 20 60>;
interrupt-parent = <&intc>;
interrupt-names = "timer-a", "timer-b";
/* ... */
};
generates these macros:
#define DT_<node>_IRQ_TIMER_A 1
#define DT_<node>_IRQ_TIMER_A_PRIORITY 5
#define DT_<node>_IRQ_TIMER_B 2
#define DT_<node>_IRQ_TIMER_B_PRIORITY 6
clocks
property macros¶
clocks
work the same as other Properties with type phandle-array, except the
generated macros have CLOCK
in them instead of CLOCKS
, giving for
example DT_<node>_CLOCK_CONTROLLER_0
instead of
DT_<node>_CLOCKS_CONTROLLER_0
.
If a clocks
controller node has a "fixed-clock"
compatible, it
must also have a clock-frequency
property giving its frequency in Hertz.
In this case, an additional macro is generated:
#define DT_<node>_CLOCKS_CLOCK_FREQUENCY <frequency>
cs-gpios
property macros¶
Take these devicetree nodes as an example. where the binding for
vendor,spi-controller
is assumed to have bus: spi
, and the bindings for
the SPI slaves are assumed to have on-bus: spi
:
gpioa: gpio@400ff000 {
compatible = "vendor,gpio-ctlr";
reg = <0x400ff000 0x40>;
label = "GPIOA";
gpio-controller;
#gpio-cells = <0x1>;
};
spi {
compatible = "vendor,spi-controller";
cs-gpios = <&gpioa 1>, <&gpioa 2>;
spi-slave@0 {
compatible = "vendor,foo-spi-device";
reg = <0>;
};
spi-slave@1 {
compatible = "vendor,bar-spi-device";
reg = <1>;
};
};
Here, the unit address of the SPI slaves (0 and 1) is taken as a chip select
number, which is used as an index into cs-gpios
(a phandle-array
).
spi-slave@0
is matched to <&gpioa 1>
, and spi-slave@1
to
<&gpiob 2>
.
The output for spi-slave@0
and spi-slave@1
is the same as if the
devicetree had looked like this:
gpioa: gpio@400ff000 {
compatible = "vendor,gpio-ctlr";
reg = <0x400ff000 0x40>;
label = "GPIOA";
gpio-controller;
#gpio-cells = <1>;
};
spi {
compatible = "vendor,spi-controller";
spi-slave@0 {
compatible = "vendor,foo-spi-device";
reg = <0>;
cs-gpios = <&gpioa 1>;
};
spi-slave@1 {
compatible = "vendor,bar-spi-device";
reg = <1>;
cs-gpios = <&gpioa 2>;
};
};
See the phandle-array
section in Generic property macros for more
information.
For example, since the node labeled gpioa
has property
label = "GPIOA"
and 1 and 2 are pin numbers, macros like the following
will be generated for spi-slave@0
:
#define DT_<node>_CS_GPIOS_CONTROLLER "GPIOA"
#define DT_<node>_CS_GPIOS_PIN 1
Other macros¶
These are generated in addition to macros generated for properties.
Node existence flags¶
An “existence flag” is a macro which is defined when the devicetree contains nodes matching some criterion.
Existence flags are generated for each compatible property that appears on an enabled node:
#define DT_COMPAT_<compatible> 1
An existence flag is also written for all enabled nodes with a matching compatible:
#define DT_INST_<instance-number>_<compatible> 1
For the i2c@40066000
node in the example above,
assuming the node is the first node with compatible = "nxp,kinetis-i2c"
,
the following existence flags would be generated:
/* At least one node had compatible nxp,kinetis-i2c: */
#define DT_COMPAT_NXP_KINETIS_I2C 1
/* Instance 0 of compatible nxp,kinetis-i2c exists: */
#define DT_INST_0_NXP_KINETIS_I2C 1
If additional nodes had compatible nxp,kinetis-i2c
, additional existence
flags would be generated:
#define DT_INST_1_NXP_KINETIS_I2C 1
#define DT_INST_2_NXP_KINETIS_I2C 1
/* ... and so on, one for each node with this compatible. */
Macros generated from flash partitions¶
Note
This section only covers flash partitions. See aliases and chosen nodes for
some other flash-related macros that get generated from devicetree, via
/chosen
.
If a node has a name that looks like partition@<unit-address>
, it is
assumed to represent a flash partition.
Assume the devicetree has this:
flash@0 {
/* ... */
label = "foo-flash";
partitions {
/* ... */
#address-cells = <1>;
#size-cells = <1>;
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 0x00010000>;
read-only;
};
slot0_partition: partition@10000 {
label = "image-0";
reg = <0x00010000 0x00020000
0x00040000 0x00010000>;
};
/* ... */
};
These macros then get generated:
#define DT_FLASH_AREA_MCUBOOT_ID 0
#define DT_FLASH_AREA_MCUBOOT_READ_ONLY 1
#define DT_FLASH_AREA_MCUBOOT_OFFSET_0 0x0
#define DT_FLASH_AREA_MCUBOOT_SIZE_0 0x10000
#define DT_FLASH_AREA_MCUBOOT_OFFSET DT_FLASH_AREA_MCUBOOT_OFFSET_0
#define DT_FLASH_AREA_MCUBOOT_SIZE DT_FLASH_AREA_MCUBOOT_SIZE_0
#define DT_FLASH_AREA_MCUBOOT_DEV "foo-flash"
#define DT_FLASH_AREA_IMAGE_0_ID 0
#define DT_FLASH_AREA_IMAGE_0_READ_ONLY 1
#define DT_FLASH_AREA_IMAGE_0_OFFSET_0 0x10000
#define DT_FLASH_AREA_IMAGE_0_SIZE_0 0x20000
#define DT_FLASH_AREA_IMAGE_0_OFFSET_1 0x40000
#define DT_FLASH_AREA_IMAGE_0_SIZE_1 0x10000
#define DT_FLASH_AREA_IMAGE_0_OFFSET DT_FLASH_AREA_IMAGE_0_OFFSET_0
#define DT_FLASH_AREA_IMAGE_0_SIZE DT_FLASH_AREA_IMAGE_0_SIZE_0
#define DT_FLASH_AREA_IMAGE_0_DEV "foo-flash"
/* Same macros, just with index instead of label */
#define DT_FLASH_AREA_0_ID 0
#define DT_FLASH_AREA_0_READ_ONLY 1
...
The *_ID
macro gives the zero-based index for the partition.
The *_OFFSET_<index>
and *_SIZE_<index>
macros give the offset and size
for each partition, derived from reg
. The *_OFFSET
and *_SIZE
macros, with no index, are aliases that point to the first sector (with index
0).
aliases
and chosen
nodes¶
Using an alias with a common name for a particular node makes it easier for you
to write board-independent source code. Devicetree aliases
nodes are used
for this purpose, by mapping certain generic, commonly used names to specific
hardware resources:
aliases {
led0 = &led0;
sw0 = &button0;
sw1 = &button1;
uart-0 = &uart0;
uart-1 = &uart1;
};
Certain software subsystems require a specific hardware resource to bind to in
order to function properly. Some of those subsystems are used with many
different boards, which makes using the devicetree chosen
nodes very
convenient. By doing so, the software subsystem can rely on having the specific
hardware peripheral assigned to it. In the following example we bind the shell
to uart1
in this board:
chosen {
zephyr,shell-uart = &uart1;
};
The table below lists Zephyr-specific chosen
properties. The macro
identifiers that start with CONFIG_*
are generated from Kconfig symbols
that reference devicetree data via the Kconfig preprocessor.
Note
Since the particular devicetree isn’t known while generating Kconfig documentation, the Kconfig symbol reference pages linked below do not include information derived from devicetree. Instead, you might see e.g. an empty default:
default "" if HAS_DTS
To see how the preprocessor is used for a symbol, look it up directly in the
Kconfig
file where it is defined instead. The reference page for the
symbol gives the definition location.
|
Generated macros |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Legacy flash partitions¶
Devicetree can be used to describe a partition layout for any flash device in the system.
Two important uses for this mechanism are:
To force the Zephyr image to be linked into a specific area on Flash.
This is useful, for example, if the Zephyr image must be linked at some offset from the flash device’s start, to be loaded by a bootloader at runtime.
To generate compile-time definitions for the partition layout, which can be shared by Zephyr subsystems and applications to operate on specific areas in flash.
This is useful, for example, to create areas for storing file systems or other persistent state. These defines only describe the boundaries of each partition. They don’t, for example, initialize a partition’s flash contents with a file system.
Partitions are generally managed using device tree overlays. See Set devicetree overlays for examples.
Defining Partitions¶
The partition layout for a flash device is described inside the
partitions
child node of the flash device’s node in the device
tree.
You can define partitions for any flash device on the system.
Most Zephyr-supported SoCs with flash support in device tree
will define a label flash0
. This label refers to the primary
on-die flash programmed to run Zephyr. To generate partitions
for this device, add the following snippet to a device tree overlay
file:
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* Define your partitions here; see below */
};
};
To define partitions for another flash device, modify the above to either use its label or provide a complete path to the flash device node in the device tree.
The content of the partitions
node looks like this:
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition1_label: partition@START_OFFSET_1 {
label = "partition1_name";
reg = <0xSTART_OFFSET_1 0xSIZE_1>;
};
/* ... */
partitionN_label: partition@START_OFFSET_N {
label = "partitionN_name";
reg = <0xSTART_OFFSET_N 0xSIZE_N>;
};
};
Where:
partitionX_label
are device tree labels that can be used elsewhere in the device tree to refer to the partitionpartitionX_name
controls how defines generated by the Zephyr build system for this partition will be namedSTART_OFFSET_x
is the start offset in hexadecimal notation of the partition from the beginning of the flash deviceSIZE_x
is the hexadecimal size, in bytes, of the flash partition
The partitions do not have to cover the entire flash device. The device tree compiler currently does not check if partitions overlap; you must ensure they do not when defining them.
Example Primary Flash Partition Layout¶
Here is a complete (but hypothetical) example device tree overlay snippet illustrating these ideas. Notice how the partitions do not overlap, but also do not cover the entire device.
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
code_dts_label: partition@8000 {
label = "zephyr-code";
reg = <0x00008000 0x34000>;
};
data_dts_label: partition@70000 {
label = "application-data";
reg = <0x00070000 0xD000>;
};
};
};
Linking Zephyr Within a Partition¶
To force the linker to output a Zephyr image within a given flash partition, add this to a device tree overlay:
/ {
chosen {
zephyr,code-partition = &slot0_partition;
};
};
Then, enable the CONFIG_USE_DT_CODE_PARTITION
Kconfig option.
Flash Partition Macros¶
The Zephyr build system generates definitions for each flash device
partition. These definitions are available to any files which
include <zephyr.h>
.
Consider this flash partition:
dts_label: partition@START_OFFSET {
label = "def-name";
reg = <0xSTART_OFFSET 0xSIZE>;
};
The build system will generate the following corresponding defines:
#define DT_FLASH_AREA_DEF_NAME_DEV "def-name"
#define DT_FLASH_AREA_DEF_NAME_OFFSET_0 0xSTART_OFFSET
#define DT_FLASH_AREA_DEF_NAME_SIZE_0 0xSIZE
#define DT_FLASH_AREA_DEF_NAME_OFFSET DT_FLASH_AREA_DEF_NAME_OFFSET_0
#define DT_FLASH_AREA_DEF_NAME_SIZE DT_FLASH_AREA_DEF_NAME_SIZE_0
As you can see, the label
property is capitalized when forming the
macro names. Other simple conversions to ensure it is a valid C
identifier, such as converting “-” to “_”, are also performed. The
offsets and sizes are available as well.
MCUboot Partitions¶
MCUboot is a secure bootloader for 32-bit microcontrollers.
Some Zephyr boards provide definitions for the flash partitions which are required to build MCUboot itself, as well as any applications which must be chain-loaded by MCUboot.
The device tree labels for these partitions are:
- boot_partition
This is the partition where the bootloader is expected to be placed. MCUboot’s build system will attempt to link the MCUboot image into this partition.
- slot0_partition
MCUboot loads the executable application image from this partition. Any application bootable by MCUboot must be linked to run from this partition.
- slot1_partition
This is the partition which stores firmware upgrade images. Zephyr applications which receive firmware updates must ensure the upgrade images are placed in this partition (the Zephyr DFU subsystem can be used for this purpose). MCUboot checks for upgrade images in this partition, and can move them to
slot0_partition
for execution. Theslot0_partition
andslot1_partition
must be the same size.- scratch_partition
This partition is used as temporary storage while swapping the contents of
slot0_partition
andslot1_partition
.
Important
Upgrade images are only temporarily stored in slot1_partition
.
They must be linked to execute of out of slot0_partition
.
See the MCUboot documentation for more details on these partitions.
File System Partitions¶
- storage_partition
This is the area where e.g. LittleFS or NVS or FCB expects its partition.
ABNF grammar¶
This section contains an Augmented Backus-Naur Form grammar for the macros generated from a devicetree. See RFC 7405 (which extends RFC 5234) for a syntax specification.
; 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