Skip to content

Commit

Permalink
Merge pull request #172 from byuccl/eblif
Browse files Browse the repository at this point in the history
SpyDrNet 1.11.0
  • Loading branch information
jacobdbrown4 committed Apr 11, 2022
2 parents 1d3f73f + 9fd055a commit dce74a3
Show file tree
Hide file tree
Showing 24 changed files with 1,176 additions and 8 deletions.
6 changes: 6 additions & 0 deletions RELEASE.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
SpyDrNet 1.11.0
---------------
April 9, 2022

* Initial support for parsing and composing EBLIF netlists.

SpyDrNet 1.10.1
----------------
January 1, 2022
Expand Down
8 changes: 8 additions & 0 deletions docs/source/reference/eblif_support.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.. _eblif_support:

EBLIF Support
--------------

SpyDrNet's EBLIF parser requires/follows conventions followed by Symbiflow. Only structural BLIF is supported, as well as a few extensions (EBLIF). See Symbiflow's File Format page (https://docs.verilogtorouting.org/en/latest/vpr/file_formats/#vpr-pack-file) for more details. It is important to point out that, along with Symbiflow's VPR, SpyDrNet expects designs in the EBLIF format to be flat. Hierarchy is not currently supported by either.

In addition, it is important that .blackbox modules are included in the netlist to provide SpyDrNet with the information about primitives in the netlist (port directions, etc.).
8 changes: 5 additions & 3 deletions docs/source/reference/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ Reference
.. toctree::
:maxdepth: 2

introduction
element_data
classes/index.rst
netlist_types
element_data
clone
uniquify
NamespaceManager
verilog_support
eblif_support
Callback_Framework
extensions
extensions
introduction
8 changes: 8 additions & 0 deletions docs/source/reference/netlist_types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.. _netlist_types:

Netlist Types
--------------

SpyDrNet supports three netlist types: EDIF, Verilog, and EBLIF. All types can be parsed into and composed out of SpyDrNet. However, crossing between types (parsing in one type and composing out another type) is not guaranteed to always work. Crossing types will likely be improved in a future version.

Some functions take a netlist type in as a parameter. Netlist types can be imported from spydrnet.util.netlist_type
2 changes: 1 addition & 1 deletion docs/source/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ SpyDrNet has built in example netlists. For this tutorial, we will use the examp

.. code-block::
netlist = sdn.load_example_netlist("one_counter")
netlist = sdn.load_example_netlist_by_name("one_counter")
Intermediate Representation (IR) Basics
---------------------------------------
Expand Down
28 changes: 25 additions & 3 deletions spydrnet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def get_active_plugins():
get_instances, get_libraries, get_netlists,
get_pins, get_ports, get_wires)
from spydrnet.util.selection import ALL, BOTH, INSIDE, OUTSIDE
from spydrnet.util.netlist_type import EDIF, VERILOG, EBLIF

base_dir = os.path.dirname(os.path.abspath(__file__))

Expand All @@ -121,7 +122,28 @@ def get_active_plugins():
example_netlist_names.append(basename[:basename.index('.')])
example_netlist_names.sort()

verilog_example_netlist_names = list()
for filename in glob.glob(os.path.join(base_dir, 'support_files', 'verilog_netlists', "*")):
basename = os.path.basename(filename)
verilog_example_netlist_names.append(basename[:basename.index('.')])
verilog_example_netlist_names.sort()

def load_example_netlist_by_name(name):
assert name in example_netlist_names, "Example netlist not found"
return parse(os.path.join(base_dir, 'support_files', 'EDIF_netlists', name + ".edf.zip"))
eblif_example_netlist_names = list()
for filename in glob.glob(os.path.join(base_dir, 'support_files', 'eblif_netlists', "*")):
basename = os.path.basename(filename)
eblif_example_netlist_names.append(basename[:basename.index('.')])
eblif_example_netlist_names.sort()

def load_example_netlist_by_name(name, format=EDIF):
if format is EDIF:
assert name in example_netlist_names, "Example netlist not found"
return parse(os.path.join(base_dir, 'support_files', 'EDIF_netlists', name + ".edf.zip"))
elif format is VERILOG:
assert name in verilog_example_netlist_names, "Example netlist not found"
return parse(os.path.join(base_dir, 'support_files', 'verilog_netlists', name + ".v.zip"))
elif format is EBLIF:
assert name in eblif_example_netlist_names, "Example netlist not found"
return parse(os.path.join(base_dir, 'support_files', 'eblif_netlists', name + ".eblif.zip"))
else: # if no version is recognized, default to edif
assert name in example_netlist_names, "Example netlist not found"
return parse(os.path.join(base_dir, 'support_files', 'EDIF_netlists', name + ".edf.zip"))
7 changes: 6 additions & 1 deletion spydrnet/composers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os


def compose(netlist, filename, definition_list=[], write_blackbox=True):
def compose(netlist, filename, definition_list=[], write_blackbox=True, write_eblif_cname=True):
"""To compose a file into a netlist format"""
extension = os.path.splitext(filename)[1]
extension_lower = extension.lower()
Expand All @@ -15,5 +15,10 @@ def compose(netlist, filename, definition_list=[], write_blackbox=True):
from spydrnet.composers.verilog.composer import Composer
composer = Composer(definition_list, write_blackbox)
composer.run(netlist, file_out=filename)
elif extension_lower in [".eblif",".blif"]:
from spydrnet.composers.eblif.eblif_composer import EBLIFComposer
composer = EBLIFComposer(write_blackbox, write_eblif_cname)
composer.run(netlist,filename)
None
else:
raise RuntimeError("Extension {} not recognized.".format(extension))
253 changes: 253 additions & 0 deletions spydrnet/composers/eblif/eblif_composer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import os
from re import sub
from spydrnet.util.selection import Selection
import spydrnet as sdn

class EBLIFComposer:
def __init__(self,write_blackbox, write_cname=True):
self.netlist = None
self.open_file = None
self.write_blackbox = write_blackbox
self.write_cname = write_cname
self.blackboxes_to_compose = set()

def run(self, ir, file_out):
self.open_file = self.prepare_file(file_out)
self._compose(ir)

def prepare_file(self,out_file):
if (os.path.exists(out_file)):
f = open(out_file,"w")
else:
f = open(out_file,"x")
return f

def write_out(self,string):
self.open_file.write(string)

def clean_up(self):
self.open_file.close()

def _compose(self,ir):
self.netlist = ir
# print("Composing...")
self.compose_comments()
self.compose_top_model()
self.clean_up()

def compose_comments(self):
for comment in self.netlist["EBLIF.comment"]:
to_write = "# "+comment+"\n"
self.write_out(to_write)
self.write_out("# Generated by \'BYU spydrnet tool\'\n")
self.write_out("\n")

def compose_top_model(self):
top_instance = self.netlist.top_instance
to_write = ".model "+top_instance.reference.name+"\n"
self.write_out(to_write)
self.compose_top_level_ports()
self.compose_top_level_clocks()
self.compose_default_wires()
self.compose_instances()
self.compose_end()
if (self.write_blackbox):
self.compose_blackboxes()

def compose_top_level_ports(self):
to_write = ".inputs "
for port in self.netlist.top_instance.get_ports(filter = lambda x: x.direction is sdn.Port.Direction.IN):
if len(port.pins) > 1:
for i in range(len(port.pins)):
to_write+=port.name+"["+str(i)+"] "
else:
to_write+=port.name+" "
to_write+="\n"
self.write_out(to_write)

to_write = ".outputs "
for port in self.netlist.top_instance.get_ports(filter = lambda x: x.direction is sdn.Port.Direction.OUT):
if len(port.pins) > 1:
for i in range(len(port.pins)):
to_write+=port.name+"["+str(i)+"] "
else:
to_write+=port.name+" "
to_write+="\n"
self.write_out(to_write)

def compose_top_level_clocks(self):
if "EBLIF.clock" in self.netlist.top_instance.data:
to_write = ".clock "
for clock in self.netlist.top_instance["EBLIF.clock"]:
to_write+=clock+" "
self.write_out(to_write+"\n")

def compose_default_wires(self):
default_wires = list()
try:
self.netlist["EBLIF.default_wires"]
default_wires = self.netlist['EBLIF.default_wires']
except KeyError:
None
if "$false" in default_wires:
self.write_out(".names $false\n")
if "$true" in default_wires:
self.write_out(".names $true\n1\n")
if "$undef" in default_wires:
self.write_out(".names $undef\n")
self.write_out("\n")

def compose_instances(self):
categories = self.separate_by_type()
if "EBLIF.subckt" in categories.keys():
self.compose_subcircuits(categories["EBLIF.subckt"])
if "EBLIF.gate" in categories.keys():
self.compose_subcircuits(categories["EBLIF.gate"],is_gate=True)
if "EBLIF.other" in categories.keys():
self.compose_subcircuits(categories["EBLIF.other"])
if "EBLIF.names" in categories.keys():
self.compose_names(categories["EBLIF.names"])
if "EBLIF.latch" in categories.keys():
self.compose_latches(categories["EBLIF.latch"])

def separate_by_type(self):
dict_by_types = dict()
for instance in self.netlist.get_instances():
try:
instance["EBLIF.type"]
except KeyError:
# print("Error, no type found")
instance["EBLIF.type"] = "EBLIF.other"
type = instance["EBLIF.type"]
try:
dict_by_types[type]
except KeyError:
dict_by_types[type] = list()
dict_by_types[type].append(instance)
return dict_by_types

def compose_subcircuits(self,list_of_subcircuits,is_gate=False):
for subckt in list_of_subcircuits:
self.blackboxes_to_compose.add(subckt.reference.name)
to_add = ""
if is_gate:
to_write = ".gate "+ subckt.reference.name+" "
else:
to_write = ".subckt "+ subckt.reference.name+" "
amount_of_ports_to_write = 0
for port in subckt.get_ports():
for pin in port.pins:
amount_of_ports_to_write+=1
for port in subckt.reference.get_ports():
inner_pin_list = port.pins
for pin in subckt.get_pins(selection=Selection.OUTSIDE,filter=lambda x: x.inner_pin.port is port):
if (amount_of_ports_to_write > 5):
to_write+=to_add+" \\ \n"
to_add = ""
if len(inner_pin_list) > 1:
index = inner_pin_list.index(pin.inner_pin)
to_add+=port.name+"["+str(index)+"]"+"="
else:
to_add+=port.name+"="
if pin.wire:
to_add+=self.find_connected_wire_info(pin)
else:
to_add+='unconn'
to_add+=" "
to_write+=to_add+'\n'
self.write_out(to_write)
self.find_and_write_additional_instance_info(subckt)

def compose_names(self,list_of_names):
for name_instance in list_of_names:
to_write = ".names "
in_pin_list = list(x for x in name_instance.get_pins(selection=Selection.OUTSIDE,filter=lambda x: x.inner_pin.port.direction is sdn.IN))
in_pin_list.reverse()
for pin in in_pin_list:
# for pin in name_instance.get_pins(selection=Selection.OUTSIDE,filter=lambda x: x.inner_pin.port.direction is sdn.IN):
if pin.wire:
to_write+=pin.wire.cable.name
if len(pin.wire.cable.wires) > 1: # if a multi bit wire, add the index
to_write+="["+str(pin.wire.cable.wires.index(pin.wire))+"]"
else:
to_write+="unconn"
to_write+=" "
for pin in name_instance.get_pins(selection=Selection.OUTSIDE,filter=lambda x: x.inner_pin.port.direction is sdn.OUT):
if pin.wire:
to_write+=pin.wire.cable.name
if len(pin.wire.cable.wires) > 1: # if a multi bit wire, add the index
to_write+="["+str(pin.wire.cable.wires.index(pin.wire))+"]"
else:
to_write+="unconn"
to_write+=" "
to_write+="\n"
try:
name_instance["EBLIF.output_covers"]
for output_cover in name_instance["EBLIF.output_covers"]:
to_write+=output_cover+"\n"
except KeyError:
None
self.write_out(to_write)
self.find_and_write_additional_instance_info(name_instance)

def compose_latches(self,latch_list):
for latch_instance in latch_list:
to_write = ".latch "
# port_list = list(x for x in latch_instance.get_ports())
for port_type in ['input', 'output', 'type', 'control', 'init-val']: # this is the specific order of ports
# current_port = next(port for port in port_list if port.name == port_type)
for pin in latch_instance.get_pins(selection=Selection.OUTSIDE,filter=lambda x: x.inner_pin.port.name == port_type):
# connection_name = None
if pin.wire:
to_write+=pin.wire.cable.name
if (len(pin.wire.cable.wires)>1):
to_write+="["+str(pin.wire.index())+"]"
to_write+=" "
# connection_name=pin.wire.cable.name
else:
to_write+="unconn "
# connection_name="unconn"
to_write+='\n'
self.write_out(to_write)
self.find_and_write_additional_instance_info(latch_instance)

def find_connected_wire_info(self,pin):
string_to_return = ""
cable = pin.wire.cable
string_to_return+=cable.name
if len(cable.wires) > 1:
string_to_return+="["+str(pin.wire.index()) +"]"
return string_to_return

def find_and_write_additional_instance_info(self,instance):
to_write = ""
if self.write_cname:
# if "EBLIF.cname" in instance.data:
# to_write+=".cname "+instance["EBLIF.cname"]+'\n'
# else:
to_write+=".cname "+instance.name+'\n'
if "EBLIF.attr" in instance.data:
for key, value in instance.data["EBLIF.attr"].items():
to_write+=".attr "+key+" "+value+'\n'
if "EBLIF.param" in instance.data:
for key, value in instance.data["EBLIF.param"].items():
to_write+=".param "+key+" "+value+'\n'
self.write_out(to_write)

def compose_blackboxes(self):
for definition in self.netlist.get_definitions():
if definition.name in self.blackboxes_to_compose:
if "EBLIF.blackbox" in definition.data.keys():
to_write = "\n.model "+definition.name
to_write+="\n.inputs"
for port in definition.get_ports(filter=lambda x: x.direction is sdn.IN):
to_write+=" "+port.name
to_write+="\n.outputs"
for port in definition.get_ports(filter=lambda x: x.direction is sdn.OUT):
to_write+=" "+port.name
self.write_out(to_write+"\n")
self.write_out(".blackbox\n")
self.compose_end()

def compose_end(self):
self.write_out(".end\n")
Loading

0 comments on commit dce74a3

Please sign in to comment.