Source code for bag3_digital.layout.stdcells._flop_scan_rst

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

"""Layout generator for a scannable, resettable flop."""

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

from pybag.enum import MinLenMode, RoundMode

from bag.util.math import HalfInt
from bag.util.immutable import Param
from bag.design.database import ModuleDB
from bag.design.module import Module
from bag.layout.routing.base import TrackID
from bag.layout.template import TemplateDB
from xbase.layout.enum import MOSWireType, MOSType
from xbase.layout.mos.base import MOSBasePlaceInfo, MOSBase


[docs]class FlopScanRstlbTwoTile(MOSBase): """A scannable, resettable flop""" def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None: MOSBase.__init__(self, temp_db, params, **kwargs) self._substrate_row_intvl: List[Tuple[int, int]] = [] @property
[docs] def substrate_row_intvl(self) -> List[Tuple[int, int]]: return self._substrate_row_intvl
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]: # noinspection PyTypeChecker return ModuleDB.get_schematic_class('bag3_digital', 'flop_scan_rstlb')
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( pinfo='The MOSBasePlaceInfo object.', seg_dict='segments dictionary.', w_dict='width dictionary.', ridx_p='pmos row index.', ridx_n='nmos row index.', sig_locs='Signal location dictionary.', vertical_rst='True to have rst on vm_layer', substrate_row='True to have dedicated substrate row.', tile0='Tile index of logic tile 0', tile1='Tile index of logic tile 1', flip_tile='True to flip tiles.',
) @classmethod
[docs] def get_default_param_values(cls) -> Dict[str, Any]: return dict( seg_dict={}, w_dict={}, ridx_p=-1, ridx_n=0, sig_locs={}, vertical_rst=False, substrate_row=False, tile0=0, tile1=1, flip_tile=False,
)
[docs] def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) flip_tile: bool = self.params['flip_tile'] self.draw_base(pinfo, flip_tile=flip_tile) seg_dict: Dict[str, int] = self.params['seg_dict'] w_dict: Dict[str, int] = self.params['w_dict'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] sig_locs: Mapping[str, Union[float, HalfInt]] = self.params['sig_locs'] vertical_rst: bool = self.params['vertical_rst'] substrate_row: bool = self.params['substrate_row'] min_sep = self.min_sep_col seg_buf = seg_dict.get('buf', 1) seg_in = seg_dict.get('in', 1) seg_mux = seg_dict.get('mux', 1) seg_keep = seg_dict.get('keep', 1) seg_pass = seg_dict.get('pass', 1) seg_rst = seg_dict.get('rst', 1) seg_out = seg_dict.get('out', 1) tile0 = self.params['tile0'] tile1 = self.params['tile1'] if substrate_row: vss0_tid = None vdd_tid = None vss1_tid = None else: vss0_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=tile0) vdd_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sup') vss1_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=tile1) place_info = self.get_tile_pinfo(tile0) default_wn = place_info.get_row_place_info(ridx_n).row_info.width default_wp = place_info.get_row_place_info(ridx_p).row_info.width wp_buf = w_dict.get('p_buf', default_wp) wn_buf = w_dict.get('n_buf', default_wn) wp_in = w_dict.get('p_in', default_wp) wn_in = w_dict.get('n_in', default_wn) wp_mux = w_dict.get('p_mux', default_wp) wn_mux = w_dict.get('n_mux', default_wn) wp_keep = w_dict.get('p_keep', default_wp) wn_keep = w_dict.get('n_keep', default_wn) wp_pass = w_dict.get('p_pass', default_wp) wn_pass = w_dict.get('n_pass', default_wn) wp_rst = w_dict.get('p_rst', default_wp) wn_rst = w_dict.get('n_rst', default_wn) wp_out = w_dict.get('p_out', default_wp) wn_out = w_dict.get('n_out', default_wn) # tracks t0_nd0_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile0) t0_nd1_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile0) t0_ng0_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile0) t0_ng1_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile0) t0_pg0_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile0) t0_pg1_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile0) t0_pd0_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile0) t0_pd1_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile0) t1_pd1_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile1) t1_pd0_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile1) t1_pg1_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile1) t1_pg0_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile1) t1_ng1_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile1) t1_ng0_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile1) t1_nd1_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile1) t1_nd0_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile1) # placement # tile 0 cur_col = 0 mos_in_n = self.add_mos(ridx_n, cur_col, seg_in, w=wn_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) mos_in_p = self.add_mos(ridx_p, cur_col, seg_in, w=wp_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) cur_col += 2 * seg_in + min_sep mos_keep_n = self.add_mos(ridx_n, cur_col, seg_keep, w=wn_keep, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) mos_keep_p = self.add_mos(ridx_p, cur_col, seg_keep, w=wp_keep, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) cur_col += 2 * seg_keep + min_sep mos_pass_n = self.add_mos(ridx_n, cur_col, seg_pass, w=wn_pass, g_on_s=False, tile_idx=tile0) mos_pass_p = self.add_mos(ridx_p, cur_col, seg_pass, w=wp_pass, g_on_s=False, tile_idx=tile0) cur_col += seg_pass + min_sep mos_buf_n = self.add_mos(ridx_n, cur_col, 2 * seg_buf, w=wn_buf, g_on_s=True, sep_g=True, tile_idx=tile0) mos_buf_p = self.add_mos(ridx_p, cur_col, 2 * seg_buf, w=wp_buf, g_on_s=True, sep_g=True, tile_idx=tile0) cur_col += 2 * seg_buf + min_sep mos_out_n = self.add_mos(ridx_n, cur_col, 2 * seg_out, w=wn_out, tile_idx=tile0) mos_out_p = self.add_mos(ridx_p, cur_col, 2 * seg_out, w=wp_out, tile_idx=tile0) col_tot = cur_col + 2 * seg_out # tile 1 cur_col = 0 mos_si_n = self.add_mos(ridx_n, cur_col, seg_in, w=wn_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) mos_si_p = self.add_mos(ridx_p, cur_col, seg_in, w=wp_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) cur_col += 2 * seg_in + min_sep mos_mux_n = self.add_mos(ridx_n, cur_col, seg_mux, w=wn_mux, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) mos_mux_p = self.add_mos(ridx_p, cur_col, seg_mux, w=wp_mux, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) cur_col = col_tot - seg_rst - min_sep - 4 * seg_rst sub_col = cur_col mos_rst_n = self.add_mos(ridx_n, cur_col, 2 * seg_rst, w=wn_rst, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) mos_rst_p = self.add_mos(ridx_p, cur_col, 4 * seg_rst, w=wp_rst, g_on_s=True, sep_g=True, tile_idx=tile1) cur_col = col_tot - seg_rst mos_rst2_n = self.add_mos(ridx_n, cur_col, seg_rst, w=wn_rst, g_on_s=True, tile_idx=tile1) mos_rst2_p = self.add_mos(ridx_p, cur_col, seg_rst, w=wp_rst, g_on_s=False, tile_idx=tile1) if substrate_row: self._substrate_row_intvl.append((sub_col, self.num_cols-sub_col)) for _tidx in range(pinfo[0].num_tiles): _pinfo = self.get_tile_pinfo(_tidx) for _ridx in range(_pinfo.num_rows): rtype = _pinfo.get_row_place_info(_ridx).row_info.row_type if rtype.is_substrate: warrs = self.add_substrate_contact(_ridx, sub_col, tile_idx=_tidx, seg=self.num_cols-sub_col) sup_name = 'VDD' if rtype is MOSType.ntap else 'VSS' self.add_pin(f'{sup_name}_sub', warrs, label=f'{sup_name}:') self.set_mos_size() # vertical routing track planning grid = self.grid tr_manager = self.tr_manager conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 vm_w = tr_manager.get_width(vm_layer, 'sig') vm_se_tidx = grid.coord_to_track(vm_layer, 0) vm_seb_tidx = tr_manager.get_next_track(vm_layer, vm_se_tidx, 'sig', 'sig') vm_o1_tidx = tr_manager.get_next_track(vm_layer, vm_seb_tidx, 'sig', 'sig') vm_ck1_tidx = tr_manager.get_next_track(vm_layer, vm_o1_tidx, 'sig', 'sig') x_ck1 = grid.track_to_coord(conn_layer, mos_mux_p.g0.track_id.base_index) vm_ck1_tidx = max(vm_ck1_tidx, grid.coord_to_track(vm_layer, x_ck1, mode=RoundMode.LESS_EQ)) x_ck2 = grid.track_to_coord(conn_layer, mos_rst_p.g[-1].track_id.base_index) vm_ck2_tidx = grid.coord_to_track(vm_layer, x_ck2, mode=RoundMode.GREATER_EQ) vm_o5_tidx = tr_manager.get_next_track(vm_layer, vm_ck2_tidx, 'sig', 'sig') vm_o42_tidx = tr_manager.get_next_track(vm_layer, vm_o5_tidx, 'sig', 'sig') x_ckb = grid.track_to_coord(conn_layer, mos_buf_n.s[0].track_id.base_index) vm_ckb_tidx = grid.coord_to_track(vm_layer, x_ckb, mode=RoundMode.GREATER_EQ) vm_seb2_tidx = tr_manager.get_next_track(vm_layer, vm_ck2_tidx, 'sig', 'sig', up=False) x_o41 = grid.track_to_coord(conn_layer, mos_pass_n.d.track_id.base_index) vm_o41_tidx = grid.coord_to_track(vm_layer, x_o41, mode=RoundMode.LESS_EQ) x_o2 = x_o41 - self.place_info.sd_pitch * 2 vm_o2_tidx = grid.coord_to_track(vm_layer, x_o2, mode=RoundMode.LESS_EQ) # routing # supplies vdd_list = [mos_in_p.s, mos_keep_p.d, mos_buf_p.d, mos_out_p.d, mos_si_p.s, mos_mux_p.d, mos_rst_p.s[0:2], mos_rst2_p.s] vss0_list = [mos_in_n.s, mos_keep_n.d, mos_buf_n.d, mos_out_n.d] vss1_list = [mos_si_n.s, mos_mux_n.d, mos_rst_n.d] if substrate_row: if tile0 < tile1: self.add_pin('VSS1', vss1_list, label='VSS:') self.add_pin('VSS0', vss0_list, label='VSS:') else: self.add_pin('VSS1', vss0_list, label='VSS:') self.add_pin('VSS0', vss1_list, label='VSS:') self.add_pin('VDD', vdd_list, label='VDD:') else: self.add_pin('VDD', self.connect_to_tracks(vdd_list, vdd_tid)) self.add_pin('VSS', self.connect_to_tracks(vss0_list, vss0_tid), connect=True) self.add_pin('VSS', self.connect_to_tracks(vss1_list, vss1_tid), connect=True) # input self.add_pin('in', self.connect_to_tracks([mos_in_n.g[0], mos_in_p.g[0]], t0_pg1_tid)) # scan in self.add_pin('scan_in', self.connect_to_tracks([mos_si_n.g[0], mos_si_p.g[0]], t1_ng1_tid)) # scan enable se = self.connect_to_tracks([mos_in_p.g[1], mos_buf_p.g[1], mos_buf_n.g[1]], t0_pg0_tid) se_top = self.connect_to_tracks(mos_si_n.g[1], t1_ng0_tid, min_len_mode=MinLenMode.LOWER) se_vm = self.connect_to_tracks([se, se_top], TrackID(vm_layer, vm_se_tidx, width=vm_w)) self.add_pin('scan_en', se) self.add_pin('scan_en_vm', se_vm, hide=True) # scan enable bar seb0 = self.connect_to_tracks(mos_buf_n.s[1], t0_nd0_tid, min_len_mode=MinLenMode.UPPER) seb1 = self.connect_to_tracks(mos_in_n.g[1], t0_ng1_tid, min_len_mode=MinLenMode.LOWER) seb2 = self.connect_to_tracks(mos_buf_p.s[1], t0_pd0_tid, min_len_mode=MinLenMode.LOWER) seb3 = self.connect_to_tracks(mos_si_p.g[1], t1_pg0_tid, min_len_mode=MinLenMode.LOWER) self.connect_to_tracks([seb1, seb2, seb3], TrackID(vm_layer, vm_seb_tidx, width=vm_w)) self.connect_to_tracks([seb0, seb2], TrackID(vm_layer, vm_seb2_tidx, width=vm_w)) # clk ck = self.connect_to_tracks([mos_keep_n.g[0], mos_pass_n.g, mos_buf_n.g[0], mos_buf_p.g[0]], t0_ng0_tid) ck1 = self.connect_to_tracks(mos_mux_p.g0, t1_pg0_tid, min_len_mode=MinLenMode.UPPER) ck_vm = self.connect_to_tracks([ck, ck1], TrackID(vm_layer, vm_ck1_tidx, width=vm_w)) ck2 = self.connect_to_tracks(mos_rst_p.g[-1], t1_pg0_tid, min_len_mode=MinLenMode.UPPER) self.connect_to_tracks([ck, ck2], TrackID(vm_layer, vm_ck2_tidx, width=vm_w)) self.add_pin('clk', ck) self.add_pin('clk_vm', ck_vm, hide=True) # clkb ckb0 = self.connect_to_tracks(mos_buf_n.s[0], t0_nd1_tid, min_len_mode=MinLenMode.UPPER) ckb1 = self.connect_to_tracks([mos_keep_p.g[0], mos_pass_p.g], t0_pg1_tid) ckb2 = self.connect_to_tracks(mos_buf_p.s[0], t0_pd1_tid, min_len_mode=MinLenMode.UPPER) ckb3 = self.connect_to_tracks([mos_mux_n.g[0], mos_rst2_n.g], t1_ng0_tid) self.connect_to_tracks([ckb0, ckb1, ckb2, ckb3], TrackID(vm_layer, vm_ckb_tidx, width=vm_w)) # o1 o10 = self.connect_to_tracks(mos_in_n.d, t0_nd1_tid, min_len_mode=MinLenMode.LOWER) o11 = self.connect_to_tracks([mos_in_p.d, mos_si_p.d], t1_pd0_tid, min_len_mode=MinLenMode.MIDDLE) o12 = self.connect_to_tracks([mos_mux_n.g[1], mos_mux_p.g[1]], t1_ng1_tid) o13 = self.connect_to_tracks(mos_si_n.d, t1_nd1_tid, min_len_mode=MinLenMode.MIDDLE) self.connect_to_tracks([o10, o11, o12, o13], TrackID(vm_layer, vm_o1_tidx, width=vm_w)) # o2 o20 = self.connect_to_tracks(mos_keep_n.s, t0_nd1_tid) o21 = self.connect_to_tracks([mos_keep_p.s, mos_mux_p.s], t1_pd1_tid) o22 = self.connect_to_tracks([mos_rst_n.g[0], mos_rst_p.g[0]], t1_pg0_tid) o23 = self.connect_to_tracks(mos_mux_n.s, t1_nd0_tid) self.connect_to_tracks([o20, o21, o22, o23], TrackID(vm_layer, vm_o2_tidx, width=vm_w)) # o3 o30 = self.connect_to_tracks([mos_keep_n.g[1], mos_keep_p.g[1]], t0_ng1_tid) o31 = self.connect_to_tracks(mos_rst_p.d[0], t1_pd0_tid) o32 = self.connect_to_tracks(mos_rst_n.s[0], t1_nd1_tid) o3_track = self.connect_wires([mos_pass_n.s, mos_pass_p.s]) self.connect_to_track_wires([o30, o31, o32], o3_track) # o4 o40 = self.connect_to_tracks(mos_pass_n.d, t0_nd0_tid, min_len_mode=MinLenMode.LOWER) o41 = self.connect_to_tracks([mos_out_n.g, mos_out_p.g], t0_pg1_tid) o42 = self.connect_to_tracks(mos_pass_p.d, t0_pd1_tid, min_len_mode=MinLenMode.LOWER) o43 = self.connect_to_tracks(mos_rst_p.d[1], t1_pd1_tid) o44 = self.connect_to_tracks(mos_rst2_n.s, t1_nd0_tid) self.connect_to_tracks([o40, o42, o43, o44], TrackID(vm_layer, vm_o41_tidx, width=vm_w)) self.connect_to_tracks([o41, o43], TrackID(vm_layer, vm_o42_tidx, width=vm_w)) # tp and tn self.connect_to_tracks([mos_rst_n.s[-1], mos_rst2_n.d], t1_nd1_tid) self.connect_to_tracks([mos_rst_p.s[-1], mos_rst2_p.d], t1_pd0_tid) # o5 o50 = self.connect_to_tracks([mos_out_n.s[0], mos_out_p.s[0]], t0_pd1_tid, min_len_mode=MinLenMode.UPPER) o51 = self.connect_to_tracks([mos_rst_n.g[-1], mos_rst2_p.g], t1_ng1_tid) self.connect_to_tracks([o50, o51], TrackID(vm_layer, vm_o5_tidx, width=vm_w)) # output out_idx = sig_locs.get('out', t0_ng1_tid.base_index) out = self.connect_to_tracks([mos_out_n.s[1], mos_out_p.s[1]], TrackID(hm_layer, out_idx)) self.add_pin('out', out) # rstlb rstlb = self.connect_to_tracks([mos_rst_n.g[1], mos_rst_p.g[1]], t1_pg1_tid) self.add_pin('rstlb', rstlb) if vertical_rst: vm_idx = self.grid.coord_to_track(vm_layer, rstlb.middle, mode=RoundMode.NEAREST) avail = self.get_available_tracks(vm_layer, vm_ck1_tidx, vm_ck2_tidx, 0, self.bound_box.h) if vm_idx not in avail: raise ValueError(f'Recheck routing on layer {vm_layer}') rstlb_vm = self.connect_to_tracks(rstlb, TrackID(vm_layer, vm_idx, width=vm_w)) self.add_pin('rstlb_vm', rstlb_vm) self.sch_params = dict( lch=place_info.lch, seg_dict={'buf': seg_buf, 'in': seg_in, 'mux': seg_mux, 'keep': seg_keep, 'pass': seg_pass, 'rst': seg_rst, 'out': seg_out}, w_dict=dict(p_buf=wp_buf, n_buf=wn_buf, p_in=wp_in, n_in=wn_in, p_mux=wp_mux, n_mux=wn_mux, p_keep=wp_keep, n_keep=wn_keep, p_pass=wp_pass, n_pass=wn_pass, p_rst=wp_rst, n_rst=wn_rst, p_out=wp_out, n_out=wn_out), th_p=place_info.get_row_place_info(ridx_p).row_info.threshold, th_n=place_info.get_row_place_info(ridx_n).row_info.threshold,
)