# SPDX-License-Identifier: BSD-3-Clause AND Apache-2.0
# Copyright 2018 Regents of the University of California
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * 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.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# 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.
# Copyright 2019 Blue Cheetah Analog Design Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This module handles exporting schematic/layout from Virtuoso.
"""
from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Dict, Any, List, Tuple, Union
import os
import shutil
from abc import ABC
from pathlib import Path
from jinja2 import Template
from pybag.enum import DesignOutput
from ..io import write_file
from ..io.template import new_template_env_fs
from ..env import get_bag_work_dir, get_gds_layer_map, get_gds_object_map
from .base import SubProcessChecker, get_flow_config
if TYPE_CHECKING:
from .base import ProcInfo, FlowInfo
[docs]class VirtuosoChecker(SubProcessChecker, ABC):
"""the base Checker class for Virtuoso.
This class implement layout/schematic export and import procedures.
Parameters
----------
tmp_dir : str
temporary directory to save files in.
root_dir : Dict[str, str]
dictionary of root run directories.
template : Dict[str, str]
dictionary of SVRF jinja template files.
env_vars: Dict[str, Dict[str, str]]
dictionary of environment variables.
params : Dict[str, Dict[str, Any]]
dictionary of default flow parameters.
max_workers : int
maximum number of sub-processes BAG can launch.
source_added_file : str
the Calibre source.added file location. Environment variable is supported.
If empty (default), this is not configured.
import_ref_lib : str
the import reference libraries list file location. Environment variable is supported.
If empty (default), this is not configured.
cancel_timeout_ms : int
cancel timeout in milliseconds.
enable_color : bool
True to enable coloring in GDS export.
"""
def __init__(self, tmp_dir: str, root_dir: Dict[str, str], template: Dict[str, str],
env_vars: Dict[str, Dict[str, str]], link_files: Dict[str, List[str]],
params: Dict[str, Dict[str, Any]], max_workers: int = 0,
source_added_file: str = '', import_ref_lib: str = '', cancel_timeout_ms: int = 10000,
enable_color: bool = False, **kwargs: Dict[str, Any]) -> None:
cancel_timeout = cancel_timeout_ms / 1e3
SubProcessChecker.__init__(self, tmp_dir, max_workers, cancel_timeout, **kwargs)
self._flow_config = get_flow_config(root_dir, template, env_vars, link_files, params)
self._source_added_file = source_added_file
self._bag_work_dir = get_bag_work_dir()
self._strm_params = dict(enable_color=enable_color)
self._temp_env_ctrl = new_template_env_fs()
self._import_ref_lib = import_ref_lib
@property
[docs] def bag_work_dir(self) -> str:
return self._bag_work_dir
[docs] def get_config(self, mode: str) -> Dict[str, Any]:
return self._flow_config[mode]
[docs] def get_control_template(self, mode: str) -> Template:
template: str = self.get_config(mode)['template']
return self._temp_env_ctrl.get_template(template)
[docs] def setup_job(self, mode: str, lib_name: str, cell_name: str,
layout: Optional[str], netlist: Optional[str], lay_view: str, sch_view: str,
user_params: Optional[Dict[str, Any]], run_dir_override: Union[str, Path]
) -> Tuple[List[FlowInfo], Path, Optional[Dict[str, str]],
Dict[str, Any], Dict[str, Any]]:
config = self.get_config(mode)
root_dir: Path = config['root_dir']
link_files: List[Tuple[Path, Path]] = config['link_files']
env_vars: Dict[str, str] = config['env_vars']
params: Dict[str, Any] = config['params']
if isinstance(run_dir_override, str):
if run_dir_override:
run_dir = Path(run_dir_override).resolve()
else:
run_dir = root_dir.joinpath(lib_name, cell_name)
else:
run_dir = run_dir_override.resolve()
run_dir.mkdir(parents=True, exist_ok=True)
for fpath, basename in link_files:
link = run_dir / basename
if not link.exists():
link.symlink_to(fpath)
flow_list = []
sch_path = run_dir / 'netlist.cdl'
ctl_params = dict(cell_name=cell_name)
if layout is not None:
if layout:
ext = Path(layout).suffix[1:]
lay_path = run_dir / f'layout.{ext}'
shutil.copy(layout, str(lay_path))
else:
ext = 'gds'
lay_path = run_dir / 'layout.gds'
info = self.setup_export_layout(lib_name, cell_name, str(lay_path), lay_view)
flow_list.append((info[0], info[1], info[2], info[3], all_pass_callback))
if ext == DesignOutput.GDS.extension:
ctl_params['layout_type'] = 'GDSII'
elif ext == DesignOutput.OASIS.extension:
ctl_params['layout_type'] = 'OASIS'
else:
raise ValueError(f'Cannot determine layout type from layout file name: {lay_path}')
ctl_params['layout_file'] = str(lay_path)
if netlist is not None:
if netlist:
shutil.copy(netlist, str(sch_path))
else:
info = self.setup_export_schematic(lib_name, cell_name, str(sch_path), sch_view)
flow_list.append((info[0], info[1], info[2], info[3], all_pass_callback))
ctl_params['netlist_file'] = str(sch_path)
params_actual = params.copy()
if user_params is not None:
params_actual.update(user_params)
ctl_params.update(params_actual)
if env_vars:
run_env = dict(**os.environ)
run_env.update(env_vars)
else:
run_env = None
return flow_list, run_dir, run_env, params_actual, ctl_params
[docs] def setup_export_layout(self, lib_name: str, cell_name: str, out_file: str,
view_name: str = 'layout', params: Optional[Dict[str, Any]] = None
) -> ProcInfo:
if params is None:
params = {}
enable_color: bool = params.get('enable_color',
self._strm_params.get('enable_color', False))
square_bracket: bool = params.get('square_bracket', False)
out_path = Path(out_file).resolve()
run_dir = out_path.parent
out_name = out_path.name
output_type: DesignOutput = params.get('output_type')
if not output_type:
# figure out output_type from out_file extension
if out_path.suffix.lower() == '.gds':
output_type = DesignOutput.GDS
elif out_path.suffix.lower() in ['.oas', '.oasis']:
output_type = DesignOutput.OASIS
else:
raise ValueError(f'Unknown layout export format: {out_path.suffix}')
if output_type is DesignOutput.GDS:
template_name = 'gds_export_config.txt'
cmd_str = 'strmout'
elif output_type is DesignOutput.OASIS:
template_name = 'oasis_export_config.txt'
cmd_str = 'oasisout'
else:
raise ValueError(f'Unknown layout export format: {output_type.name}')
log_file = str(run_dir / 'layout_export.log')
run_dir.mkdir(parents=True, exist_ok=True)
# fill in stream out configuration file.
content = self.render_file_template(template_name,
dict(lib_name=lib_name,
cell_name=cell_name,
view_name=view_name,
output_name=out_name,
run_dir=str(run_dir),
enable_color=str(enable_color).lower(),
square_bracket=str(square_bracket).lower(),
))
# run strmOut or oasisOut
ctrl_file = run_dir / 'stream_template'
write_file(ctrl_file, content)
cmd = [cmd_str, '-templateFile', str(ctrl_file)]
return cmd, log_file, None, self._bag_work_dir
[docs] def setup_import_layout(self, in_file: str, lib_name: str, cell_name: str,
view_name: str = 'layout', params: Optional[Dict[str, Any]] = None
) -> ProcInfo:
if params is None:
params = {}
enable_color: bool = params.get('enable_color',
self._strm_params.get('enable_color', False))
square_bracket: bool = params.get('square_bracket', False)
in_path = Path(in_file).resolve()
run_dir = in_path.parent
in_name = in_path.name
input_type: DesignOutput = params.get('input_type')
if not input_type:
# figure out input_type from in_file extension
if in_path.suffix.lower() == '.gds':
input_type = DesignOutput.GDS
elif in_path.suffix.lower() in ['.oas', '.oasis']:
input_type = DesignOutput.OASIS
else:
raise ValueError(f'Unknown layout import format: {in_path.suffix}')
if input_type is DesignOutput.GDS:
template_name = 'gds_import_config.txt'
cmd_str = 'strmin'
elif input_type is DesignOutput.OASIS:
template_name = 'oasis_import_config.txt'
cmd_str = 'oasisin'
else:
raise ValueError(f'Unknown layout import format: {input_type.name}')
log_file = str(run_dir / 'layout_import.log')
run_dir.mkdir(parents=True, exist_ok=True)
# fill in stream in configuration file.
content = self.render_file_template(template_name,
dict(lib_name=lib_name,
cell_name=cell_name,
view_name=view_name,
input_name=in_name,
run_dir=str(run_dir),
enable_color=str(enable_color).lower(),
square_bracket=str(square_bracket).lower(),
import_ref_lib=self._import_ref_lib,
layer_map=get_gds_layer_map(),
object_map=get_gds_object_map(),
))
# run strmIn or oasisIn
ctrl_file = run_dir / 'stream_template'
write_file(ctrl_file, content)
cmd = [cmd_str, '-templateFile', str(ctrl_file)]
return cmd, log_file, None, self._bag_work_dir
[docs] def setup_export_schematic(self, lib_name: str, cell_name: str, out_file: str,
view_name: str = 'schematic',
params: Optional[Dict[str, Any]] = None) -> ProcInfo:
out_path = Path(out_file).resolve()
run_dir = out_path.parent
out_name = out_path.name
log_file = str(run_dir / 'schematic_export.log')
run_dir.mkdir(parents=True, exist_ok=True)
# fill in stream out configuration file.
content = self.render_file_template('si_env.txt',
dict(
lib_name=lib_name,
cell_name=cell_name,
view_name=view_name,
output_name=out_name,
source_added_file=self._source_added_file,
run_dir=run_dir,
))
# run command
write_file(run_dir / 'si.env', content)
cmd = ['si', str(run_dir), '-batch', '-command', 'netlist']
return cmd, log_file, None, self._bag_work_dir
# noinspection PyUnusedLocal
[docs]def all_pass_callback(retcode: int, log_file: str) -> bool:
return True