Source code for xbase.layout.esd_static

from typing import Optional, Type, Any, Mapping, Tuple, Sequence
from itertools import chain

from bag.layout.template import TemplateBase, TemplateDB
from bag.layout.util import BlackBoxTemplate
from bag.layout.routing.base import WDictType, SpDictType, TrackManager, TrackID
from bag.design.module import Module
from bag.util.immutable import Param

from pybag.core import BBox
from pybag.enum import Orient2D, Direction, RoundMode, MinLenMode

from ...schematic.esd_static import xbase__esd_static


[docs]class ESDStatic(TemplateBase): """This class instantiates a static ESD layout as a BlackBoxTemplate and then brings up pins on routing grid.""" def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None: TemplateBase.__init__(self, temp_db, params, **kwargs) tr_widths: WDictType = self.params['tr_widths'] tr_spaces: SpDictType = self.params['tr_spaces'] self._tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) self._conn_layer = -1 self._top_layer = -1 @property
[docs] def conn_layer(self) -> int: return self._conn_layer
@property
[docs] def top_layer(self) -> int: return self._top_layer
@property
[docs] def tr_manager(self) -> TrackManager: return self._tr_manager
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]: return xbase__esd_static
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]: return dict( lib_name='The library name.', cell_name='The layout cell name.', tr_widths='Track widths dictionary', tr_spaces='Track spaces dictionary',
)
[docs] def draw_layout(self) -> None: lib_name: str = self.params['lib_name'] cell_name: str = self.params['cell_name'] tr_manager = self._tr_manager # --- Placement --- # esd_info = self.grid.tech_info.tech_params['esd'] self._top_layer = top_layer = esd_info['top_layer'] if self.grid.get_direction(top_layer) is Orient2D.y: self._conn_layer = port_layer = top_layer else: self._conn_layer = port_layer = top_layer - 1 used_port_layer: int = esd_info['used_port_layer'] assert used_port_layer < port_layer, f'top_layer={top_layer} must be greater than ' \ f'used_port_layer={used_port_layer}' config: Mapping[str, Any] = esd_info['types'][cell_name] static_lib: str = config['lib_name'] static_cell: str = config['cell_name'] size: Tuple[int, int] = config['size'] ports: Mapping[str, Mapping[str, Sequence[Tuple[int, int, int, int]]]] = config['ports'] # make master master = self.new_template(BlackBoxTemplate, params=dict(lib_name=static_lib, cell_name=static_cell, top_layer=used_port_layer, size=size, ports=ports)) # add instance inst = self.add_instance(master, inst_name='XINST') bbox = inst.bound_box self.set_size_from_bound_box(port_layer, bbox, round_up=True) # add rectangle arrays, if any rect_arr_list: Sequence[Mapping[str, Any]] = esd_info['rect_arr_list'] for rect_arr in rect_arr_list: edge_margin: Mapping[str, int] = rect_arr.get('edge_margin', {}) rect_xl = bbox.xl + edge_margin.get('xl', 0) rect_xh = bbox.xh + edge_margin.get('xh', 0) rect_yl = bbox.yl + edge_margin.get('yl', 0) rect_yh = bbox.yh + edge_margin.get('yh', 0) spx: int = rect_arr.get('spx', 0) if spx == 0: num_x = 1 else: w_unit: int = rect_arr['w_unit'] num_x = (rect_xh - rect_xl - w_unit) // spx + 1 rect_xh = rect_xl + w_unit spy: int = rect_arr.get('spy', 0) if spy == 0: num_y = 1 else: h_unit: int = rect_arr['h_unit'] num_y = (rect_yh - rect_yl - h_unit) // spy + 1 rect_yh = rect_yl + h_unit lp: Tuple[str, str] = rect_arr['lay_purp'] self.add_rect_array(lp, BBox(rect_xl, rect_yl, rect_xh, rect_yh), num_x, num_y, spx, spy) # --- Routing --- # # First route to (used_port_layer + 1) on RoutingGrid tr_layer = used_port_layer + 1 # using hm for lower layer and vm for higher layer just for convenience. plus_hm: Sequence[BBox] = inst.get_all_port_pins('plus', used_port_layer) minus_hm: Sequence[BBox] = inst.get_all_port_pins('minus', used_port_layer) if cell_name == 'esd_vss': sup_name = 'VDD' sup_idx = 0 minus_idx = 1 plus_idx = 2 elif cell_name == 'esd_vdd': sup_name = 'VSS' sup_idx = 2 minus_idx = 0 plus_idx = 1 else: raise ValueError(f'Unknown cell_name={cell_name}. Use "esd_vdd" or "esd_vss".') sup_hm: Sequence[BBox] = inst.get_all_port_pins(sup_name, used_port_layer) hm_lp = self.grid.tech_info.get_lay_purp_list(used_port_layer)[0] # put pins on (used_port_layer + 1) on RoutingGrid if self.grid.get_direction(used_port_layer) is Orient2D.y: hm_lower = max([bbox.yl for bbox in chain(plus_hm, minus_hm, sup_hm)]) hm_upper = min([bbox.yh for bbox in chain(plus_hm, minus_hm, sup_hm)]) else: # Orient2D.x hm_lower = max([bbox.xl for bbox in chain(plus_hm, minus_hm, sup_hm)]) hm_upper = min([bbox.xh for bbox in chain(plus_hm, minus_hm, sup_hm)]) vm_l_idx = self.grid.coord_to_track(tr_layer, hm_lower, RoundMode.GREATER_EQ) vm_r_idx = self.grid.coord_to_track(tr_layer, hm_upper, RoundMode.LESS_EQ) vm_num = tr_manager.get_num_wires_between(tr_layer, 'sup', vm_l_idx, 'sup', vm_r_idx, 'sup') + 2 if vm_num < 3: raise ValueError(f'Redo routing on layer={tr_layer}') _n = vm_num // 3 vm_idx_list = tr_manager.spread_wires(tr_layer, ['sup', 'sup', 'sup'] * _n, vm_l_idx, vm_r_idx, ('sup', 'sup')) _p = (vm_idx_list[1] - vm_idx_list[0]) * 3 w_sup_vm = tr_manager.get_width(tr_layer, 'sup') sup_vm_tid = TrackID(tr_layer, vm_idx_list[sup_idx], w_sup_vm, _n, _p) sup_vm = [] for idx, bbox in enumerate(sup_hm): sup_vm.append(self.connect_bbox_to_tracks(Direction.LOWER, hm_lp, bbox, sup_vm_tid, min_len_mode=MinLenMode.MIDDLE)) plus_vm_tid = TrackID(tr_layer, vm_idx_list[plus_idx], w_sup_vm, _n, _p) plus_vm = [] for bbox in plus_hm: plus_vm.append(self.connect_bbox_to_tracks(Direction.LOWER, hm_lp, bbox, plus_vm_tid, min_len_mode=MinLenMode.MIDDLE)) minus_vm_tid = TrackID(tr_layer, vm_idx_list[minus_idx], w_sup_vm, _n, _p) minus_vm = [] for bbox in minus_hm: minus_vm.append(self.connect_bbox_to_tracks(Direction.LOWER, hm_lp, bbox, minus_vm_tid, min_len_mode=MinLenMode.MIDDLE)) sup_port = self.connect_wires(sup_vm)[0] plus_port = self.connect_wires(plus_vm)[0] minus_port = self.connect_wires(minus_vm)[0] # route up to port_layer if port_layer > tr_layer: for _layer in range(tr_layer + 1, port_layer + 1): _lower = min([warr.lower for warr in [sup_port, plus_port, minus_port]]) _upper = max([warr.upper for warr in [sup_port, plus_port, minus_port]]) _l_idx = self.grid.coord_to_track(_layer, _lower, RoundMode.GREATER_EQ) _r_idx = self.grid.coord_to_track(_layer, _upper, RoundMode.LESS_EQ) _num = tr_manager.get_num_wires_between(_layer, 'sup', _l_idx, 'sup', _r_idx, 'sup') + 2 if _num < 3: raise ValueError(f'Redo routing on layer={_layer}') _n = _num // 3 _idx_list = tr_manager.spread_wires(_layer, ['sup', 'sup', 'sup'] * _n, _l_idx, _r_idx, ('sup', 'sup')) _p = (_idx_list[1] - _idx_list[0]) * 3 w_sup = tr_manager.get_width(_layer, 'sup') if cell_name == 'esd_vdd': sup_idx = 2 plus_idx = 1 minus_idx = 0 else: # cell_name == 'esd_vss' if self.grid.get_direction(_layer) is Orient2D.y: sup_idx = 0 plus_idx = 2 minus_idx = 1 else: sup_idx = 2 plus_idx = 0 minus_idx = 1 sup_tid = TrackID(_layer, _idx_list[sup_idx], w_sup, _n, _p) sup_port = self.connect_to_tracks(sup_port, sup_tid, min_len_mode=MinLenMode.MIDDLE) plus_tid = TrackID(_layer, _idx_list[plus_idx], w_sup, _n, _p) plus_port = self.connect_to_tracks(plus_port, plus_tid, min_len_mode=MinLenMode.MIDDLE) minus_tid = TrackID(_layer, _idx_list[minus_idx], w_sup, _n, _p) minus_port = self.connect_to_tracks(minus_port, minus_tid, min_len_mode=MinLenMode.MIDDLE) self.add_pin(sup_name, sup_port) self.add_pin('plus', plus_port) self.add_pin('minus', minus_port) # add hidden pins on unconnected used_port_layer wires for collision checking in top level cells nc_hm: Sequence[BBox] = inst.get_all_port_pins('NC', used_port_layer) for bbox in nc_hm: self.add_rect(hm_lp, bbox) self.add_pin_primitive('NC', f'{used_port_layer}', bbox, hide=True) # setup schematic parameters self.sch_params = dict( lib_name=lib_name, cell_name=cell_name,
)