# SPDX-License-Identifier: Apache-2.0
# 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.
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Tuple, Mapping, Union, Optional, Type, cast, Sequence
import abc
from pathlib import Path
from copy import deepcopy
from dataclasses import dataclass
from pybag.enum import LogLevel
from ..util.logging import LoggingBase
from ..util.importlib import import_class
from ..io.file import write_yaml
from ..concurrent.core import batch_async_task
from .core import TestbenchManager
if TYPE_CHECKING:
from .cache import SimulationDB, DesignInstance, SimResults, MeasureResult
@dataclass
[docs]class MeasInfo:
[docs] prev_results: Mapping[str, Any]
[docs]class MeasurementManager(LoggingBase, abc.ABC):
"""A class that handles circuit performance measurement.
This class handles all the steps needed to measure a specific performance
metric of the device-under-test. This may involve creating and simulating
multiple different testbenches, where configuration of successive testbenches
depends on previous simulation results. This class reduces the potentially
complex measurement tasks into a few simple abstract methods that designers
simply have to implement.
"""
def __init__(self, meas_specs: Mapping[str, Any], log_file: str,
log_level: LogLevel = LogLevel.DEBUG, precision: int = 6) -> None:
LoggingBase.__init__(self, self.__class__.__name__, log_file, log_level=log_level)
self._specs: Mapping[str, Any] = {k: deepcopy(v) for k, v in meas_specs.items()}
self._precision = precision
self.commit()
@property
[docs] def specs(self) -> Mapping[str, Any]:
return self._specs
@property
[docs] def precision(self) -> int:
return self._precision
[docs] def commit(self) -> None:
"""Commit changes to specs dictionary. Perform necessary initialization."""
pass
[docs] def make_tbm(self, tbm_cls: Union[Type[TestbenchManager], str], tbm_specs: Mapping[str, Any],
) -> TestbenchManager:
obj_cls = cast(Type[TestbenchManager], import_class(tbm_cls))
return obj_cls(None, Path(), '', '', tbm_specs, None, None,
precision=self._precision, logger=self.logger)
[docs] def make_mm(self, mm_cls: Union[Type[MeasurementManager], str], mm_specs: Mapping[str, Any]
) -> MeasurementManager:
obj_cls = cast(Type[MeasurementManager], import_class(mm_cls))
return obj_cls(mm_specs, self.log_file, log_level=self.log_level, precision=self._precision)
@abc.abstractmethod
[docs]class MeasurementManagerFSM(MeasurementManager, abc.ABC):
"""A class that handles circuit performance measurement in an FSM-like fashion.
Any subclass of MeasurementManagerFSM will need to implement the following methods:
initialize, process_output, get_sim_info.
Refer to async_measure_performance to see how the above methods are integrated together into the measurement process.
"""
@abc.abstractmethod
[docs] def initialize(self, sim_db: SimulationDB, dut: DesignInstance,
harnesses: Optional[Sequence[DesignInstance]] = None) -> Tuple[bool, MeasInfo]:
"""Initialize this MeasurementManager to get ready for measurement.
Parameters
----------
sim_db : SimulationDB
the simulation database object.
dut : DesignInstance
the design instance.
harnesses : Optional[Sequence[DesignInstance]]
the list of harness instances.
Returns
-------
done : bool
If True, then do not run measurement.
info : MeasInfo
the initial MeasInfo object.
"""
pass
@abc.abstractmethod
[docs] def process_output(self, cur_info: MeasInfo, sim_results: Union[SimResults, MeasureResult]
) -> Tuple[bool, MeasInfo]:
"""Process simulation output data.
Parameters
----------
cur_info : MeasInfo
the MeasInfo object representing the current measurement state.
sim_results : Union[SimResults, MeasureResult]
the simulation results object.
Returns
-------
done : bool
True if this measurement is finished.
next_info : MeasInfo
the updated measurement state.
"""
pass
@abc.abstractmethod
[docs] def get_sim_info(self, sim_db: SimulationDB, dut: DesignInstance, cur_info: MeasInfo,
harnesses: Optional[Sequence[DesignInstance]] = None
) -> Tuple[Union[Tuple[TestbenchManager, Mapping[str, Any]],
MeasurementManager], bool]:
"""Get the testbench manager needed for the current measurement state.
Override to customize your testbench manager.
Parameters
----------
sim_db : SimulationDB
the simulation database object.
dut : DesignInstance
the design instance.
cur_info: MeasInfo
the MeasInfo object representing the current measurement state.
harnesses : Optional[Sequence[DesignInstance]]
the list of harness instances
Returns
-------
sim_object : Union[Tuple[TestbenchManager, Mapping[str, Any]], MeasurementManager]
either a TestbenchManager/tb_params tuple, or a measurement manager instance.
use_dut : bool
True to run simulation with DesignInstance.
"""
pass