Configurations

Real projects rarely have a single build. You need a debug build and a release build; you switch between simulator vendors; you target different platforms; you run a quick smoke test or a full regression. Configurations let you capture these variants in one package – selecting them at run time with -c – instead of duplicating the flow or hand-editing parameters.

A configuration is a named overlay on the base package: it can set parameter values, override tasks, inject new tasks into existing ones, extend tasks, and add imports, fragments, types, or tasks – all without touching the base definition.

dfm run build              # default
dfm run build -c debug     # the 'debug' variant
dfm run build -c release   # the 'release' variant

Defining a Configuration

Configurations are declared in the package’s configs field. Each entry adjusts the base package for one variant:

package:
  name: my_project

  with:
    debug:
      type: bool
      value: false

  tasks:
  - name: build
    uses: std.Message
    with:
      msg: "Building in default mode"

  configs:
  - name: debug
    with:
      debug:
        value: true
    tasks:
    - name: build
      override: build
      with:
        msg: "Building in debug mode"

  - name: release
    with:
      debug:
        value: false
    overrides:
    - override: hdlsim
      with: hdlsim.xcelium

A configuration can specify:

  • with – parameter values for this configuration.

  • uses – a base configuration to inherit from (see Inheriting Configurations).

  • overrides – package/parameter overrides.

  • extensions – task extensions (see Modifying Tasks with Extensions).

  • tasks – additional or overriding tasks (including tasks that inject data via feeds – see Injecting Options with feeds).

  • types – additional or overriding types.

  • imports / fragments – additional imports or fragments for this configuration.

Selecting a Configuration

Choose a configuration on the command line with -c / --config:

dfm run build -c debug

When a configuration is selected, the merged definition is built in this order:

  1. The base package is loaded.

  2. Configuration parameters override package parameters.

  3. Configuration overrides are applied.

  4. Configuration tasks, types, imports, and fragments are merged.

  5. The task graph is built from the merged definition.

Configuration values participate in the normal parameter resolution order, and command-line -D overrides still take precedence over the selected configuration.

Inheriting Configurations

A configuration can build on another with uses. This lets you layer variants – for example a debug configuration that starts from a shared instrumented base:

configs:
- name: instrumented
  with:
    trace:
      type: bool
      value: true

- name: debug
  uses: instrumented
  with:
    opt_level:
      value: 0

debug inherits everything from instrumented (here, trace: true) and adds its own settings.

Injecting Options with feeds

A configuration often needs to add arguments, options, or data files to tasks that are defined elsewhere (in the base package or an imported package). The cleanest way to do this is the feeds field: a config adds a new task whose output is injected into an existing task, without modifying that task’s definition.

feeds is the inverse of needs – where needs names a task’s dependencies, feeds declares “my output is an input to that task.” Expressed from a config, it connects a config-specific task into the base flow:

package:
  name: my_project

  tasks:
  - root: build
    uses: sim.SimImage
    needs: [rtl, tb]
    with:
      top: [tb_top]

  - root: run
    uses: sim.SimRun
    needs: [build]

  configs:
  - name: debug
    tasks:
      - name: debug_compile_args
        uses: hdlsim.SimCompileArgs
        with:
          args: ["-debug_access+all"]
        feeds: [my_project.build]

      - name: debug_run_args
        uses: hdlsim.SimRunArgs
        with:
          args: ["-gui"]
        feeds: [my_project.run]

With -c debug, the build task additionally receives the -debug_access+all compile arguments and run receives -gui – yet neither build nor run was edited.

Key points:

  • feeds injects data “from the side” into a task without changing its definition.

  • The feed target is a fully-qualified task name (package_name.task_name).

  • It is equivalent to adding the feeding task to the target’s needs list, but expressed from the producer’s perspective.

  • This keeps the base flow clean: the configuration only adds tasks that connect to existing ones, which is easy to read and maintain.

Use feeds (inject from the side) when you want to add inputs/options to a task; use overrides or extensions when you need to change a task’s own parameters or base.

Modifying Tasks with Extensions

Where feeds adds a new upstream input, an extension modifies an existing task in place – adding parameters, dependencies, or changing its base – without replacing the whole task. Extensions are typically defined inside a configuration and are especially useful for augmenting tasks from imported packages.

package:
  name: my_project

  imports:
  - hdlsim.vlt

  configs:
  - name: coverage
    extensions:
    - task: hdlsim.vlt.SimImage
      with:
        coverage:
          type: bool
          value: true
      needs:
      - coverage_setup

The extension above adds a coverage parameter and an extra dependency to the imported hdlsim.vlt.SimImage task.

Extensions can:

  • Add parameters – introduce new options on the task.

  • Add dependencies – include additional needs.

  • Change the base – specify a different uses base task.

Extensions can themselves inherit from other extensions with uses, letting you build up instrumentation incrementally:

configs:
- name: base_instrumentation
  extensions:
  - task: my_tool.Compile
    with:
      verbose:
        type: bool
        value: false

- name: debug_instrumentation
  uses: base_instrumentation
  extensions:
  - task: my_tool.Compile
    uses: base_instrumentation.my_tool.Compile
    with:
      verbose:
        value: true
      trace:
        type: bool
        value: true

Per-Configuration Imports

An import can itself select a configuration of the imported package via the config key, so a variant can propagate down the dependency tree:

imports:
- name: hdlsim.vlt
  config: gui

What Configurations Enable

  • Build variants – debug, release, profiling builds.

  • Tool selection – switch between simulator/tool vendors.

  • Target platforms – customize for different deployment targets.

  • Test modes – normal vs. regression vs. continuous integration.

See Also