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)