Source code for bag.simulation.base

# 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 high level simulation routines.

This module defines SimAccess, which provides methods to run simulations
and retrieve results.
"""

from typing import Mapping, Any, Tuple, Union, Sequence, Dict, Type

import abc
from pathlib import Path

from pybag.enum import DesignOutput
from pybag.core import get_cdba_name_bits

from ..concurrent.core import SubProcessManager, batch_async_task
from ..util.importlib import import_class
from .data import SimNetlistInfo, SimData


[docs]def get_corner_temp(env_str: str) -> Tuple[str, int]: idx = env_str.rfind('_') if idx < 0: raise ValueError(f'Invalid environment string: {env_str}') return env_str[:idx], int(env_str[idx + 1:].replace('m', '-'))
[docs]def setup_corner(corner_str: str, temp: int) -> str: # Inverse of get_corner_temp # Useful to setup strings that can properly be parsed by it return corner_str + '_' + str(temp).replace('-', 'm')
[docs]def get_bit_list(pin: Union[str, Sequence[str]]) -> Sequence[str]: if isinstance(pin, str): return get_cdba_name_bits(pin) if pin else [] else: return [val for p_ in pin for val in get_cdba_name_bits(p_)]
[docs]class SimAccess(abc.ABC): """A class that interacts with a simulator. Parameters ---------- parent : str parent directory for SimAccess. sim_config : Mapping[str, Any] the simulation configuration dictionary. """ def __init__(self, parent: str, sim_config: Mapping[str, Any]) -> None: self._config = sim_config self._dir_path = (Path(parent) / "simulations").resolve() @property @abc.abstractmethod
[docs] def netlist_type(self) -> DesignOutput: return DesignOutput.CDL
@abc.abstractmethod
[docs] def create_netlist(self, output_path: Path, sch_netlist: Path, info: SimNetlistInfo, precision: int = 6) -> None: pass
@abc.abstractmethod
[docs] def get_sim_file(self, dir_path: Path, sim_tag: str) -> Path: """Returns path to the simulation file.""" pass
@abc.abstractmethod
[docs] def load_sim_data(self, dir_path: Path, sim_tag: str) -> SimData: """Load simulation results. Parameters ---------- dir_path : Path the working directory path. sim_tag : str optional simulation name. Empty for default. Returns ------- data : Dict[str, Any] the simulation data dictionary. """ pass
@abc.abstractmethod
[docs] async def async_run_simulation(self, netlist: Path, sim_tag: str) -> None: """A coroutine for simulation a testbench. Parameters ---------- netlist : Path the netlist file name. sim_tag : str optional simulation name. Empty for default. """ pass
@property
[docs] def dir_path(self) -> Path: """Path: the directory for simulation files.""" return self._dir_path
@property
[docs] def config(self) -> Mapping[str, Any]: """Dict[str, Any]: simulation configurations.""" return self._config
[docs] def run_simulation(self, netlist: Path, sim_tag: str) -> None: coro = self.async_run_simulation(netlist, sim_tag) batch_async_task([coro])
[docs]class SimProcessManager(SimAccess, abc.ABC): """An implementation of :class:`SimAccess` using :class:`SubProcessManager`. Parameters ---------- tmp_dir : str temporary file directory for SimAccess. sim_config : Dict[str, Any] the simulation configuration dictionary. """ def __init__(self, tmp_dir: str, sim_config: Mapping[str, Any]) -> None: SimAccess.__init__(self, tmp_dir, sim_config) mgr_class: Type[SubProcessManager] = import_class(sim_config.get('mgr_class', SubProcessManager)) mgr_kwargs: Dict[str, Any] = sim_config.get('mgr_kwargs', {}) cancel_timeout = sim_config.get('cancel_timeout_ms', 10000) / 1e3 self._manager: SubProcessManager = mgr_class(max_workers=sim_config.get('max_workers', 0), cancel_timeout=cancel_timeout, **mgr_kwargs) @property
[docs] def manager(self) -> SubProcessManager: return self._manager
[docs]class EmSimAccess(abc.ABC): """A class that interacts with an EM simulator. Parameters ---------- parent : str parent directory for EmSimAccess. sim_config : Mapping[str, Any] the simulation configuration dictionary. """ def __init__(self, parent: str, sim_config: Mapping[str, Any]) -> None: self._config = sim_config self._dir_path = (Path(parent) / "em_simulations").resolve() @property
[docs] def dir_path(self) -> Path: """Path: the directory for simulation files.""" return self._dir_path
@property
[docs] def config(self) -> Mapping[str, Any]: """Mapping[str, Any]: simulation configurations.""" return self._config
@staticmethod
[docs] def _get_em_base_path(root_path: Path) -> Path: return root_path.resolve() / 'em_meas'
[docs] def get_log_path(self, root_path: Path) -> Path: """Path: the directory for simulation files.""" return self._get_em_base_path(root_path) / 'bag_em.log'
@abc.abstractmethod
[docs] async def async_gen_nport(self, cell_name: str, gds_file: Path, params: Mapping[str, Any], root_path: Path, run_sim: bool = False) -> Path: """A coroutine for running EM sim to generate nport for the current module. Parameters ---------- cell_name : str Name of the cell gds_file : Path location of the gds file of the cell params : Mapping[str, Any] various EM parameters root_path : Path Root path for running sims and storing results run_sim : bool True to run EM sim; False by default Returns ------- sp_file: Path location of generated s parameter file """ pass
[docs] def run_simulation(self, cell_name: str, gds_file: Path, params: Mapping[str, Any], root_path: Path) -> None: coro = self.async_gen_nport(cell_name, gds_file, params, root_path, run_sim=True) batch_async_task([coro])
@abc.abstractmethod
[docs] def process_output(self, cell_name: str, params: Mapping[str, Any], root_path: Path) -> None: pass
[docs]class EmSimProcessManager(EmSimAccess, abc.ABC): """An implementation of :class:`EmSimAccess` using :class:`SubProcessManager`. Parameters ---------- tmp_dir : str temporary file directory for EmSimAccess. sim_config : Mapping[str, Any] the simulation configuration dictionary. """ def __init__(self, tmp_dir: str, sim_config: Mapping[str, Any]) -> None: EmSimAccess.__init__(self, tmp_dir, sim_config) mgr_class: Type[SubProcessManager] = import_class(sim_config.get('mgr_class', SubProcessManager)) mgr_kwargs: Dict[str, Any] = sim_config.get('mgr_kwargs', {}) cancel_timeout = sim_config.get('cancel_timeout_ms', 10000) / 1e3 self._manager: SubProcessManager = mgr_class(max_workers=sim_config.get('max_workers', 0), cancel_timeout=cancel_timeout, **mgr_kwargs) @property
[docs] def manager(self) -> SubProcessManager: return self._manager