Connecting Hardware with dutctl

Connect a board to FirmwareCI so that jobs can power it on and off, flash firmware, and read its serial console. FirmwareCI does this through dutctl. Its service, the dutagent, runs on a computer next to the board and receives commands from FirmwareCI over the network.

┌────────────┐          ┌───────────────┐          ┌─────────┐
│ FirmwareCI │ network  │   dutagent    │  wired   │   DUT   │
│            │─────────▶│               │─────────▶│         │
└────────────┘          └───────────────┘          └─────────┘

Prerequisites

  • A dutagent that controls the board. See the dutctl documentation for installation and configuration.
  • A network route from FirmwareCI to the agent (see Reachability).
  • The fwci CLI and a repository with .firmwareci initialized (see Repository Setup).

Step-by-Step Setup

1. Collect the Agent Details

FirmwareCI needs three things from the agent:

WhatExampleUsed as
Address with portlab-pi.local:2024Agent attribute
Device nameboard-1Device attribute
Command namespower, flashcommand in the steps

The agent’s configuration defines the command names and their arguments. Use the dutctl CLI to list the devices and commands of the agent, and try each command there before you use it in FirmwareCI. A command that works with the CLI works the same way in a FirmwareCI step.

2. Add the DUT to FirmwareCI

fwci init created an example DUT in .firmwareci/duts/dut-<name>/ that uses reservation-system: mock and controls no hardware. Replace it, or add a new directory for the board, for example .firmwareci/duts/board-1/dut.yaml:

name: Board-1
label: board
reservation-system: dutctl
attributes:
  Agent: "lab-pi.local:2024"
  Device: "board-1"
  Host: "board-1.lab"

Workflows find the board by its label: a workflow with runs-on: board runs on every DUT with label: board. To run jobs on several identical boards, add one DUT per board with the same label. With reservation-system: dutctl, FirmwareCI locks the board on the agent while a job runs, so no other job uses it at the same time.

See DUT Configuration for all fields.

3. Prepare and Clean Up the Board

pre.yaml runs before every test on this DUT, and post.yaml runs after it, even if the test fails. The examples use the commands power and flash. Use the names and arguments your agent defines.

.firmwareci/duts/board-1/pre.yaml:

pre-stage:
  - cmd: dutctl
    name: Power off
    parameters:
      agent: "[[attributes.Agent]]"
      device: "[[attributes.Device]]"
      command: power
      args: ["off"]

  - cmd: dutctl
    name: Flash firmware
    parameters:
      agent: "[[attributes.Agent]]"
      device: "[[attributes.Device]]"
      command: flash
      args: [write, "[[input.Binary]]"]
    options:
      timeout: 10m

  - cmd: dutctl
    name: Power on
    parameters:
      agent: "[[attributes.Agent]]"
      device: "[[attributes.Device]]"
      command: power
      args: ["on"]
    options:
      timeout: 2m

.firmwareci/duts/board-1/post.yaml:

post-stage:
  - cmd: dutctl
    name: Power off
    parameters:
      agent: "[[attributes.Agent]]"
      device: "[[attributes.Device]]"
      command: power
      args: ["off"]

[[attributes.Agent]] and [[attributes.Device]] take their values from dut.yaml. [[input.Binary]] is the firmware file you pass when you trigger the job (see Templating and Variables).

To wait until the board has booted, add a serial step to the pre-stage (see Serial Console). See the dutctl step reference for all options.

4. Validate and Push

fwci validate
git add .firmwareci
git commit -m "Add board-1"
git push

Then point a workflow at the board with runs-on: board. The example workflow from fwci init runs on the label of the example DUT. Change its runs-on, or write a new workflow and test as described in Writing a Simple Boot Test.

5. Watch a Job

While a job runs, the device list in FirmwareCI shows the device as Busy, and FirmwareCI holds the lock on the agent as <job-id>@firmwareci.

Using the Board Manually

While a job runs, the board belongs to the job, and manual dutctl commands are refused. To work on the board yourself, disable the device in FirmwareCI. Jobs skip it, and everybody sees why. A manual dutctl lock also keeps jobs away (see Manual Locks).

Common Issues

ProblemSolution
fwci validate reports reservation-system is requiredAdd reservation-system: dutctl to dut.yaml.
Device is Unavailable with agent unreachableStart the agent, and make sure FirmwareCI can reach the host and port in Agent. Reaching it from your own machine is not enough.
Device is Unavailable, but the agent is runningSet Device to the device name the agent lists.
Job stays Queued while the device shows IdleRelease the manual dutctl lock on the device (see Manual Locks).

See also When the Step Fails.

See Also