This guide walks through every part of the Devicetree Explorer user interface. The screenshots
come from the MASTERs 2026 class 26020 RTOS4 (SAM E54 Xplained Pro, Zephyr 4.3). To regenerate
them, run docs/make_screenshots.py.
The window has three areas:
| Area | Content |
|---|---|
| Header (top) | Build selection, reload, New build…, Full build, Port to board…, Board nodes…, Legend |
| Left panel | Tabs: Nodes (the devicetree), Include tree (the source files), Pins, Package (the chip's pins), Board (the connectors), Draft (pending configurator changes), Log (what the tool does, section 11a), Changes (what git sees in the app, section 11b) |
| Right panel | Details of whatever is selected on the left; the overview when nothing is selected |
When started without an argument, the tool first opens a folder dialog. Select a west
workspace, an app folder or a single build folder; every build below it is offered in the
Build list. The dialog starts in the last folder you used. With a path argument
(dt_explorer.bat D:\ws), the tool skips the dialog.
- Folder button (left): opens the folder dialog again and switches to another workspace without restarting. Its tooltip shows the current folder.
- Build: lists every Zephyr build folder found under the workspace, i.e. every folder that
contains
zephyr/edt.pickle. Picking an entry loads that build. - Reload: scans for builds again and reloads the current one. Press it after every
west build. - New build… and Full build: start a build from the explorer (see below).
- Port to board…: ports the app of the current build to another board (section 1.3).
- Board nodes…: adds LEDs, buttons, PWM LEDs, aliases and chosen entries to the draft (section 10.3).
- Legend (right end of the header): a menu with the text colours used in both trees and in the Origin column. It tells you which kind of file something comes from. Click it to open the key:
| Colour | Category | Typical files |
|---|---|---|
| green | Overlay (app) | apps/<lab>/boards/<board>.overlay |
| blue | Board | zephyr/boards/.../<board>.dts, ...-pinctrl.dtsi |
| grey | SoC / Zephyr | zephyr/dts/arm/...dtsi, zephyr/include/.../dt-bindings/*.h |
| purple | Module | modules/... (e.g. pinctrl headers from hal_atmel) |
| yellow | Binding default | Value written in no .dts file; it comes only from default: in the binding YAML |
Creates a new build, for example for another app or board, without leaving the explorer.
- App folder and Board (with a filter): the app to build and the board to build it for.
- Build folder name: defaults to
build_<board>; the full path is shown below it. - Start: runs a cmake-only build, which is quick but does not link the image. Its devices show the cyan Driver built, not linked yet state until a full build has run (see 1.2).
- When the build finishes, the list is refreshed and the new build is selected.
- Cancel build stops a running build; Close hides the dialog.
Full build runs west build -d <build folder> for the build selected in the header. The log
streams into the dialog. When the build succeeds, the explorer reloads it: the cyan states turn into
green (device created) or red (driver built, no device). Use it after a cmake-only build, or after
changing the app. Cancel build stops it.
Port to board… takes the app of the current build and shows what it would look like on another
board of the same app: the overlay and .conf lines for the target, and a report of what could not be
carried over. The source build is not changed. Nothing is written into the app until you press Apply to app… and confirm (below).
- Target build: a build of the same app for another board (the list shows only builds of a different board). Create target build… opens New build… for it; a cmake-only build is enough. Or type a build folder path below the list.
- Include source board configuration (checkbox, off by default): off, the port carries the app's
configuration (the app overlay and the draft) and leaves what only the source board file configures to the
target board's own file (pad overrides of a PWM, Ethernet, ...). Those nodes are listed in section 1
(
[not_ported]) and section 5 asnot ported (the target board's own applies), and the status line counts them. On, the board configuration is carried over as well. A node that the app also touches is ported without the properties, pads, chosen entries and aliases that the board file wrote. - Run port: runs the port. The log shows the steps, and the result appears below the log.
The result has these parts:
- Target overlay and Target .conf: the text for the target, with Copy and Download.
- Port report: numbered sections, each one expandable:
| Section | Content |
|---|---|
| 1. Nodes: translated and untranslatable properties | Each node of the source and its translation to the target's binding. Lines say what is identical, what is not emitted, or why a node is not ported (no_target: the target workspace has no binding or driver for it; no_node: the target build has no such node) |
| 2. Kconfig differences | Each prj.conf line of the source with its value in the source and in the target .config, e.g. a symbol that the target Kconfig tree does not define |
| 3. Node references in src/ that the target does not resolve | Each DT_NODELABEL(...) / DT_ALIAS(...) of the app that the target lacks, with a suggestion |
| 4. Pins not available on the target SoC | Pins that the source uses but the target package does not have |
| 5. Nodes without translation and not representable children | Nodes that have to be carried over by hand, and child nodes that the draft cannot represent yet |
| 5b. App-defined nodes of the app overlay | Nodes that the app's overlay defines, and whether each is carried over to the target (carried:). Both / { ... } and &{/} { ... } blocks (the form Board nodes… writes: LEDs, keys, PWM LEDs, aliases, chosen, zephyr,user) are read; an alias or chosen path must exist in the target or be carried too |
| 7. Connector roles of the used pads | For each connector pin that the port uses: the pin's role and pad on both boards, e.g. EXT1 pin 16 (MOSI): PB27 on same54_xpro → PB24 on pic32cx_sg41_cult, with the node that uses it. A pin is not available when its net does not reach the SoC. Needs a board description for both boards; without one the section is left out |
Below the sections, Fast check on the target build lists the findings of the same check as in the Draft tab (section 11) on the ported overlay.
The example ports lab0 (SAME54 Xplained Pro) to the PIC32CX SG41 Curiosity Ultra test build of lab0 (typed into the "or a build folder path" field, since it is not in the workspace). The first picture shows section 1 with three conflict nodes: &sercom2 (its TX/RX pads PB25/PB24 are used by &sercom0, which the target board enables), &sercom4 (the target board enables it as the UART console, so an SPI port would take it over) and &sercom7 (SDA/SCL PD08/PD09 are used by &sercom6). Each conflict has a suggestion: line. When a free instance of the target reaches the same connector pins, the node is not a conflict but remapped (see New lines of the report below); on these two boards none qualifies. The second picture shows section 7 with the connector roles, and section 3 flags DT_ALIAS(button0) as missing because /buttons/button_0 is not carried over yet.
- Verify with west: runs Zephyr's own check of the port on the target: a cmake-only build of the target
board into a scratch folder with the target overlay and
.conf(about 30 s). The target build is not touched. A green line says that Zephyr accepts the port; a red line says it rejects it, and the errors are shown under the log.
When a node of the source would collide with the target board, the port does not emit it. Section 1 then shows a conflict: line per reason, a not emitted; … line and a suggestion: line. The status line counts them (3 node(s) in conflict with the target board). There are three kinds: (a) the target board enables the same node with another compatible (the target's chosen and aliases are named); (b) a pad the port needs is used by another enabled node of the target build; (c) a pad is not on the target package. A DT_NODELABEL(...) or DT_ALIAS(...) in src/ that points at a conflicting node is listed in section 3 instead of resolving silently.
The suggestions are text. They name the target pads at the same connector pins, the instances that could mux those pads for the same roles (instances that are busy on the target are not offered any more), and the pads already in use. If you want a conflicting node on another instance, choose instance and pads with Configure… in the Draft workflow (section 10).
Besides the sections above, section 1, 3 and 5 of the report carry these lines (the example ports lab1 to the SG41 CUlt build; the status line counts only conflict nodes, a remapped node is not counted):
- Consumer lines. A node that uses a ported controller through a
phandle-arrayproperty is rewritten from the target controller's specifier names. The TC/TCC PWM has#pwm-cells2 on the old driver and 3 on the g1 driver, so the LED that uses it gets the third cell:consumer /pwmleds/pwm_led_0 pwms [override] <&tcc0 2 20000000> -> <&tcc0 2 20000000 0>, with the notecell 'polarity' is new in the target: 0. The second line of the picture is untranslatable:&usb0 [no_target]says that the target has no USB device node, binding or driver for that IP, so there is nothing to emit.
- Translation exists, not ported. An enabled SoC node (gpio, rtc, dac) that has a translation but no recipe to lift it into the draft is listed in section 5 with the reason; configure it by hand in the target board file.
- Remapped. A conflicting SERCOM whose connector pins are also on the pads of a free instance of the target is emitted on that instance:
[remapped], the linesremap MOSI: PB27 (EXT1 pin 16) -> PA22and the consequence for the code (DT_NODELABEL(sercom4)must becomeDT_NODELABEL(sercom3); section 3 lists it as[remapped]and the overlay adds an alias). The picture is from a run with changed board data (EXT1 pins 16 to 18 of the SG41 CUlt on free SERCOM3 pads, see docs/evidence/P8.17.md), because on the real CUlt boards no node qualifies (the SCK pin has no pad).
- Version note. The last line of the report says
source Zephyr 4.3.0, target Zephyr 4.5.0-rc1: app APIs may have changedwhen the source and the target workspace have different Zephyr versions; it is left out inside one workspace. It is about code outside the devicetree.
Apply to app… appears below the fast check and Verify with west. It is the only action in this dialog that writes files, and it asks before it does:
- Plan (shown after the click), one block per board file of the port result:
- NEW: the file does not exist in the app yet; it is written by Write files.
- EXISTS: the file exists and differs. Default is append: the app's own content stays and the port result goes into a block marked
/* dt_explorer port begin <date> */ … /* dt_explorer end */(id=port-apply-diff-<kind>shows the file after the append; applying again replaces only that block; a.confgets its lines added once, lines the file already has are left out). The block is valid DTS because the overlay may repeat&label { };and/ { };; an#includein the result stays inside the block (cpp accepts it there). Replace is the explicit alternative: tick Replace the existing … instead of appending (id=port-apply-replace-<kind>); the fold-out Replace instead (id=port-apply-diff-replace-<kind>) shows that diff. - identical (grey): the existing file already has this content; nothing is written.
- SKIP (COMMENTS ONLY): the port result holds only comments, e.g. a
.confwith no Kconfig lines; it is not written.
- Write files writes the files that are not skipped, and shows the result. The next step is the hint
west build -p always -b <target board> <app>(or New build… for a new build folder);-p alwaysmakes Zephyr read the new board files.
The file names follow Zephyr's board-file rules: boards/<board>.overlay and boards/<board>.conf when the target's board.yml lists exactly one SoC (true for both boards here), otherwise boards/<board>_<qualifiers> with / replaced by _. Zephyr finds these files on its own, so west build -b <board> uses them without extra options. Only <app>/boards/ is written. The source build and src/ are not touched, and applying to the source board itself is refused. The four plan and result pictures are from a test run on a copy of lab1 in the temp folder, because the button itself writes into the app.
This banner appears above everything else when a driver from the app or a module was compiled,
but no devicetree node with that compatible has status = "okay". The build succeeds anyway,
but no device is created, and the app does nothing.
Typical causes:
- The overlay was edited in the editor but not saved, so it is still empty on disk.
compatibleis misspelled.- The node has
status = "disabled".
The file path in the banner opens the driver source.
- Filter: shows only nodes whose label contains the text, e.g.
sercomorled. - Tree: the final devicetree after board, SoC and overlay have been merged. The entries
read
label: name@unit-address, e.g.my_led0: my_led@41008000. A label is what C code uses inDT_NODELABEL(...). - Text colour: the file that defines the node (see legend).
- Bold +
overlaybadge: the node is defined somewhere else, but the app overlay changed it, e.g. setstatus = "okay". - Dot: traffic light that shows whether the node became a device:
| Dot | Meaning | Is it a problem? |
|---|---|---|
| green: Device created | The image contains __device_dts_ord_<n>. |
— |
| orange: Driver not enabled | A driver exists, but Kconfig did not enable it. | Usually fine. Only a problem if the app needs this hardware. |
| red: Driver built, no device | The driver was compiled, but no device exists for this node. | Yes. Check the driver macro and the binding. |
| cyan: Driver built, not linked yet | The driver was compiled, but the build has no zephyr.elf/zephyr.map yet (e.g. west build --cmake-only), so it is unknown whether the device is in the image. |
No. Run a full build and press Reload. |
| dark grey: Disabled | status = "disabled". |
Only if you expected it to be on. |
| blue-grey: No driver | CPU, memory, a container, or a child node that the parent or the app reads directly. | Usually fine. |
| deep orange: No binding found | The node has a compatible, but no binding YAML matches it. Zephyr does not evaluate its properties and generates no DT_PROP() macros. |
Yes, if your code uses the node. Typical causes: a typo in compatible, or a missing dts_root / dts/bindings folder for out-of-tree bindings. |
The overview lists every DT_NODELABEL(...), DT_ALIAS(...) and DT_CHOSEN(...) in the app's
src/ folder. Each card shows:
- File:line (blue): opens the C source at that line.
- The code line itself.
- Macro → node: the devicetree node that the macro resolves to. Click the node to open it. The badge shows its traffic-light state.
- If a macro cannot be resolved, the card says "not in the devicetree!" in red. The usual cause is an unsaved overlay or a misspelled label or alias.
- Title: all labels (
&my_led0), the node name, and the full path. - Origin badge + "defined in": the file and line that create the node. Click it to open the source. All lines of this node are marked there.
- Status card: the traffic-light state with an explanation. Driver shows the C file whose
#define DT_DRV_COMPATmatches this node, and whether it was compiled. Click the path to open the driver at itsDT_DRV_COMPATline. - Configure… button: shown on a disabled node of a supported peripheral. It opens the configurator (section 10).
- Edit pins… button: shown on an enabled node of a supported peripheral. It opens the same dialog, filled with the node's current pins (section 10.1).
For child nodes without their own compatible (for example a button below gpio-keys), the
card explains that the parent's binding describes them and that the app reads them directly:
The example shows the I2C temperature sensor of lab3, whose binding lives in the app.
compatiblestrings of the node and the description from the binding.- Binding facts (shown when they apply):
on-bus: the node is a device on that bus, and itsregis the bus address (here I2C 0x4f).bus: child nodes of this node are devices on that bus.<space>-cells: the names of the cells that a reference passes. For example,gpio-cells: pin, flagsexplains why<&portb 31 0x11>means pin 31 with flags 0x11.child-binding: the child nodes are described by this binding.
- Binding file and its include: hierarchy: the matched YAML file and every file it includes,
recursively. Here the chain is
my-temp-sensor.yaml→i2c-device.yaml→base.yaml/pm.yaml, pluspower.yaml. Click any file to open it. - All N properties this binding allows (expandable table):
| Column | Meaning |
|---|---|
| Type, Required | As declared in the binding |
| In this node | set (written in a .dts file) · default (only the binding's default: applies) · not set |
| Default, Allowed values | default: and enum: / const: from the binding |
| Declared in | Which YAML file in the hierarchy declares the property. Click it to open the file at the declaration |
The table shows set, required and own properties. The switch adds the generic, unset
properties that come from base.yaml and similar files.
In this example the binding default of resolution-bits is 12, and the overlay sets it to 9.
If no binding matches, the card says so and the status card turns deep orange:
Below the card, Registers shows the decoded reg entries (address and size).
| Column | Meaning |
|---|---|
| Property | Name as in the devicetree |
| As written | Value as it appears in zephyr.dts, with references like &portb |
| Interpreted | Value as Zephyr understands it through the binding. Numbers are shown in decimal and hex, references are resolved, and GPIO flags are decoded, e.g. 0x11 = ACTIVE_LOW | PULL_UP |
| Origin | Category of the file that set this value last (see legend). Binding default rows (yellow) list values that no .dts file contains. They still end up in the generated C macros. |
| File:line | That exact location. Click to open the source |
| Meaning (binding) | First line of the property description in the binding |
One node can mix several origins. On button_0 in lab0, for example, gpios comes from the
board and status comes from the overlay.
- Depends on / Used by: other nodes that this node references (for example the GPIO port), and nodes that reference this one. Click a name to jump to that node.
- In the app's C code: the lines in
src/that use this node. - Generated C macros: the
#defines that Zephyr created indevicetree_generated.hfor this node: the node identifier (DT_N_S_...), its ordinal, and the property macros (..._P_<property>). This is whatDT_PROP(...)and the other macros expand to in the end.
This tab shows which files include which, in the order the C preprocessor processed them. The tool
rebuilds it from the line markers in zephyr.dts.pre.
- The root is the board
.dts. It pulls in the SoC chain (same5xx20.dtsi→same5x.dtsi→samd5x.dtsi→armv7-m.dtsi→skeleton.dtsi) and the board pinctrl file. - The app overlay comes last. That is why it can change everything above it.
- Badge number: how many entries (nodes and properties) in the final devicetree come from
this file. A file without a badge contributes no values directly; it only includes other files
or provides
#defineconstants. - Show internal helper headers: also shows Zephyr's internal macro headers
(
sys/util_*.h). They are hidden by default.
- File path (blue): click to open the whole file.
- Include chain: how the board file reached this file.
- Contributes N entries: one card per node. The node name jumps to the node in the Nodes tab.
Each
property :linelink opens the source at that line.
A header contributes no nodes. It provides constants that the .dts files use, such as
GPIO_ACTIVE_LOW, GPIO_PULL_UP or the pinctrl names PA22C_SERCOM3_PAD0. The table lists each
constant with its value. The Line column opens the header at the definition.
The source viewer opens when you click any blue file:line link.
- Yellow line: the line that was clicked.
- Green lines: the other lines in this file that belong to the same node.
- Open in VS Code: opens the file at the same line in VS Code. This button only appears if
the
codecommand is in your PATH. - × or Esc closes the viewer.
One row per pin of the chip's package, in pin-number order. If the device has several packages, the Package list picks one.
- Summary line, e.g.
128 pins, 23 used, 77 free, 1 with more than one user. - Show: all pins, used pins only, or free pins only.
- Filter: any text in the pin, pad, node or function columns.
| Column | Meaning |
|---|---|
| Pin, Pad | Pin number and pad name, e.g. PC22 |
| State | free (nothing uses it) · used (one node) · conflict (more than one node uses it) · other (power, ground, NC, reset) |
| Used by (node) | Node paths that use the pin. Click a path to open the node in the Nodes tab |
| Role / function | What the node does with the pin, e.g. pinctrl: B DAC.VOUT0 (default) |
| All possible functions | Every function the pin can take on this chip; the default is marked |
The same pin states drawn on the package outline. Pin 1 is at the top left of a QFP and ball A1 at the top left of a BGA; the numbering runs counter-clockwise.
- Package list: switch to another package of the device.
- Legend with counts: free (grey) · used (green) · conflict (orange) · power / other (dark blue) · draft (purple, not built yet).
- Click a pin: the panel below shows its users and function, with links to the nodes.
The connectors of the board as they sit on it. Each connector is drawn with its pins in two rows (odd pins above, even pins below, pin 1 at the top left). The example is the SAME54 Xplained Pro of lab1 (apps/lab1/build_same54_xpro): EXT1 uses SPI (MOSI, MISO, SCK) and the DAC, EXT2 and EXT3 use the I2C instance on PD08/PD09.
- Connector heading: name, designator (e.g.
J401), connector type, and the status of the entry,reviewedorsuggested. - Pin: number, the role printed above or below it (e.g.
MOSI,SDA), and the SoC pad behind it (e.g.PB27). - Colour: green = used by the devicetree, orange = conflict (more than one user), purple = draft (not built yet), grey = free, dark blue = supply / ID, and a dark tile = no SoC pad traced. The counts above the connectors give the totals (here
used: 8). - Click a pin: below the board it shows the pin, its role and pad, the state, the nodes that use it (click a node to open it in the Nodes tab) and the possible functions of the pad. A pin that no node uses says so.
The view needs a board description, dt_explorer/boards/<board>.yaml, for the build's board. Without one the tab says No board description for '<board>'. The same tab works for the other boards that have a description, e.g. the PIC32CX SG41 Curiosity Ultra build below.
The dialog opens from the Configure… button of a disabled node (section 5.1). The example turns SERCOM1 into an I2C controller: SDA on PC22, SCL on PC23.
- Class (UART, I2C, SPI): the pads and properties below change with it.
- Pads: one pad per role. Pads that other nodes already use are still offered; the check (section 11) reports the conflict. Derived properties lists the pads that are still to assign (orange).
- Properties: the class properties, e.g.
clock-frequency. - Use:
chosenandaliasesentries for the node, if needed. - Save adds the node to the draft and opens the Draft tab. Cancel closes the dialog.
The draft is kept for this build. Saving the dialog does not build anything; use the check in the Draft tab, and copy the snippet into your app overlay.
On an enabled node (status = "okay"), Edit pins… opens the same dialog for changing the pads and
properties of that node. The example is SERCOM2 of lab1 as UART: TX PB25, RX PB24, filled in from the
node as it is built.
- The change goes to the draft as an edit of the node. Its pinctrl group gets the name
<label>_<class>_edit(for examplesercom2_uart_edit), so the board's own group is not changed. - Cancel closes the dialog and changes nothing.
When no recipe covers the compatible of a disabled node but the build has its binding, Configure…
still appears and opens a form generated from the binding. The example is &tc0
(atmel,sam0-tc32).
- Pads (optional): one pad list per signal role of the node's peripheral, taken from the device
data. It appears only when the binding takes
pinctrl-0and the device data knows the peripheral. A pad that carries several signals of the peripheral is reported under To fix because the overlay cannot choose between them without a recipe. - Properties: one field per scalar property of the binding chain (
int,string,boolean), with its enum as a list, the description and[required|optional, type, default]below it. A required property is marked*. A value that the SoC or board file already sets is filled in; only changes appear in the overlay. - To fix at the top lists every problem (required field empty, text that is not a number, value not in the enum, a boolean the board sets that cannot be switched off by an overlay). While it is not empty, Save is off, and the field is outlined in red.
- A grey line names the properties the form cannot edit (clocks, interrupts, ...). They stay as the board file has them.
- Use and Save work as in section 10. The draft node has the class
generic; its pinctrl group is named<label>_generic_default.
The second picture is &adc1 with the required prescaler emptied.
A node that is already enabled and has no recipe (for example wdog) has no button in the node view.
Other compatible (generic form). A node that has a recipe can have a second family without one. &tc2 is the example: the recipe dialog offers the class PWM, and above the pads a list Other compatible (generic form) (cfg-alt-compat) with the next button Use generic form (cfg-open-generic-alt). The list names the compatibles whose binding exists and has something to configure: the node's own old one (atmel,sam0-tc32) or a g1 binding that includes the node's base (microchip,tc-g1-counter). The button opens the generic form above for that compatible, so a TC can be a counter instead of a PWM.
When the class is I2C or SPI, the dialog has a Child devices list below Properties and the button
Add child device… (cfg-child-add). It opens a second dialog (child-dialog). The example is a
bosch,bmi160 on &sercom5 as SPI.
- Device (compatible): the bindings of the workspace whose
on-busis the bus of the node (type to search). A bus controller is not offered. One compatible can have a binding per bus (bosch,bme280-i2c.yaml,bosch,bme280-spi.yaml); the one for this bus is used. - Address (
child-address): for I2C the 7-bit address in hex (76or0x76), for SPI the chip-select index, preset to the next free one. It becomesregand the unit address (bme280@76). If another device of this bus already has the address, an orange line names it; the dialog still saves, Check then reports the error. Two devices of the same compatible get distinct node names (bme280,bme280-2), so the compiler does not merge them. - Chip select (SPI only): a pad list (
child-cs-pad) and the level (active low is preset). It writes entry index of the controller'scs-gpios. The index must follow the entries that exist. - GPIO properties of the binding (
int-gpios,reset-gpios,supply-gpios, ...) are pad lists with a level each (child-gpio-<property>); a required one must be set. Pads that something else uses are listed with their users but not hidden: the overlay then reports the clash. - Properties: the scalar properties of the binding (including
spi-max-frequencyand the other bus properties), as in the generic form. To fix (child-problems) lists what is missing; Add (child-save) stays off until it is empty.
The list in the main dialog shows each child with a delete button (cfg-child-remove-<name>-<unit>). A child
that the board file already has (for example eeprom@5e on &sercom7) is shown as from the board and
cannot be removed. Save puts the children into the draft node; they render inside the node:
An I2C device looks the same with the address in hex instead of the chip select. Saved, the draft node renders like this:
&sercom5 {
...
cs-gpios = <&portb 14 1>;
bmi160_0: bmi160@0 {
compatible = "bosch,bmi160";
reg = <0x0>;
spi-max-frequency = <1000000>;
int-gpios = <&portb 15 0>;
};
};
The Draft tab suggests the driver symbols of the children (for example CONFIG_BME280=y). Check adds these
rules: bus_address_duplicate (two I2C devices at one address or two SPI devices at one chip select, error),
spi_cs_missing (chip select beyond the cs-gpios list, error) and pin_conflict for a GPIO pad that another
node, or the same node's pinctrl, also uses (error).
The header button Board nodes… edits the nodes that belong to the board, not to a peripheral:
- Entries: pick LED (
gpio-leds), Button (gpio-keys) or PWM LED (pwm-leds; offered only when the build has a PWM controller). Give it a Name (lower case letters, digits and_; it becomes the node name and the label). An LED or button takes a GPIO pad (every pad shows who already uses it), active low, and for a button pull (a button starts as pull-up, active low) and an optionalzephyr,code. A PWM LED takes the PWM controller (a controller of the build or a PWM node that the same draft enables, e.g. a newtcc3), the channel and the period in ms. The optional label text becomes thelabelproperty. Add refuses a bad name, a label the build already has, and a pad or PWM channel used twice in the dialog. - Aliases and Chosen: a name and the node it points at (a node of the build or one of the new board
nodes). The chosen name offers the common
zephyr,...names and takes any other. Both are written as path strings (led1 = "/user_leds/led_user";). Removing an entry removes the aliases that pointed at it. zephyr,user: a property name (io-channels,pwms,gpios, or any name), a controller (a node of the build or of the draft, e.g. the ADC you configured) and its cells (6); Add once per entry. The overlay writeszephyr,user { io-channels = <&adc1 6>, <&adc1 7>; };, which samples such asadc_dtread withDT_PATH(zephyr_user); no hand edit is needed. - The line under the title lists the problems. A pad that something else already uses is a
warning here; the Check in the Draft tab reports it as the error
pin_conflict. An alias or chosen name that the build already has is a warning too (the overlay replaces it). - Save puts one draft node in the Draft tab (row board nodes, pencil = edit again, trash = remove all).
An empty dialog removes it. The overlay writes
&{/} { user_leds { ... }; user_keys { ... }; user_pwm_leds { ... }; aliases { ... }; chosen { ... }; zephyr,user { ... }; };; the board's ownleds,buttonsandaliasesstay as they are.
Limits: the pad of an LED or button needs the GPIO controllers to map onto the device's pads (the same mapping the Pins tab uses); a PWM LED does not touch pins (the controller's own pinctrl does). Port to board… carries them over as part of the app overlay (section 1.3, section 5b).
The same Configure… dialog (section 10) covers more peripherals than the SERCOM. The class name is the only button; the pads and properties change with the peripheral. Nothing here is saved until Save.
PWM (TCC and TC). &tcc1 of lab1 is the example. One pad list per output WO0 to WO7 (a TC has WO0 and WO1), taken from the device data; pick the pads you need. The PWM controller's #pwm-cells is not a field: it is 2 on the old driver and 3 on the g1 driver, set by the recipe. The recipe suggests a prescaler. A consumer such as pwm-leds needs the channel that belongs to its pad; Board nodes… (section 10.4) takes controller and channel.
ADC. &adc1 is the example. One pad list per input AIN0 to AIN15. Every pad that you choose becomes a child channel@N with reg = <N> and zephyr,input-positive = <N> (the AIN number); prescaler is required on the g1 driver and the recipe suggests the smallest value that keeps the ADC clock at 16 MHz or less. The check warns when an ADC input pad is also used digitally (analog_digital_pad) and reports two users of one channel (adc_channel_duplicate, pwm_channel_duplicate).
GMAC (Ethernet). The SAME54 Xplained Pro enables the GMAC itself, so the example is Edit pins… (on a disabled GMAC it is Configure…). The dialog has two tabs, Ethernet (the MAC) and MDIO. The eight RMII pads each have exactly one candidate pad, so they are filled in and need no hand picking (the optional MII pads stay empty). For MDC/MDIO the dialog offers the IOSET pairs and prefers the pair the node has now if nothing else uses it. Saving gives two draft nodes: the MAC with phy-handle, and the MDIO bus with the PHY ethernet-phy@0 as its child. The Kconfig lines are the Ethernet driver symbols; TCP/IP and TLS stay in menuconfig.
The pending changes of the current build. The number on the tab counts the draft nodes.
- Draft nodes: one line per node. The pencil opens it in the Configure dialog; the bin removes it from the draft.
- Overlay snippet: the devicetree overlay for the draft. Copy puts it on the clipboard,
Download saves it as
draft.overlay. Paste it into the app overlay yourself. - Suggested prj.conf lines: the Kconfig options the class needs, e.g.
CONFIG_I2C=yandCONFIG_I2C_SAM0=y. - Check: Check again runs the validation on the overlay. It takes a few seconds; a spinner shows while it runs. The line above the results counts the errors, warnings and infos. Each finding has a badge and a message that names the nodes and pins involved.
| Badge | Meaning |
|---|---|
| error (red) | The overlay would not work, e.g. two nodes use the same pin or a required property is missing |
| warning (orange) | The overlay works but needs attention, e.g. a pinmux IOSET mix |
| info (grey) | For information, e.g. a pin conflict that already exists in the build |
The Check above is a fast check in Python. The two buttons below use Zephyr itself.
Verify with west runs west build --cmake-only of the build's board into a scratch folder in
%TEMP%, with the draft overlay and the Kconfig lines (about 15–45 s). The build folder is not touched.
A green line says that Zephyr accepts the draft; a red line says that it rejects it, and the errors are
shown under the log.
Write to app copies the draft into the app folder as dt_explorer.overlay and dt_explorer.conf.
A confirmation first names the folder and lists the files that would be overwritten. Zephyr reads the two
files only if the app's CMakeLists.txt or your west command refers to them, for example with
-DEXTRA_DTC_OVERLAY_FILE=dt_explorer.overlay -DEXTRA_CONF_FILE=dt_explorer.conf.
Apply to app… (below Write to app, id=draft-apply) writes the draft as boards/<board>.overlay and
boards/<board>.conf of the app, for the board this build was configured for. Zephyr picks these files up by itself, so
a plain west build -b <board> <app> uses the draft without extra options. This is the difference to Write to app,
which writes dt_explorer.overlay/.conf that you must hand to west yourself. The file name follows Zephyr's rule
(the short name <board> for a single-SoC board, else <board>_<soc>…), the same as in the port dialog.
The button first shows a plan, per file NEW, EXISTS, IDENTICAL or a note that the .conf is empty. A missing file
is created. If the app already has the file (apps usually have their own boards/<board>.overlay), the default is
append: the app's content stays and the draft is added as a block /* dt_explorer draft begin <date> */ … /* dt_explorer end */.
The diff of the file after the append is shown first (id=draft-apply-diff-<kind>); applying again replaces only that block
(your edits outside it survive), and a block that is already up to date is skipped. In the .conf the lines are appended once:
lines the file already has are left out, with a note. Replace is the explicit alternative: the fold-out Replace instead
(id=draft-apply-diff-replace-<kind>) shows the diff existing file -> draft, and the box Replace the existing … instead of
appending (id=draft-apply-replace-<kind>) makes Write files overwrite the file, dropping everything the draft does not
contain. Write files (id=draft-apply-confirm) then writes; the result lines end with the west
command for the next build, west build -p always -b <board> <app> (-p always: a pristine build makes Zephyr read the new board files). src/ and other files are never touched.
The second picture is the second Apply on the same app with a changed value (UART speed 57600 instead of 115200): the diff shows that only the content of the block changes (the old &sercom7 lines out, the new in; everything outside the block stays), and the result lines end with the west build -p always hint. The shots are from copies of an app in the temp folder, because the button writes into the app.
The Draft tab opens by itself after Save. Use tab=draft in a link to open it directly.
Generate code… (below Apply to app, id=draft-codegen) turns the devices of the draft into a small C module in the
app folder, so that the application calls dtx_bme280_76_read(&d) instead of writing DT_NODELABEL and driver calls by
hand. Devices are the child nodes with a node label: a sensor on an I2C or SPI bus, an LED below gpio-leds, any other
child with a compatible. A child without a label is left out with a note (DT_NODELABEL needs one). The button shows a
plan first and writes nothing (id=codegen-box):
- Devices (
id=codegen-device-<node>). A sensor has a channel list (id=codegen-chan-<node>), pre-filled from theSENSOR_CHAN_*that the driver'schannel_getnames (found through the driver index; a heuristic). Add or remove channels; each channel becomes onestruct sensor_valuefield instruct dtx_<node>_data. - Files (
id=codegen-file-<name>):CMakeLists.txt,include/dtx_devices.h(umbrella,dtx_devices_init()callsdevice_is_ready()for all), and per deviceinclude/dtx_<node>.handsrc/dtx_<node>.c. Sensor:dtx_<node>_read()(sensor_sample_fetchandsensor_channel_getper channel). LED:dtx_<node>_set(bool). Other:dtx_<node>_get(). State per file:NEW,UNCHANGED,REGENERATE(the tool's own file, now different: it is rewritten, the diff is shown),EDITED(see below). Write files (id=codegen-write) writes theNEWandREGENERATEfiles. - Header and checksum. Every file starts with
generated by dt_explorer (dtx_devices), checksum <hex>; the checksum covers the text below that line. If you edit a generated file, the checksum no longer matches: the file is shown asEDITED, is not overwritten, and the diff to what would be generated is shown (id=codegen-diff-<name>). A file without the header is treated the same way. A generated file whose device left the draft is listed asSTALEand not deleted.
-
CMakeLists.txt of the app. The module needs one line,
add_subdirectory(dtx_devices). The tab shows the diff ofCMakeLists.txt(id=codegen-cmake-diff); Add the line… (id=codegen-cmake-add) opens a confirmation dialog with the same diff (id=codegen-cmake-dialog,codegen-cmake-ok/codegen-cmake-cancel). Only after OK is the line appended, and never twice. This is the only change outsidedtx_devices/;src/is never touched.
-
prj.conf. The Kconfig lines (the driver symbols of the draft plus
CONFIG_SENSOR=y/CONFIG_GPIO=ywhen the module needs them) are appended to the app'sprj.confas a marked block# dt_explorer codegen begin <date>…# dt_explorer end(id=codegen-conf-diff, buttonid=codegen-conf-confirm); lines the file already has are left out, applying again replaces only that block.
After Write files the tab lists the written files:
The application then includes dtx_devices.h from its own src/main.c (you write that part). The overlay itself still
goes through Apply to app….
Shows the log of the tool (see README, section Log) while it runs; the lines are the same as in the log file
%USERPROFILE%\.dt_explorer\logs\dtx_<date>.log, which is named above the lines. Direct link: tab=log.
- Level hides lines below DEBUG, INFO or WARNING. The buffer holds the last 5000 lines.
- DEBUG switches the detail level of the tool itself: with it on, the tool also records a line for every node
with the reason for its traffic-light dot (status, compatible, binding found, driver files and whether they
were compiled, linked or not), for example
state wdog (/soc/watchdog@40002000): nobuild/orange [status="okay"; compatible=atmel,sam0-watchdog; binding=yes; driver files: wdt_sam0.c (compiled: none); linked=True] - A driver exists but is disabled in Kconfig .... Start with--debugto have it on from the first line. - Copy puts the context header (tool commit, Python, NiceGUI, Zephyr version, workspace, build, board, SoC, ATDF file, plugins) and the visible lines on the clipboard.
- The arrow button scrolls to the newest line.
The log contains local paths including the user name; read it before you send it to someone.
Shows what git sees in the app folder of the open build (APPLICATION_SOURCE_DIR), for example after Apply to app….
It is read only: the tool never commits, reverts or checks out. Direct link: tab=changes.
- The list is git's own:
git status --porcelainlimited to the app folder (modifiedM, addedA, deletedD, renamedR, untracked?). Files matched by.gitignoredo not appear. - A purple dtx badge marks files the tool writes:
boards/<board>.overlayand.confof the build's board,dt_explorer.overlay/dt_explorer.conf, anything belowdtx_devices/or.dt_explorer/, and any file that contains a/* dt_explorer ... begin ... */.../* dt_explorer end */block (also aCMakeLists.txtthat namesdtx_devices). The badge does not change the list. - Click a file name: the source viewer opens with
git diffagainst HEAD. Added lines are green, removed lines red, hunk headers blue. An untracked file is shown whole as added; a binary file is only named. - The number on the tab is the number of changed files. It appears after the first read.
- git is read when the tab opens and when you press Refresh, never in between. The line under the button shows the
time of the last read, the branch and the short HEAD hash (
HEAD (no commits yet)for a new repository). - If the app folder is not inside a git repository, or git is not on PATH, the tab shows a message instead of the list; everything else works as before.
The list after Apply to app and Generate code on an app that is in a git repository (a copy under the temp folder): the purple dtx badges mark the files the tool wrote, CMakeLists.txt and prj.conf are modified, src/main.c was edited by hand and has no badge.
A click on a file shows its diff against HEAD in the source viewer:
Every view can be opened directly, for example from slides or lab instructions:
| Parameter | Example | Effect |
|---|---|---|
build |
build=apps/lab1/build_same54_xpro |
Load this build (path relative to the workspace) |
node |
node=/my_led@41008000 |
Select this node |
macros=1 |
Expand the generated-macro section | |
bprops=1 |
Expand the table of all binding properties | |
tab |
tab=pins |
Start on a tab: include, pins, package, board, draft, log or changes (default: Nodes) |
file |
file=zephyr\dts\arm\atmel\same5x.dtsi |
Select this file in the include tree |
src, line |
src=apps\lab3\boards\same54_xpro.overlay&line=20 |
Open the source viewer |
URL-encode the values: / becomes %2F, @ becomes %40 and \ becomes %5C.
For example:
http://localhost:8765/?build=apps/lab1/build_same54_xpro&node=%2Fmy_led%4041008000






























































