#!/usr/bin/python
########################################################################################################################
#
# Copyright (c) 2020, Nifty Chips Laboratory, Hanyang University
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
# following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
# disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
# following disclaimer in the documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
########################################################################################################################
"""
This module implements interface with virtuoso in skill language
"""
import logging
from math import log10
from decimal import *
import yaml
import numpy as np
import laygo2.object
import laygo2.util.transform as tf
import importlib
if hasattr(importlib, 'find_loader'): # python 3.4-
if importlib.find_loader('laygo2_tech') is not None:
import laygo2_tech as tech
else: # load dummy tech
tech = {"name": "dummy", "layers": {}, "templates": {}, "grids": {}}
elif hasattr(importlib, 'find_spec'): # python 3.4+
if importlib.find_spec('laygo2_tech') is not None:
import laygo2_tech as tech
else: # load dummy tech
tech = {"name": "dummy", "layers": {}, "templates": {}, "grids": {}}
__author__ = ""
__maintainer__ = ""
__status__ = "Prototype"
def _py2skill_number(value, scale=0.001):
fmt_str = "%." + "%d" % (-1 * log10(scale) + 1) + "f " # for truncations
return fmt_str % (value * scale)
def _py2skill_float(value, scale=0.001):
exp = np.ceil(-1 * log10(value) + 1)
fmt_str = "%." + "%d" % (-1 * log10(scale) + 1) + "f" # for truncations
# convert to exponentail expression
print(value, exp, str(value))
return fmt_str % (value * (10**exp) * scale) + "e" + str(int(-1 * exp))
def _py2skill_list(pylist, scale=0.001):
"""Convert a python list object to a skill list."""
list_str = "list( "
for item in pylist:
if isinstance(item, list): # nested list
list_str += _py2skill_list(item) + " "
elif isinstance(item, np.ndarray): # nested list
list_str += _py2skill_list(item, scale=scale) + " "
elif isinstance(item, str):
list_str += '"' + str(item) + '" '
elif isinstance(item, bool):
if item:
list_str += "t "
else:
list_str += "nil "
elif isinstance(item, int) or isinstance(item, np.integer):
# fmt_str = "%."+"%d" % (-1*log10(scale)+1)+"f " # for truncations
# list_str += fmt_str%(item*scale) + " "
list_str += _py2skill_number(item, scale) + " "
list_str += ")"
return list_str
def _py2skill_inst_params_list(pylist):
"""Convert a python list object to a skill list. (for pcell parameters)"""
list_str = "list( "
for item in pylist:
if isinstance(item, list): # nested list
list_str += _py2skill_inst_params_list(item) + " "
elif isinstance(item, np.ndarray): # nested list
list_str += _py2skill_inst_params_list(item) + " "
elif isinstance(item, str):
list_str += '"' + str(item) + '" '
elif isinstance(item, float):
list_str += str(item)
# list_str += _py2skill_float(item, scale) + " "
elif isinstance(item, bool):
if item:
list_str += "t "
else:
list_str += "nil "
elif isinstance(item, int) or isinstance(item, np.integer):
# fmt_str = "%."+"%d" % (-1*log10(scale)+1)+"f " # for truncations
# list_str += fmt_str%(item*scale) + " "
# list_str += _py2skill_number(item, 1) + " " # do not scale integers
list_str += str(item) + " " # do not scale integers
list_str += ")"
return list_str
def _py2skill_inst_params(value_dict):
"""Convert instance parameter dictionary to skill list"""
_list = []
for k, v in value_dict.items():
if isinstance(v, str):
_type = "string"
elif isinstance(v, float):
_type = "float"
elif isinstance(v, bool):
_type = "boolean"
elif isinstance(v, int) or isinstance(v, np.integer):
_type = "int"
_list.append([k, _type, v])
return _py2skill_inst_params_list(_list)
def _translate_obj(objname, obj, scale=0.001, master=None, offset=np.array([0, 0])):
"""
Convert an object to corresponding skill commands.
offset : np.array([int, int])
Offsets to obj.xy
"""
if master is None:
mxy = np.array([0, 0])
mtf = "R0"
else: # if the translated object has a master (e.g. VirtualInstance)
mxy = master.xy
mtf = master.transform
if obj.__class__ == laygo2.object.Rect:
# coloring
if obj.color == None:
color = "not MPT"
else:
color = obj.color
# Invoke _laygo2_generate_rect( cv layer bbox ) in {header_filename}
_xy = np.sort(obj.xy, axis=0) # make sure obj.xy is sorted
_xy = mxy + np.dot(
_xy
+ np.array(
[[-obj.hextension, -obj.vextension], [obj.hextension, obj.vextension]]
),
tf.Mt(mtf).T,
)
# _xy = mxy + np.dot(obj.xy + np.array([[-obj.hextension, -obj.vextension], [obj.hextension, obj.vextension]]),
# tf.Mt(mtf).T)
return '_laygo2_generate_rect(cv, %s, %s, "%s") ; for the Rect object %s \n' % (
_py2skill_list(obj.layer),
_py2skill_list(_xy, scale=scale),
color,
objname,
) # coloring func. added
elif obj.__class__ == laygo2.object.Path:
# TODO: implement path export function.
pass
elif obj.__class__ == laygo2.object.Pin:
if obj.elements is None:
_objelem = [obj]
else:
_objelem = obj.elements
for idx, _obj in np.ndenumerate(_objelem):
# Invoke _laygo2_generate_pin(cv, name, layer, bbox) in {header_filename}
_xy = mxy + np.dot(_obj.xy, tf.Mt(mtf).T)
return (
'_laygo2_generate_pin(cv, "%s", %s, %s ) ; for the Pin object %s \n'
% (
_obj.netname,
_py2skill_list(_obj.layer),
_py2skill_list(_xy, scale=scale),
objname,
)
)
elif obj.__class__ == laygo2.object.Text:
# TODO: implement text export function.
pass
elif obj.__class__ == laygo2.object.Instance:
# Invoke _laygo2_generate_instance( cv name libname cellname viewname loc orient num_rows num_cols
# sp_rows sp_cols params params_order )
_xy = mxy + np.dot(obj.xy, tf.Mt(mtf).T)
if master is None:
transform = obj.transform
else: # if the translated object has a master (e.g. VirtualInstance)
transform = tf.combine(obj.transform, master.transform)
if obj.shape is None:
num_rows = 1
num_cols = 1
sp_rows = 0
sp_cols = 0
else:
num_rows = obj.shape[1]
num_cols = obj.shape[0]
sp_rows = _py2skill_number(obj.pitch[1])
sp_cols = _py2skill_number(obj.pitch[0])
if obj.params is None:
inst_params = "nil"
else:
inst_params = _py2skill_inst_params(obj.params["pcell_params"])
# inst_params = _py2skill_list([["Wfg", "string", "500n"], ["fingers", "string", "4"], ["l", "string", "500n"]])
return (
'_laygo2_generate_instance(cv, "%s", "%s", "%s", "%s", %s, "%s", %d, %d, %s, %s, %s, %s) '
"; for the Instance object %s \n"
% (
objname,
obj.libname,
obj.cellname,
obj.viewname,
_py2skill_list(_xy),
transform,
num_rows,
num_cols,
sp_rows,
sp_cols,
inst_params,
"nil",
objname,
)
)
elif obj.__class__ == laygo2.object.VirtualInstance:
cmd = ""
if obj.shape is None:
for elem_name, elem in obj.native_elements.items():
if not elem.__class__ == laygo2.object.Pin:
if obj.name == None:
obj.name = "NoName"
else:
pass
cmd += _translate_obj(
obj.name + "_" + elem_name, elem, scale=scale, master=obj
)
else: # arrayed VirtualInstance
for i, j in np.ndindex(tuple(obj.shape.tolist())): # iterate over obj.shape
for elem_name, elem in obj.native_elements.items():
if not elem.__class__ == laygo2.object.Pin:
cmd += _translate_obj(
obj.name + "_" + elem_name + str(i) + "_" + str(j),
elem,
scale=scale,
master=obj[i, j],
)
return cmd
elif obj.__class__ == laygo2.object.Via: # For autovia skill
cmd = ""
center = _py2skill_list(obj.center, scale=scale)
cut = obj.params['cut']
color = obj.color # coloring func. added
layer = _py2skill_list([obj.params['xlayer'],obj.params['ylayer']])
cmd += "_laygo2_generate_autovia(cv,%s, %s, \"%s\", \"%s\", \"%s\") " \
"; for the Via object %s \n" \
% (layer, center, cut, color, tech.name, objname)
return cmd
else:
return obj.translate_to_skill() #
return ""
[docs]
def export(
db, filename=None, cellname=None, scale=1e-3, reset_library=False, tech_library=None
):
"""
Export a laygo2.object.database.Library object to skill code.
Parameters
----------
db: laygo2.database.Library
The library database to exported.
filename: str, optional
If specified, the generated skill script is stored in filename.
cellname: str or List[str]
The name(s) of cell(s) to be exported.
scale: float
The scaling factor between laygo2's integer coordinats actual physical coordinates.
reset_library: bool, optional
If True, the library to export the cells is reset.
tech_library: str, optional
The name of technology library to be attached to the resetted library.
Returns
-------
str: The generated skill script.
Example
--------
>>> import laygo2
>>> from laygo2.object.database import Design
>>> from laygo2.object.physical import Rect, Pin, Instance, Text
>>> # Create a design.
>>> dsn = Design(name="mycell", libname="genlib")
>>> # Create layout objects.
>>> r0 = Rect(xy=[[0, 0], [100, 100]], layer=["M1", "drawing"])
>>> p0 = Pin(xy=[[0, 0], [50, 50]], layer=["M1", "pin"], name="P")
>>> i0 = Instance(libname="tlib", cellname="t0", name="I0", xy=[0, 0])
>>> t0 = Text(xy=[50, 50], layer=["text", "drawing"], text="T")
>>> # Add the layout objects to the design object.
>>> dsn.append(r0)
>>> dsn.append(p0)
>>> dsn.append(i0)
>>> dsn.append(t0)
>>> #
>>> # Export to skill.
>>> lib = laygo2.object.database.Library(name="mylib")
>>> lib.append(dsn)
>>> scr = laygo2.interface.skill.export(lib, filename="myscript.il")
>>> print(scr)
; (definitions of laygo2 skill functions)
; exporting mylib__mycell
cv = _laygo2_open_layout("mylib" "mycell" "layout")
_laygo2_generate_rect(cv, list( "M1" "drawing" ), list( list( 0.0000 0.0000 ) list( 0.1000 0.1000 ) ), "None")
_laygo2_generate_pin(cv, "P", list( "M1" "pin" ), list( list( 0.0000 0.0000 ) list( 0.0500 0.0500 ) ) )
_laygo2_generate_instance(cv, "I0", "tlib", "t0", "layout", list( 0.0000 0.0000 ), "R0", 1, 1, 0, 0, nil, nil)
_laygo2_save_and_close_layout(cv)
"""
# parse header functions.
cmd = "; laygo2 layout export skill script.\n\n"
import os
header_filename = (
os.path.abspath(laygo2.interface.__file__)[:-11] + "skill_export.il"
)
with open(header_filename, "r") as f:
cmd += f.read()
cmd += "\n"
cellname = (
db.keys() if cellname is None else cellname
) # export all cells if cellname is not given.
cellname = (
[cellname] if isinstance(cellname, str) else cellname
) # convert to a list for iteration.
if reset_library:
cmd += '_laygo2_reset_library("%s" "%s")\n' % (db.name, tech_library)
for cn in cellname:
cmd += "\n; exporting %s__%s\n" % (db.name, cn) # open the design.
logging.debug("Export_to_SKILL: Cellname:" + cn)
# cmd += "let( (cv) \n" # limit the scope of cv. Disabled to prevent overflow.
cmd += 'cv = _laygo2_open_layout("%s" "%s" "layout")\n' % (
db.name,
cn,
) # open the design.
# export objects
for objname, obj in db[cn].items():
cmd += _translate_obj(objname, obj, scale=scale)
cmd += "_laygo2_save_and_close_layout(cv)\n" # close the layout
# cmd += ");let\n" Disabled to prevent overflow.
if filename is not None: # export to a file.
with open(filename, "w") as f:
f.write(cmd)
return cmd
[docs]
def load_cell_list(libname, filename, yaml_filename="import_skill_scratch.yaml"):
# parse header functions.
cmd = "; laygo2 cell-list load script.\n\n"
import os
header_filename = (
os.path.abspath(laygo2.interface.__file__)[:-11] + "skill_import.il"
)
with open(header_filename, "r") as f:
cmd += f.read()
cmd += "\n"
cmd += '_laygo2_get_cell_list("%s" "%s")\n' % (libname, yaml_filename)
if filename is not None: # export to a file.
with open(filename, "w") as f:
f.write(cmd)
return cmd
[docs]
def load(libname, mpt=False, cellname=None, filename=None, yaml_filename="import_skill_scratch.yaml"):
mpt = "t" if mpt else "nil"
cmd = "; laygo2 cell load script.\n\n"
cmd += '_laygo2_parse_cad_layout("%s" "%s" "%s" "%s" )\n' %(libname, cellname, yaml_filename, mpt)
#cmd += "_laygo2_parse_cad_layout(\"" + libname + "\" \"" + cn + "\" \"" + yaml_filename + "\" \"" + mpt + "\")"
if filename is not None: # export to a file.
with open(filename, "w") as f:
f.write(cmd)
return cmd