Source code for bag3_testbenches.measurement.digital.flop.timing

# 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 typing import Any, Union, Tuple, Set, Mapping, Dict, Type, Sequence, cast

import numpy as np

from bag.simulation.core import TestbenchManager
from bag.simulation.cache import SimulationDB, DesignInstance, SimResults
from bag.simulation.measure import MeasInfo

from ...data.tran import EdgeType
from ...search import IntervalSearchMM
from ..util import setup_digital_tran
from .base import FlopMeasMode, FlopTimingBase


[docs]class FlopConstraintTimingMM(IntervalSearchMM): """Measures setup/hold/recovery/removal time. Notes ----- specification dictionary has the following entries in addition to IntervalSearchMM: meas_mode : Union[str, FlopMeasMode] the measurement mode. flop_params : Mapping[str, Any] flop parameters. delay_thres : float Defaults to 0.05. Percent increase in delay for setup/hold constraints. Use infinity to disable. At least one of delay_thres or delay_inc must be specified. If both are given, both constraints will be satisfied. delay_inc : float Defaults to infinity. Increase in delay in seconds for setup/hold constraints. At least one delay_thres or delay_inc must be specified. If both are given, both constraints will be satisfied. constraint_min : float Defaults to negative infinity. If the timing constraint is less than this value, return this value instead. Used to speed up simulation. sim_env_name : str Use to query for sim_env dependent timing offset. tbm_cls : Union[str, Type[FlopTimingBase]] The testbench class. tbm_specs : Mapping[str, Any] TestbenchManager specifications. search_params : Mapping[str, Any] interval search parameters, with the following entries: max_margin : float Optional. maximum timing margin in seconds. Defaults to t_clk_per/4. tol : float tolerance of the binary search. Terminate the search when it is below this value. overhead_factor : float ratio of simulation startup time to time it takes to simulate one sweep point. fake: bool Defaults to False. True to output fake data for debugging. use_dut : bool Defaults to True. True to instantiate DUT. wrapper_params : Mapping[str, Any] Used only if simulated with a DUT wrapper. Contains the following entries: lib : str wrapper library name. cell : str wrapper cell name. params : Mapping[str, Any] DUT wrapper schematic parameters. pins : Sequence[str] wrapper pin list. power_domain : Mapping[str, Tuple[str, str] power domain of wrapper. """ def __init__(self, *args: Any, **kwargs: Any) -> None: self._delay_targ: Dict[Tuple[str, str], float] = {} self._output_map: Mapping[str, Tuple[Mapping[str, Any], Sequence[Tuple[EdgeType, Sequence[str]]]]] = {} super().__init__(*args, **kwargs)
[docs] def get_init_result(self, adj_name: str) -> Dict[str, Any]: specs = self.specs fake: bool = specs.get('fake', False) if fake: neg_inf = float('-inf') constraint_min: float = specs.get('constraint_min', neg_inf) value = constraint_min if constraint_min != neg_inf else 40.0e-12 timing_info = self._output_map[adj_name][0] return dict(value=value + timing_info['offset'], margin=0.0, delay=20.0e-12, timing_info=timing_info) return {}
[docs] def process_init(self, cur_info: MeasInfo, sim_results: SimResults ) -> Tuple[Dict[str, Any], bool]: specs = self.specs delay_thres: float = specs.get('delay_thres', 0.05) delay_inc: float = specs.get('delay_inc', float('inf')) tbm = cast(FlopTimingBase, sim_results.tbm) data = sim_results.data self._delay_targ.clear() result = {} for var_name, (timing_info, edge_out_list) in self._output_map.items(): if timing_info['inc_delay']: scale = 1 + delay_thres inc = delay_inc pick_fun = min else: scale = 1 - delay_thres inc = -delay_inc pick_fun = max cur_results = {} for edge, out_list in edge_out_list: for out_pin in out_list: delay = tbm.calc_clk_to_q(data, out_pin, edge).item() if delay == float('inf'): raise ValueError('Cannot measure reference delay.') cur_results[out_pin] = (edge.name, delay) delay_targ = pick_fun(scale * delay, delay + inc) self._delay_targ[(var_name, out_pin)] = delay_targ self.log(f'var={var_name}, out={out_pin}, ' f'delay = {delay:.4g}, delay_targ = {delay_targ:.4g}') result[var_name] = dict(delay=cur_results, timing_info=timing_info) self.log_result(cur_info.state, result) return result, False
[docs] def process_output_helper(self, cur_info: MeasInfo, sim_results: SimResults, remaining: Set[str]) -> Mapping[str, Tuple[Tuple[float, float], Dict[str, Any], bool]]: prev_results = cur_info.prev_results tbm = cast(FlopTimingBase, sim_results.tbm) sim_data = sim_results.data info_table = {} meas_mode = tbm.meas_mode is_removal = meas_mode.is_removal for var_name in remaining: cur_result = prev_results[var_name] if var_name in sim_data: adj_values = sim_data[var_name] else: adj_values = np.array([tbm.sim_params[var_name]]) timing_info, edge_out_list = self._output_map[var_name] cons_offset: float = timing_info['offset'] if is_removal: # NOTE: for removal test, we want to make sure clk-to-q delay is infinite for all # outputs. adj_sign = True diff = np.full(adj_values.shape, True) for edge, out_list in edge_out_list: for out_pin in out_list: # NOTE: remove corners arr = tbm.calc_clk_to_q(sim_data, out_pin, edge)[0, ...] diff = np.logical_and(diff, np.isinf(arr)) diff = 2 * diff.astype(int) - 1 else: adj_sign = timing_info['inc_delay'] diff = np.full(adj_values.shape, np.inf) for edge, out_list in edge_out_list: for out_pin in out_list: # NOTE: remove corners arr = tbm.calc_clk_to_q(sim_data, out_pin, edge)[0, ...] targ = self._delay_targ[(var_name, out_pin)] diff = np.minimum(diff, targ - arr) # find sign change res_idx, accept, low, high = self.get_adj_interval(var_name, adj_sign, adj_values, diff) cur_result['value'] = adj_values[res_idx] + cons_offset cur_result['margin'] = diff[res_idx] info_table[var_name] = ((low, high), cur_result, accept) self.log_result(cur_info.state, info_table) return info_table