from copy import deepcopy
from spydrnet.ir import FirstClassElement
from spydrnet.ir import Library
from spydrnet.ir import Instance
from spydrnet.ir.views.listview import ListView
from spydrnet.global_state import global_callback
from spydrnet.global_state.global_callback import _call_create_netlist
from spydrnet.ir import Definition
class Netlist(FirstClassElement):
"""Represents a netlist object.
Contains a top level instance and libraries
Examples
--------
After importing the spydrnet package, we can initialize the netlist from scratch
>>> import spydrnet as sdn
>>> netlist = sdn.Netlist()
We can also initalize an top instance for the netlist. For more info: :ref:`Instance`
>>> top_instance = sdn.Instance()
>>> netlist.top_instance = top_instance
This is the way to set up a top level definition for the netlist
>>> top_definition = sdn.Definition()
>>> netlist.top_instance = top_definition
We can initialize a "primatives" and a "work" library this way:
>>> primitives_library = netlist.create_library()
>>> primitives_library['EDIF.identifier'] = 'hdi_primitives'
>>> work_library = netlist.create_library()
>>> work_library['EDIF.identifier'] = 'work'
"""
__slots__ = ["_libraries", "_top_instance"]
[docs] def __init__(self, name=None, properties=None):
"""
creates an empty object of type netlist
parameters
----------
name - (str) the name of this instance
properties - (dict) the dictionary which holds the properties
"""
super().__init__()
self._libraries = []
self._top_instance = None
_call_create_netlist(self)
if name is not None:
self.name = name
if properties is not None:
assert isinstance(properties, dict), "properties must be a dictionary"
for key in properties:
self[key] = properties[key]
[docs] def compose(self, *args, **kwargs):
"""Compose a netlist into a file format.
Compose(filename).
Shortcut to :func:`~spydrnet.compose`.
"""
from spydrnet.composers import compose
compose(self, *args, **kwargs)
@property
def libraries(self):
"""Get a list of all libraries included in the netlist."""
return ListView(self._libraries)
@libraries.setter
def libraries(self, value):
"""Set the libraries.
This function can only be used to reorder the libraries. Use the remove_library and
add_library functions to add and remove libraries.
Parameters
----------
value - the reordered list of libraries
"""
value_list = list(value)
value_set = set(value_list)
assert (
len(value_list) == len(value_set) and set(self._libraries) == value_set
), "Set of values do not match, this assignment can only reorder values, \
values must be unique"
self._libraries = value_list
@property
def top_instance(self):
"""Get the top instance in the netlist.
Returns
-------
Instance
The top level instance in the environment
"""
return self._top_instance
@top_instance.setter
def top_instance(self, instance):
"""Sets the top instance of the design.
The instance must not be null and should probably come from this netlist
Parameters
----------
instance - (Instance or Definition) the instance to set as the top instance. If a definition
is passed into the funciton,
creates a new instance with that definition and set it as the top instance.
"""
assert (
instance is None
or isinstance(instance, Instance)
or isinstance(instance, Definition)
), "Must specify an instance"
global_callback._call_netlist_top_instance(self, instance)
# TODO: should We have a DRC that makes sure the instance is of a definition contained in
# netlist? I think no but I am open to hear other points of veiw.
if self.top_instance:
self.top_instance.is_top_instance = False
if isinstance(instance, Definition):
top = Instance()
top.reference = instance
top.is_top_instance = True
self.top_instance = top
else:
self._top_instance = instance
if instance:
instance.is_top_instance = True
def set_top_instance(self, instance, instance_name="instance"):
"""Sets the top instance of the design.
The instance must not be null and should probably come from this netlist
Parameters
----------
instance - (Instance or Definition) the instance to set as the top instance. If a definition
is passed into the funciton,
creates a new instance with that definition and set it as the top instance.
"""
assert (
instance is None
or isinstance(instance, Instance)
or isinstance(instance, Definition)
), "Must specify an instance"
global_callback._call_netlist_top_instance(self, instance)
# TODO: should We have a DRC that makes sure the instance is of a definition contained in
# netlist? I think no but I am open to hear other points of veiw.
if isinstance(instance, Definition):
top = Instance()
top.reference = instance
instance.name = instance_name
self.top_instance = top
self.top_instance.name = instance_name
else:
self._top_instance = instance
[docs] def create_library(self, name=None, properties=None):
"""Create a library and add it to the netlist and return that library
parameters
----------
name - (str) the name of the library
properties - (dict) the dictionary which holds the properties of the library
"""
library = Library(name, properties)
self.add_library(library)
return library
[docs] def add_library(self, library, position=None):
"""add an already existing library to the netlist.
This library should not belong to another netlist. Use
remove_library from other netlists before adding
Parameters
----------
library - Library
The library to be added to the netlist.
position - int, (default None)
When set, it is the index at which to add the library in the libraries list.
"""
assert library not in self._libraries, "Library already included in netlist"
assert library.netlist is None, "Library already belongs to a different netlist"
global_callback._call_netlist_add_library(self, library)
if position is not None:
self._libraries.insert(position, library)
else:
self._libraries.append(library)
library._netlist = self
[docs] def remove_library(self, library):
"""Removes the given library if it is in the netlist
Parameters
----------
library - Library
The library to be removed.
"""
assert library.netlist == self, "Library is not included in netlist"
self._remove_library(library)
self._libraries.remove(library)
[docs] def remove_libraries_from(self, libraries):
"""Removes all the given libraries from the netlist.
All libraries must be in the netlist.
Parameters
----------
libraries - Set
Libraries to be removed.
"""
if isinstance(libraries, set):
excluded_libraries = libraries
else:
excluded_libraries = set(libraries)
assert all(x.netlist == self for x in excluded_libraries), (
"Some libraries to remove are not included in " "netlist "
)
included_libraries = []
for library in self._libraries:
if library not in excluded_libraries:
included_libraries.append(library)
else:
self._remove_library(library)
self._libraries = included_libraries
def _remove_library(self, library):
"""Internal function which will separate a particular libraries binding from the netlist."""
global_callback._call_netlist_remove_library(self, library)
library._netlist = None
def _clone_rip(self, memo):
"""Need to remove any extraneous references to floating, no parent instances"""
for lib in self._libraries:
for defin in lib._definitions:
new_ref = set()
for ref in defin._references:
if ref in memo.values():
new_ref.add(ref)
defin._references = new_ref
def _clone(self, memo):
"""Clone leaving all references in tact.
The element can then either be ripped or ripped and replaced.
"""
assert (
self not in memo
), "the object should not have been copied twice in this pass"
from spydrnet.ir import Netlist as NetlistExtended
c = NetlistExtended()
memo[self] = c
c._data = deepcopy(self._data)
new_libraries = []
for library in self._libraries:
new_libraries.append(library._clone(memo))
c._libraries = new_libraries
if self._top_instance is None:
c._top_instance = None
else:
if self._top_instance in memo:
c._top_instance = memo[self._top_instance]
else:
new_top = self.top_instance._clone(memo)
new_top._clone_rip_and_replace_in_definition(memo)
new_top._clone_rip_and_replace_in_library(memo)
c._top_instance = new_top
for library in c._libraries:
library._netlist = c
library._clone_rip_and_replace(memo)
return c
[docs] def clone(self):
"""API safe clone on a netlist.
This clone function should act just the way you would expect
All references are internal to the netlist that has been cloned.
"""
memo = {}
c = self._clone(memo)
c._clone_rip(memo)
return c
def __str__(self):
"""Re-define the print function so it is easier to read"""
rep = super().__str__()
rep = rep[:-1] + "; "
if self.top_instance is None:
rep += "top_instance undefined"
elif self.top_instance.name is None:
rep += "top_instance.name undefined"
else:
rep += "top_instance.name '" + self.top_instance.name + "'"
rep += ">"
return rep
def is_unique(self):
"""
checks whether all of the instances in the netlist are unique or not
"""
for instance in self.get_instances():
if len(instance.reference.references) == 1 or instance.reference.is_leaf():
continue
else:
return False
return True