Guide to Writing UDDF Drivers#

UDDF Driver Model#

The UDDF driver model is centered around two concepts: driver interfaces and driver objects.

In UDDF, a driver interface is a C++ class that defines only pure virtual methods—it contains no data or code. Interfaces provide all communication between a driver and its environment. Driver interfaces come in two flavors:

  • DDI: interfaces implemented by the driver.

  • CDI: interfaces provided to the driver (discussed later).

Driver interfaces lack ownership semantics; they act only as views into a given driver object instance.

A driver object represents a concrete driver instance. UDDF finds, probes, and loads these objects as requested by the driver consumer. In UDDF, driver objects implement a singular method to provide driver interfaces. Beyond that, their internal composition is otherwise entirely opaque to UDDF.

In practice, consumers of your driver will request various interfaces, such as an interface to update the exposure of a sensor. UDDF forwards this request to your driver. If your driver supports this capability, it returns the corresponding sensor control interface.

Block diagram of the UDDF driver model

UDDF Driver Model Overview#

Device Driver Interface (DDI)#

Driver Interfaces#

DDIs are implemented by drivers and called by the UDDF client. Methods in these interfaces are often called driver entrypoints.

Every interface in DDI has the following:

  • A name (beginning with “I”).

  • A GUID (a 128-bit globally unique identifier).

  • A static member named id containing the GUID, which identifies the interface.

  • Inheritance from the empty base class IInterface.

For example, the interface for reading and writing sensor devices on an I2C bus is named IReadWriteI2C and is defined in a file named IReadWriteI2C.hpp, and its GUID is UUID(0xacc5ff36, 0x22a0, 0x4d8a, 0x8271, 0xab, 0x97, 0x5b, 0xa8, 0x84, 0xeb) (defined in that header file). The interface has two methods: ReadI2C() and WriteI2C().

Note

Drivers don’t directly use IReadWriteI2C to program hardware. It’s an interface provided by a driver, not an interface used by a driver.

The declaration for an example interface IReadWriteI2C for reading and writing sensor devices on an I2C bus resembles the following code:

/** The unique identifier for the IReadWriteI2C interface type. */
inline UUID IREADWRITEI2C_INTERFACE_ID = UUID(0xacc5ff36, 0x22a0, 0x4d8a, 0x8271, 0xab, 0x97, 0x5b, 0xa8, 0x84, 0xeb);

class IReadWriteI2C: public IInterface {   // Note the inheritance from the IInterface class!

public:

    static constexpr UUID id { IREADWRITEI2C_INTERFACE_ID };  // The GUID for this interface

    /** Return values from read and write operations. */
    enum I2CResult {
        RWI2C_SUCCESS,              ///< Success
        RWI2C_ERROR_NACK,           ///< NACK received
        RWI2C_ERROR_BUS_TIMEOUT,    ///< Bus timeout
        RWI2C_ERROR_ARBLOST,        ///< Arbitration lost
        RWI2C_OUT_OF_RANGE,         ///< Address, offset, or length is out of range
        RWI2C_ERROR_UNKNOWN,        ///< Unknown error
    };

    /**
     * Read from the I2C bus.
     *
     * @param[in]  sensorIndex  The index of the sensor to read from.
     * @param[in]  address      The address of the device to read from.
     * @param[in]  offset       The starting offset to read from.
     * @param[out] data         The data read from the I2C bus.
     * @param[in]  length       The number of bytes to read.
     */
    virtual I2CResult ReadI2C(uint8_t sensorIndex, uint16_t address, uint16_t offset,
                              uint8_t* data, uint16_t length) = 0;

    /**
     * Write to the I2C bus.
     *
     * @param[in]  sensorIndex  The index of the sensor to write to.
     * @param[in]  address      The address of the device to write to.
     * @param[in]  offset       The starting offset to write to.
     * @param[in]  data         The data to write to the I2C bus.
     * @param[in]  length       The number of bytes to write.
     */
    virtual I2CResult WriteI2C(uint8_t sensorIndex, uint16_t address, uint16_t offset,
                               uint8_t const* data, uint16_t length) = 0;
};

For a driver to support the IReadWriteI2C interface, it must implement the ReadI2C() and WriteI2C() methods.

Driver Objects#

All UDDF drivers must inherit from and implement IDriver and the singular GetInterface() method. All driver interfaces supported by your driver will be retrieved via this method. For example, driver consumers can request the I2C read/write interface as follows:

IDriver* driver = ...;

UUID READ_WRITE_I2C_GUID = IReadWriteI2C::id;  // This is the GUID for the IReadWriteI2C interface
IReadWriteI2C* rwI2C = static_cast<IReadWriteI2C*>(driver->GetInterface(READ_WRITE_I2C_GUID));

if (rwI2C == nullptr) {
    std::cerr << "Sorry, I2C read/write functionality is unavailable" << std::endl;
}

GetInterface() returns a pointer to an interface if the requested interface is supported, or nullptr otherwise.

Putting it all together, a driver that implements IReadWriteI2C (and no other interfaces) might resemble the following code:

#include "uddf/ddi/interfaces/IReadWriteI2C.hpp"

class MyDriver: public IDriver, public IReadWriteI2C
{
public:

    /*-- IDriver methods --*/

    IInterface* GetInterface(const UUID& uuid) noexcept override {
        if (uuid == IReadWriteI2C::id) {
            return static_cast<IReadWriteI2C*>(this);
        } else {
            return nullptr;   // interface not supported
        }
    }

    /*-- IReadWriteI2C methods --*/

    I2CResult ReadI2C(uint8_t sensorIndex, uint16_t address, uint16_t offset,
                     uint8_t* data, uint16_t length) override {
        // implement I2C read
    }

    I2CResult WriteI2C(uint8_t sensorIndex, uint16_t address, uint16_t offset,
                      uint8_t const* data, uint16_t length) override {
        // implement I2C write
    }
};

Alternative Implementation#

Although the preceding example shows MyDriver implementing required driver interface and driver object methods, this need not always be the case. The internal structure of any driver object implementation is entirely opaque to UDDF. Consider the alternative driver structure, which is an equally valid UDDF driver.

#include "uddf/ddi/interfaces/IReadWriteI2C.hpp"

/* Defined by your driver */
class MyI2CReadWriteBackend : public IReadWriteI2C
{
    /* Elided */
};

class MyDriver: public IDriver
{
public:

    /*-- IDriver methods --*/

    IInterface* GetInterface(const UUID& uuid) noexcept override {
        if (uuid == IReadWriteI2C::id) {
            return static_cast<IReadWriteI2C*>(m_i2cReadWriteBackend);
        } else {
            return nullptr;   // interface not supported
        }
    }

private:
   MyI2CReadWriteBackend m_i2cReadWriteBackend;
};

UDDF Driver Model: Camera Driver Interface (CDI)#

UDDF CDI interfaces are provided to the driver so that the driver can do its work. In most DDI interfaces, they are provided to every driver entrypoint.

The most important CDI interface for most drivers is named IHardwareAccess. This interface lets the driver communicate with the device that it’s managing. The interface looks like the following code:

class IHardwareAccess {

public:

    /**
     * Acquire a HSL sequence object for building sequences at runtime.
     */
    virtual HSLDynamicSequence& GetDynamicSequence() = 0;

    /**
     * Submit a dynamic HSL sequence.
     *
     * @param[in] sequence A reference to the HSLDynamicSequence object.
     */
    virtual HSLResult SubmitSequence(HSLDynamicSequence& sequence) = 0;

    /**
     * Submit a pre-defined static HSL sequence.
     *
     * @param[in] sequence  The HSLStaticSequence object containing the data.
     */
    HSLResult SubmitSequence(const HSLStaticSequence& sequence) = 0;

    /**
     * Read bytes from an I2C device.
     *
     * @param[in]  deviceIndex  The index of the I2C device in the driver's device table.
     * @param[in]  startOffset  The starting offset in the I2C device.
     * @param[in]  length       The number of bytes to read.
     * @param[out] buffer       The buffer to store the read bytes.
     */
    virtual bool ReadI2C(size_t deviceIndex, uint16_t startOffset, uint16_t length, uint8_t* buffer) = 0;
};

UDDF drivers communicate with hardware primarily through the Hardware Sequence Language (HSL). In this interface, HSLStaticSequence refers to HSL bytecode that is precompiled and built into the driver. (Details on how to do this appear later in this document.) HSLDynamicSequence is an object that allows the driver to construct new HSL sequences on the fly.

Note that the ReadI2C() method is the only method that doesn’t relate to HSL.

CDI also defines an interface named IDriverServices, which provides basic services such as logging.

Hardware Sequence Language (HSL)#

HSL is a simple language for specifying I2C and GPIO hardware accesses. HSL’s standard source language is named PyHSL and generates HSL bytecode.

Drivers use HSL in two ways:

  • They can create sequences of bytecode on the fly (and send them to hardware). This is known as dynamic HSL.

  • They can send precompiled sequences of bytecode directly to hardware. This is known as static HSL.

Block diagram of the HSL submission model for UDDF

HSL Submission Model for UDDF#

Dynamic HSL#

UDDF drivers submit all I2C and GPIO commands through HSL. In the case of dynamic HSL, this is largely abstracted away from the driver through the II2CBuilder interface. This interface contains several APIs for both simple and compound I2C transactions (such as read-modify-write). As the II2CBuilder interface name suggests, drivers build lists of I2C commands before submitting the entire list to UDDF CDI for fulfillment. UDDF creates one I2C builder interface for each I2C device requested by the driver. Consequently, each I2C builder interface builds I2C commands for only one I2C device. You can use multiple I2C builder interfaces to create I2C command lists targeting multiple devices. All I2C builder interfaces are retrieved by the UDDF-supplied HSLDynamicSequence object.

UDDF drivers can request one of these objects at any time as long as the IHardwareAccess interface is supplied to the calling DDI entrypoint.

After submission, the Sequence object is cleared of any enqueued commands and can safely be used again.

The full workflow is as follows:

  1. The UDDF driver requests a HSLDynamicSequence object from the supplied IHardwareAccess implementation.

  2. The UDDF driver requests a II2CBuilder interface for a specific I2C device and begins enqueuing HSL commands.

  3. When finished, the UDDF driver submits the sequence to the IHardwareAccess implementation. The sequence is turned into HSL bytecode behind the scenes and submitted to hardware. The HSLDynamicSequence object is ready to be used again.

Static HSL#

UDDF drivers are encouraged to use static HSL sequences where possible. Instead of generating commands on-the-fly, HSL is written in the PyHSL source language. When your UDDF driver is compiled, so too is the HSL source file. The resultant bytecode is transformed into a C++ header ready for inclusion into your driver. This workflow is discussed in the HSL Toolchain Support section.

The following code snippet shows an HSLStaticSequence object generated by the aforementioned workflow. UDDF drivers must pass this object to the IHardwareAccess interface that was supplied to the calling DDI entrypoint.

/**
 * @brief  HSL bytecode sequence for init_device
 */
inline constexpr uddf::cdi::HSLStaticSequence<134U> init_device { {{
    0x0c, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
    0x02, 0x00, 0x01, 0x05, 0x00, 0x7a, 0x00, 0x02, 0x01, 0x14, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    0x00, 0x00, 0x00, 0x01, 0x00, 0x1b, 0x1a, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x53, 0x65,
    0x6e, 0x73, 0x6f, 0x72, 0x20, 0x44, 0x72, 0x69, 0x76, 0x65, 0x72, 0x20, 0x49, 0x6e, 0x69, 0x74,
    0x00, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0xc1, 0x04, 0x03, 0x01, 0x02, 0xe3, 0x05, 0x08,
    0x00, 0x20, 0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x15, 0x01, 0x00, 0x17, 0x06, 0x00, 0x30, 0x00,
    0x00, 0x00, 0x02, 0x17, 0x06, 0x00, 0x32, 0x00, 0x02, 0x00, 0x02, 0x15, 0x01, 0x01, 0x17, 0x06,
    0x00, 0x34, 0x00, 0x00, 0x00, 0x01,
}} };

/* Example usage */
auto ret = context.hwAccess->SubmitSequence(init_device);

The full workflow is as follows:

  1. Author HSL in the PyHSL source language.

  2. Use the provided HSL toolkit tools to compile and transform your sequences into C++ headers.

  3. Submit the sequences directly to the IHardwareAccess implementation.

Dynamic and Static HSL#

Good news: Both HSL submission models can be combined! Even in the same function, UDDF drivers may submit a static sequence and then generate a few dynamic HSL commands based on some runtime condition for a follow-up sequence. Any combination is supported - the only invariant that exists is that static HSL sequences are read-only.

Discovery and Enumeration#

UDDF drivers are loaded from driver shared libraries via dynamic linking. Each driver shared library can contain one or more driver object implementations (IDriver). A UDDF driver library is identified by an exported uddf_discover_drivers() symbol. The UDDF framework invokes this symbol to gather information on all of the available drivers within the library.

Driver Information#

Each available driver is described with a small DriverInfo structure. This structure defines the name of the driver and other associated information. Additionally, it defines the type of driver that this descriptor describes. The recognized driver types are defined by UDDF.

/**
 * @brief  Contains descriptive information about a specific driver implementation.
 *
 * This structure holds metadata about a driver, such as its name, vendor, and
 * the unique type ID of the IDriver interface it implements. This information is
 * used by UDDF to identify and select appropriate drivers.
 */
struct DriverInfo {
    std::string name;
    std::string description;
    std::string vendor;
    std::string revision;
    UUID driverTypeId;
};

Driver Enumeration#

The DriverInfo structures are provided by a small proxy object returned through the exported discovery symbol. This object returns the number of contained driver descriptors and a symbol to create each corresponding driver.

/**
 * @brief  Interface for discovering available driver implementations provided by
 *         a driver library.
 *
 * Driver libraries must implement this interface to allow the UDDF framework
 * to query the drivers they contain. A single instance of a class derived from
 * IDriverEnumerator is returned by the library's entry point, @ref uddf_discover_drivers().
 */
class IDriverEnumerator {
public:
    virtual std::string_view GetName() const = 0;
    virtual size_t GetDriverCount() const = 0;
    virtual const DriverInfo* GetDriverInfo(size_t index) const = 0;
    virtual std::unique_ptr<IDriver> CreateDriver(size_t index) = 0;
};

The following diagram shows two libraries. The simple library contains one constructible driver that is of type COE_MODULE_DRIVER_ID. The second library advertises three constructible drivers. The first two descriptors expose two different variations of the same underlying IDriver object. Because the internal structure of an IDriver implementation is opaque to UDDF, driver writers are free to use this mechanism to construct variations or other customizations of the same driver. The third descriptor is an entirely different driver.

Diagram of UDDF driver discovery and enumeration

UDDF Driver Discovery and Enumeration#

Drivers in Practice#

C++ Usage#

UDDF drivers are written entirely in C++, and this section discusses relevant language details.

Language Standard#

UDDF interfaces require C++17. Ensure that your UDDF driver is compiled against this standard. If you are using CMake, for example, use the following commands:

add_library(MY_UDDF_DRIVER driver.cpp)

target_compile_features(MY_UDDF_DRIVER PRIVATE cxx_std_17)

Namespace Conventions#

UDDF uses C++ namespaces to precisely name interfaces and datatypes. For example, every DDI interface is in the uddf::ddi::interfaces namespace. An instance of the ICameraModule interface might be declared like this:

uddf::ddi::interfaces::ICameraModule* cameraModule;

Consider using namespace aliases or using declarations to increase code readability.

/* Namespace Alias */
namespace DDI_IF = uddf::ddi::interfaces;
DDI_IF::ICameraModule* cam;

/* Using-Declaration */
using uddf::ddi::interfaces::ICameraModule;
ICameraModule* cam;

Exceptions#

C++ exceptions are not used in UDDF. Drivers must never throw exceptions from any entrypoint.

Best Practices#

To simplify development and to be a “good citizen” in the UDDF ecosystem, we strongly recommend that drivers follow these guidelines:

  • Use static HSL sequences as much as possible. They allow developers to author (and test) those sequences offline using PyHSL and can greatly simplify driver code.

  • Maintain as little state as possible. CDI interfaces and configuration data are usually passed into every entrypoint, so they don’t need to be cached inside a driver object.

  • Avoid dynamic memory allocation within a driver.

  • Use the provided IDriverServices interface for logging, instead of printing to stdout or stderr.

Checklist for UDDF Driver Development#

Driver Architecture and Implementation#

  • Inherit from the IDriver base class (uddf/ddi/IDriver.hpp) and implement the GetInterface() method.

  • Implement appropriate driver-specific interfaces. For example:

    • ICoEModuleControl for CoE drivers.

    • ISensorControl for sensor functionality (uddf/ddi/interfaces/ISensorControl.hpp).

  • Define driver type ID using constants from uddf/ddi/DriverTypeIds.hpp:

    uddf::ddi::UUID GetID() const noexcept override {
        return uddf::ddi::drivers::COE_MODULE_DRIVER_ID;
    }
    
  • Implement GetInterface() with proper interface resolution. For example:

    uddf::ddi::IInterface* GetInterface(const uddf::ddi::UUID& uuid) noexcept override {
        if (uuid == ICoEModuleControl::id) {
            return static_cast<ICoEModuleControl*>(this);
        }
        if (uuid == ISensorControl::id) {
            return static_cast<ISensorControl*>(this);
        }
        return nullptr;
    }
    
  • Handle unsupported interface requests by returning nullptr from GetInterface().

Hardware Access and HSL#

  • Populate DeviceTable in ConfigureDriver() with all I2C devices (uddf/ddi/DeviceTable.hpp):

    bool ConfigureDriver(const CoEModuleContext& context, uddf::ddi::DeviceTable& deviceTable) override {
        deviceTable.push_back(uddf::ddi::DeviceTableEntry{
            .i2cAddress = 0x7A,           // 7-bit I2C address
            .offsetWidth = 2,             // 16-bit register offsets
            .dataWidth = 1,               // 8-bit data
            .flags = 0,                   // Additional flags
        });
        return true;
    }
    
  • Validate device table entries against HSL I2C device definitions:

    # HSL: I2CDevice(0x7A, 16, 8, 'coe_module_ctrl_i2c')
    # Must match: address=0x7A, offsetWidth=2 (16-bit), dataWidth=1 (8-bit)
    
  • Author HSL sequences in PyHSL source language (.py files). For example:

    coe_module_ctrl_i2c = I2CDevice(0x7A, 16, 8, 'coe_module_ctrl_i2c')
    
    with ph_sequence('Init') as seq:
        seq.annotate('Sample Driver Initialization')
        with coe_module_ctrl_i2c:
            write(0x0100, 0xC1)
            write(0x0102, 0xE3, noverify=True)
    
  • Configure build system integration (CMake hsl_add_driver_sources):

    hsl_add_driver_sources(sample_coe_driver
        SOURCES Sequence_Lifecycle.py
        NAMESPACE uddf::samples::coe::hsl
    )
    
  • Include generated HSL headers (.hpp files) in the driver source code:

    #include "Sequence_Lifecycle.hpp"  // Generated from .py file
    
  • Submit static sequences by using IHardwareAccess::SubmitSequence():

    context.hwAccess->SubmitSequence(coe::hsl::Init);
    
  • For dynamic HSL, obtain HSLDynamicSequence objects via IHardwareAccess::GetDynamicSequence(). The I2C builder indices correspond directly to the indices in the driver-provided device table entry list.

    HSLDynamicSequence& sequence = context.hwAccess->GetDynamicSequence();
    
    /* Use the I2C builder for the first registered I2C device (index zero) */
    sequence.getI2CBuilder(0)->write(0x100, 0xAA);
    
    HSLResult result = context.hwAccess->SubmitSequence(sequence);
    
  • Prefer static HSL where possible for performance and validation.

  • Use appropriate sequence annotations for debugging and documentation.

  • Sequence objects are automatically reset after submission.

Driver Discovery and Export#

  • Implement the uddf_discover_drivers() export function (uddf/ddi/IDriverEnumerator.hpp):

    extern "C" {
        uddf::ddi::IDriverEnumerator* uddf_discover_drivers() {
            return new MyDriverEnumerator();
        }
    }
    
  • Create the IDriverEnumerator implementation class:

    class MyDriverEnumerator : public uddf::ddi::IDriverEnumerator {
        size_t GetDriverCount() const override { return 1; }
        const DriverInfo* GetDriverInfo(size_t index) const override;
        std::unique_ptr<IDriver> CreateDriver(size_t index) override;
    };
    
  • Provide accurate DriverInfo structures. The name field is the primary identifier for your driver.

    static const uddf::ddi::DriverInfo driverInfo = {
        .name = "Sample CoE Module Driver",
        .description = "Sample implementation for CoE camera modules",
        .vendor = "NVIDIA Corporation",
        .revision = "1.0.0",
        .driverTypeId = uddf::ddi::drivers::COE_MODULE_DRIVER_ID
    };
    

Static HSL Toolchain Support in Depth#

Diagram of the static HSL compilation workflow for UDDF

UDDF Static HSL Compilation Workflow#

HSL Header Generation (drvhsl)#

The HSL toolkit’s drvhsl tool converts compiled HSL bytecode files into C++ header files. Although driver developers typically don’t invoke this tool directly, it serves an essential role in making HSL bytecode accessible to your UDDF driver. The tool takes binary HSL container files (.hslc) and transforms them into ready-to-include C++ headers containing HSLStaticSequence objects with embedded bytecode as compile-time constants. This process lets your driver directly reference precompiled HSL sequences without needing runtime file I/O or dynamic memory allocation. The drvhsl tool handles the mechanical work of converting binary data to properly formatted C++ arrays, managing namespace organization, constant naming, and header guards. The included CMake HSL module automatically invokes this tool, making static HSL sequences as straightforward to use as including any other header file.

usage: drvhsl.py [-h] [-i source-file] [-d output-directory] [-b basename] [-p prefix] [-n namespace]
                 [-l {debug,info,warning,error,critical}]

Convert a .hslc file into C++ code to retrieve HSL bytecode blobs

options:
  -h, --help            show this help message and exit
  -i source-file, --input source-file
                        source .hslc file to process
  -d output-directory, --directory output-directory
                        output directory for generated files
  -b basename, --basename basename
                        basename for generated files (defaults to input filename)
  -p prefix, --prefix prefix
                        prefix for generated constants
  -n namespace, --namespace namespace
                        namespace for the generated code (e.g., "mydriver::hsl")
  -l {debug,info,warning,error,critical}, --loglevel {debug,info,warning,error,critical}
                        Logging level

HSL CMake Module#

The HSL CMake integration module provides a streamlined way to compile PyHSL scripts into C++ header files and integrate them into your UDDF drivers. This module handles the compilation pipeline from HSL source files to generated C++ headers that you can directly include in your driver code.

Prerequisites#
  • Python 3 and the HSL PDK package.

  • CMake 3.15 or later.

Configuring the HSL Module#

Add the HSL CMake integration to your project by including the module in your CMakeLists.txt file:

list(APPEND CMAKE_MODULE_PATH "${PATH_TO_HSL_PDK}/integrations/cmake")
include(hsl_compile_rules)

The module requires two HSL paths to be configured: the location of the HSL tools (compiler, etc.) and any compiled Python modules. For the HSL PDK, these paths are identical.

set(HSL_PYTHON_SCRIPT_DIR "${PATH_TO_HSL_PDK}/pyhsl")
set(HSL_PYTHON_MODULE_DIR "${PATH_TO_HSL_PDK}/pyhsl")
Usage#

A single function, hsl_add_driver_sources, handles all compilation including setting up build dependencies.

hsl_add_driver_sources(<TARGET>
    SOURCES <hsl_file1.py> [<hsl_file2.py> ...]
    [OUTPUT_SUBDIR <subdir>]
    [CONFIG_PARAMS <param1=value1> [<param2=value2> ...]]
    [NAMESPACE <cpp_namespace>]
    [CONSTANT_PREFIX <prefix>]
    [DEPENDENCIES <dep1.py> [<dep2.py> ...]]
)

This function is used as follows:

hsl_add_driver_sources(my_camera_driver
    SOURCES camera_sequences.py
    NAMESPACE my_driver::hsl
)

hsl_add_driver_sources(advanced_driver
    SOURCES
        init_sequences.py
        streaming_sequences.py
        power_sequences.py
    NAMESPACE advanced_driver::hsl
    CONSTANT_PREFIX ADV_
)
Configuration Variables#

Parameter

Type

Required

Description

TARGET

String

Yes

The CMake target to add HSL compilation to.

SOURCES

List

Yes

List of HSL Python source files to compile.

OUTPUT_SUBDIR

String

No

Output subdirectory in build directory (default: hsl_gen).

CONFIG_PARAMS

List

No

Configuration parameters passed to hslcompile.py as -D definitions.

NAMESPACE

String

No

C++ namespace for generated headers.

CONSTANT_PREFIX

String

No

Prefix for generated constants.

DEPENDENCIES

List

No

Additional Python files that HSL sources depend on. Use this variable to establish proper rebuilds for any dependent non-PyHSL source files.

Generated Files#

For each HSL source file, the following files are generated:

  • <basename>.hslc: Compiled HSL bytecode (intermediate).

  • <basename>.hpp: C++ header with sequence definitions.

To use the compiled output in your driver, simply #include <basename>.hpp.

More Information#

  • For complete examples, see sample drivers in sipl/uddf/samples/drivers/.

  • To construct HSL sequences, check the HSL documentation: PyHSL.