Source code for xbase.layout.array.tech

# 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.

"""This module defines a technology base class for generating device arrays
"""

from __future__ import annotations

from typing import Tuple, Any, List, Mapping, Optional, Dict

import abc

from pybag.core import COORD_MAX

from bag.util.math import HalfInt
from bag.util.immutable import ImmutableSortedDict
from bag.util.search import BinaryIterator
from bag.layout.tech import TechInfo
from bag.layout.routing.base import TrackManager
from bag.layout.routing.grid import TrackSpec

from ..enum import Alignment, ExtendMode
from ..wires import WireSpecs, WireLookup
from ..data import LayoutInfo, CornerLayInfo
from .data import ArrayLayInfo, ArrayEndInfo

[docs]WireDictType = ImmutableSortedDict[Tuple[str, int], Tuple[HalfInt, str]]
[docs]class ArrayTech(abc.ABC): def __init__(self, tech_info: TechInfo, dev_name: str, **kwargs: Any) -> None: self._tech_info = tech_info kwargs['dev_name'] = dev_name self._kwargs = kwargs @property @abc.abstractmethod
[docs] def min_size(self) -> Tuple[int, int]: return 1, 1
@property @abc.abstractmethod
[docs] def blk_pitch(self) -> Tuple[int, int]: return 1, 1
@property @abc.abstractmethod
[docs] def conn_layer(self) -> int: return 1
@abc.abstractmethod
[docs] def get_track_specs(self, conn_layer: int, top_layer: int) -> List[TrackSpec]: raise NotImplementedError('Not implemented.')
@abc.abstractmethod
[docs] def get_edge_width(self, info: ImmutableSortedDict[str, Any], arr_dim: int, blk_pitch: int ) -> int: raise NotImplementedError('Not implemented.')
@abc.abstractmethod
[docs] def get_end_height(self, info: ImmutableSortedDict[str, Any], arr_dim: int, blk_pitch: int ) -> int: raise NotImplementedError('Not implemented.')
@abc.abstractmethod
[docs] def get_blk_info(self, conn_layer: int, w: int, h: int, nx: int, ny: int, **kwargs: Any ) -> Optional[ArrayLayInfo]: raise NotImplementedError('Not implemented.')
@abc.abstractmethod
[docs] def get_edge_info(self, w: int, h: int, info: ImmutableSortedDict[str, Any], **kwargs: Any ) -> LayoutInfo: raise NotImplementedError('Not implemented.')
@abc.abstractmethod
[docs] def get_end_info(self, w: int, h: int, info: ImmutableSortedDict[str, Any], **kwargs: Any ) -> ArrayEndInfo: raise NotImplementedError('Not implemented.')
@abc.abstractmethod
[docs] def get_corner_info(self, w: int, h: int, info: ImmutableSortedDict[str, Any], **kwargs: Any ) -> CornerLayInfo: raise NotImplementedError('Not implemented.')
@property
[docs] def desc(self) -> str: name = self.__class__.__name__ try: idx = name.find('Tech') return name[:idx] except ValueError: return 'Array'
@property
[docs] def tech_info(self) -> TechInfo: return self._tech_info
@property
[docs] def tech_kwargs(self) -> Mapping[str, Any]: return self._kwargs
[docs] def size_unit_block(self, conn_layer: int, top_layer: int, nx: int, ny: int, tr_manager: TrackManager, wire_specs: Mapping[int, Any], mode: ExtendMode, max_ext: int = 1000, **kwargs: Any ) -> Tuple[int, int, Dict[int, WireLookup], ArrayLayInfo]: wire_specs = WireSpecs.make_wire_specs(conn_layer, top_layer, tr_manager, wire_specs, min_size=self.min_size, blk_pitch=self.blk_pitch, align_default=Alignment.CENTER_COMPACT) blk_info: Optional[ArrayLayInfo] = None w_min, h_min = wire_specs.min_size blk_w, blk_h = wire_specs.blk_size w = w_min h = h_min opt_area = COORD_MAX ** 2 def helper_fun(w_test: int, h_test: int, binfo: Optional[ArrayLayInfo], wc: int, hc: int, opt_a: int, iterator: BinaryIterator ) -> Tuple[Optional[ArrayLayInfo], int, int, int]: cur_area = w_test * h_test if cur_area >= opt_a: # this point can't beat current optimum iterator.down() return binfo, wc, hc, opt_a else: cur_info = self.get_blk_info(conn_layer, w_test, h_test, nx, ny, **kwargs) if cur_info is None: iterator.up() return binfo, wc, hc, opt_a else: # found new optimum iterator.down() return cur_info, w_test, h_test, cur_area if mode is ExtendMode.AREA: # extend in both direction # linear search in height, binary search in width # in this way, for same area, use height as tie breaker for h_cur in range(h_min, h_min + max_ext * blk_h, blk_h): if w_min * h_cur >= opt_area: # terminate linear search break bin_iter = BinaryIterator(w_min, w_min + max_ext * blk_w, step=blk_w) while bin_iter.has_next(): w_cur = bin_iter.get_next() blk_info, w, h, opt_area = helper_fun(w_cur, h_cur, blk_info, w, h, opt_area, bin_iter) elif mode is ExtendMode.X: h_cur = h_min bin_iter = BinaryIterator(w_min, w_min + max_ext * blk_w, step=blk_w) while bin_iter.has_next(): w_cur = bin_iter.get_next() blk_info, w, h, opt_area = helper_fun(w_cur, h_cur, blk_info, w, h, opt_area, bin_iter) else: w_cur = w_min bin_iter = BinaryIterator(h_min, h_min + max_ext * blk_h, step=blk_h) while bin_iter.has_next(): h_cur = bin_iter.get_next() blk_info, w, h, opt_area = helper_fun(w_cur, h_cur, blk_info, w, h, opt_area, bin_iter) if blk_info is None: raise ValueError(f'Failed to find legal resistor unit block size ' f'with max_ext={max_ext}') return w, h, wire_specs.place_wires(tr_manager, w, h), blk_info