Properties

Example showing the different types of properties.

def different_types_of_properties(design: db_uu.Design) -> None:
    # Assuming the design does not have any properties

    import numpy as np  # for AppProp

    # App property (app-specific property whose value is an arbitrary array of bytes)
    app_prop_binary = de.db.AppProp.create(design, "app_prop_binary", "ILList", np.array([1, 2, 3, 4, 5, 6, 7, 8]))
    assert app_prop_binary.name == "app_prop_binary"
    assert app_prop_binary.type == de.db.PropType.APP
    assert app_prop_binary.app_type == "ILList"
    assert np.array_equal(app_prop_binary.value, np.array([1, 2, 3, 4, 5, 6, 7, 8]))

    # AppProp also supports strings
    app_prop_str = de.db.AppProp.create(design, "app_prop_str", "ILList", "Hello, World!")
    assert app_prop_str.type == de.db.PropType.APP
    assert app_prop_str.app_type == "ILList"
    # NOTE: AppProp.value_as_string doesn't verify the array of bytes is a valid string of characters
    assert app_prop_str.value_as_string() == "Hello, World!"

    # Boolean property
    bool_prop = de.db.BooleanProp.create(design, "bool_prop", True)
    assert design.props["bool_prop"] == bool_prop
    assert bool_prop.type == de.db.PropType.BOOLEAN
    assert bool_prop.value == 1
    bool_prop.value = False
    assert bool_prop.value == 0
    bool_prop.value = True
    assert bool_prop.value == 1

    # Double Property, 64-bit floating-point number
    double_prop = de.db.DoubleProp.create(design, "double_prop", 3.141592653589793)
    assert double_prop.type == de.db.PropType.DOUBLE
    assert double_prop.value == 3.141592653589793

    # Double range property, 64-bit floating-point number (min, value, max)
    double_range_prop = de.db.DoubleRangeProp.create(design, "double_range_prop", 3.1, 3.14, 3.14159)
    assert double_range_prop.name == "double_range_prop"
    assert double_range_prop.type == de.db.PropType.DOUBLE_RANGE
    assert double_range_prop.lower_bound == 3.1
    assert double_range_prop.value == 3.14
    assert double_range_prop.upper_bound == 3.14159
    # For a ranged property, valid values must be within the inclusive-lower and inclusive-upper bound
    double_range_prop.value = 3.1  # Okay
    double_range_prop.value = 3.14159  # Okay
    try:
        double_range_prop.value = 3.1416
    except RuntimeError as e:
        assert str(e) == "Value 3.1416 for property 'double_range_prop' not in specified range: [3.1 3.14159]."
    # You can change the range, if desired
    double_range_prop.set_range(2.0, 3.0, 4.0)
    assert double_range_prop.value == 3.0

    # Enum property (string value with a list of valid string values)
    enum_prop = de.db.EnumProp.create(design, "enum_prop", "red", ["red", "green", "blue"])
    assert enum_prop.name == "enum_prop"
    assert enum_prop.type == de.db.PropType.ENUM
    assert enum_prop.value == "red"
    # Enum properties must have a valid value
    try:
        enum_prop.value = "yellow"
    except RuntimeError as e:
        assert str(e) == "Value not a member of enumeration set."

    #######################################
    # FloatProp works just like DoubleProp but with a 32-bit floating-point number
    #######################################
    # FloatRangeProp works just like DoubleRangeProp but with a 32-bit floating-point numbers
    #######################################

    # Hierarchical property (no value but a property that contains other properties)
    hier_record_prop = de.db.HierProp.create(design, "hier_record_prop")
    assert hier_record_prop.name == "hier_record_prop"
    assert hier_record_prop.type == de.db.PropType.HIER
    de.db.StringProp.create(hier_record_prop, "company", "Keysight Technologies")
    de.db.IntProp.create(hier_record_prop, "year", 2024)

    # Get the properties from the HierProp
    hier_props = hier_record_prop.props
    assert len(hier_props) == 2
    assert hier_props["company"].value == "Keysight Technologies"
    assert hier_props["year"].value == 2024

    # Integer property
    int_prop = de.db.IntProp.create(design, "int_prop", 42)
    assert int_prop.name == "int_prop"
    assert int_prop.type == de.db.PropType.INT
    assert int_prop.value == 42

    # Integer range property (min, value, max)
    int_range_prop = de.db.IntRangeProp.create(design, "int_range_prop", 20, 25, 30)
    assert int_range_prop.name == "int_range_prop"
    assert int_range_prop.type == de.db.PropType.INT_RANGE
    assert int_range_prop.lower_bound == 20
    assert int_range_prop.value == 25
    assert int_range_prop.upper_bound == 30
    # For a ranged property, valid values must be within the inclusive-lower and inclusive-upper bound
    int_range_prop.value = 20  # Okay
    int_range_prop.value = 30  # Okay
    try:
        int_range_prop.value = 31
    except RuntimeError as e:
        assert str(e) == "Value 31 for property 'int_range_prop' not in specified range: [20 30]."

    # String property
    str_prop = de.db.StringProp.create(design, "str_prop", "Hello, World!")
    assert str_prop.name == "str_prop"
    assert str_prop.type == de.db.PropType.STRING
    assert str_prop.value == "Hello, World!"

    # Time property - integer representing time in seconds
    time_prop = de.db.TimeProp.create(design, "time_prop", 1717724783)
    assert time_prop.name == "time_prop"
    assert time_prop.type == de.db.PropType.TIME
    assert time_prop.value == 1717724783

    #######################################
    # Time range property operates like IntRangeProp, an integer representing time in seconds (min, value, max),
    #######################################

    # Now, let's get all the properties on the design
    assert len(design.props) == 11
    prop = design.props["app_prop_binary"]
    assert prop == app_prop_binary
    prop = design.props["app_prop_str"]
    assert prop == app_prop_str
    prop = design.props["bool_prop"]
    assert prop == bool_prop
    prop = design.props["double_prop"]
    assert prop == double_prop
    prop = design.props["double_range_prop"]
    assert prop == double_range_prop
    prop = design.props["enum_prop"]
    assert prop == enum_prop
    prop = design.props["hier_record_prop"]
    assert prop == hier_record_prop
    prop = design.props["int_prop"]
    assert prop == int_prop
    prop = design.props["int_range_prop"]
    assert prop == int_range_prop
    prop = design.props["str_prop"]
    assert prop == str_prop
    prop = design.props["time_prop"]
    assert prop == time_prop

Example showing the correlation between properties and deactivating an instance.

def accessing_deactivated_instance_properties(design: db_uu.Design) -> None:
    # Assuming design has an instance named "C1" that is activated and without any properties
    inst_c1 = design.instances["C1"]
    assert not inst_c1.is_deactivated
    properties = inst_c1.props
    assert len(properties) == 0

    # Deactivate the instance and retrieve its properties
    inst_c1.deactivate()
    assert inst_c1.is_deactivated
    properties = inst_c1.props

    # Deactivating an instance sets two properties, nlAction and lvsIgnore
    assert len(properties) == 2
    # The nlAction property is a String property and its value is "ignore" when the instance is deactivated
    nl_action = properties["nlAction"]
    assert de.db.Property.is_string(nl_action) and isinstance(nl_action, de.db.StringProp)
    assert nl_action.value == "ignore"
    # The lvsIgnore property is a Boolean property and its value is True when the instance is deactivated
    lvs_ignore = properties["lvsIgnore"]
    # OA stores Boolean properties as integers and may contain values different from 0 and 1
    assert de.db.Property.is_boolean(lvs_ignore) and isinstance(lvs_ignore, de.db.BooleanProp)
    assert lvs_ignore.value == 1

    # Reactivate and verify the properties are removed
    inst_c1.activate()
    properties = inst_c1.props
    assert len(properties) == 0

    # Deactivate and short will set a different set of properties
    inst_c1.deactivate_and_short()
    assert inst_c1.is_deactivated
    assert inst_c1.is_deactivated_and_shorted
    properties = inst_c1.props
    assert len(properties) == 2

    # The deactivateAndShort and lvsIgnore properties are set when the instance is deactivated and shorted
    deactivate_and_short = properties["deactivateAndShort"]
    assert de.db.BooleanProp.is_boolean(deactivate_and_short) and isinstance(deactivate_and_short, de.db.BooleanProp)
    assert deactivate_and_short.value == 1
    lvs_ignore = properties["lvsIgnore"]
    assert de.db.Property.is_boolean(lvs_ignore) and isinstance(lvs_ignore, de.db.BooleanProp)
    # Reactivate and verify the properties are removed
    inst_c1.activate()
    assert not inst_c1.is_deactivated
    assert not inst_c1.is_deactivated_and_shorted
    properties = inst_c1.props
    assert len(properties) == 0

    # We can go the other way now by setting the properties and verifying the instance is deactivated
    de.db.StringProp.create(inst_c1, "nlAction", "ignore")
    de.db.BooleanProp.create(inst_c1, "lvsIgnore", 1)

    properties = inst_c1.props
    assert len(properties) == 2
    assert inst_c1.is_deactivated

    # Deleting the properties will reactivate the instance
    properties["lvsIgnore"].delete_prop()
    properties["nlAction"].delete_prop()
    properties = inst_c1.props
    assert len(properties) == 0
    assert not inst_c1.is_deactivated

Example showing the correlation between properties and parameters for interoperable instances.

def properties_and_cdf_parameters(design: db_uu.Design) -> None:
    # Assume the design has an unmodified instance of analogLib:n1port named NPORT0
    inst_v1 = design.instances["NPORT0"]
    assert inst_v1.cell_name == "n1port"
    # Properties on interoperable components are also parameters.
    thermal_noise_prop = inst_v1.props["thermalnoise"]
    assert thermal_noise_prop.value == "yes"

    inst_cdf = exp.cdf.InstanceParams(inst_v1)
    thermal_noise_param = inst_cdf.params["thermalnoise"]
    assert thermal_noise_param.value == "yes"

    # Setting the value of a property will also update the value of the corresponding parameter
    assert de.db.Property.is_string(thermal_noise_prop)
    thermal_noise_prop.value = "no"
    assert thermal_noise_param.value == "no"
    # Setting the value of the parameter will also update the value of the corresponding property
    thermal_noise_param.value = "yes"
    # NOTE: Updating the value of the parameter doesn't apply to the instance until update_instance is called
    assert thermal_noise_prop.value == "no"
    inst_cdf.update_instance(inst_v1)
    # After updating the instance, the value of the parameter is applied to the property
    assert thermal_noise_prop.value == "yes"

    datafile_param = inst_cdf.params["dataFile"]
    # No value by default
    assert datafile_param.value == ""
    # No dataFile property
    assert inst_v1.props.find("dataFile") is None
    # Set the value of the parameter and update the instance
    datafile_param.value = "dataFile.txt"
    assert datafile_param.value == "dataFile.txt"
    inst_cdf.update_instance(inst_v1)
    # Property is now available
    datafile_prop = inst_v1.props["dataFile"]
    assert datafile_prop.value == "dataFile.txt"

    # Deleting a property will restore the default value of the parameter
    datafile_prop.delete_prop()
    assert inst_v1.props.find("dataFile") is None
    inst_cdf = exp.cdf.InstanceParams(inst_v1)
    datafile_param = inst_cdf.params["dataFile"]
    # Value restored to the default
    assert datafile_param.value == ""

Example showing how DMData is used as a container for properties associated with a library.

def dm_data_as_a_property_container(workspace: de.Workspace, library: de.Library) -> None:
    # NOTE: Use DMData to attach properties to a Library, Cell, or View.
    # This example just shows just the library case; Cell and View work similarly.
    # Assume the library is writable and there isn't already DMData attached to the library
    assert not library.has_dm_data
    assert library.is_writable
    # Open the DMData in write mode so that it can be saved. DMData opened with "w" mode will delete
    # existing properties.
    data = de.DMData.open(library, "w")
    assert data.owner == library
    de.db.StringProp.create(data, "company", "Keysight Technologies")
    de.db.IntProp.create(data, "year", 2024)
    # The modified property is True when DMData has been modified but not saved
    assert data.modified
    data.save()
    assert not data.modified

    # Let's close the library and reopen it to verify the properties are still there
    lib_name = library.name
    lib_path = str(library.path.resolve())
    workspace.close_library(library)
    # If you want to modify DMData and save it, you'll need to reopen the library in one of the write modes
    library = workspace.open_library(lib_name, lib_path, de.LibraryMode.NON_SHARED)
    # You can obtain DMData directly from the library, if it has been previously saved
    assert library.has_dm_data
    # You'll need to reopen the DMData in append mode if you want to modify it
    data = library.dm_data("a")
    assert data.props["company"].value == "Keysight Technologies"
    assert data.props["year"].value == 2024
    # Delete a property
    data.props["year"].delete_prop()
    assert data.props.find("year") is None
    # If you're unsure if a property exists, you can check for it. You can use find_prop and check the
    # result or catch the KeyError exception when accessing the property directly.
    prop = data.find_prop("year")
    assert prop is None
    try:
        prop = data.props["year"]
    except KeyError as e:
        assert str(e) == "'Key not found: \"year\".'"

    # Changes to DMData can be reverted to the last saved state
    data.revert()
    assert data.props["company"].value == "Keysight Technologies"
    assert data.props["year"].value == 2024

    # Save it
    data.save()
    workspace.close_library(library)
    # You cannot open DMData in write mode if the library is opened in read-only mode
    library = workspace.open_library(lib_name, lib_path, de.LibraryMode.READ_ONLY)
    try:
        data = de.DMData.open(library, "w")
    except RuntimeError as e:
        print(str)
        assert str(e) == f'Failed to get DMData for library "{lib_name}".: Library "{lib_name}" is read-only.'
        # So open it in write mode
        workspace.close_library(library)
        library = workspace.open_library(lib_name, lib_path, de.LibraryMode.NON_SHARED)

    # You can't save DMData if it's read-only, even when the library is open in one of the write modes
    data = de.DMData.open(library, "r")
    assert data.props["company"].value == "Keysight Technologies"
    assert data.props["year"].value == 2024
    data.props["year"].delete_prop()

    try:
        data.save()
    except RuntimeError as e:
        assert str(e) == "Attempt to save a read-only DMData."
        # You can make a DMData that was opened as read-only writable and save it
        data.make_writable()
        data.save()

    # And let's go ahead and delete the DMData entirely
    library.delete_dm_data()
    assert not library.has_dm_data
    workspace.close_library(library)
    library = workspace.open_library(lib_name, lib_path, de.LibraryMode.NON_SHARED)
    assert not library.has_dm_data
    workspace.close_library(library)
On this page