Source code for bag3_digital.layout.clk_divider.div2_chain

# BSD 3-Clause License
#
# Copyright (c) 2018, Regents of the University of California
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
#   list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from typing import Any, Mapping, Sequence, Optional, Union, Type, Tuple

from pybag.enum import RoundMode, MinLenMode

from bag.util.math import HalfInt
from bag.util.immutable import Param, ImmutableSortedDict
from bag.layout.routing.base import TrackID, WireArray
from bag.design.database import Module

from xbase.layout.enum import MOSWireType
from xbase.layout.mos.base import MOSBasePlaceInfo, MOSBase

from .div2 import Div2
from ..stdcells.gates import InvChainCore
from ..stdcells.clk_gate import ClkGate

from ...schematic.div2_chain import bag3_digital__div2_chain


[docs]class Div2Chain(MOSBase): @classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]: return bag3_digital__div2_chain
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]: return dict( pinfo='The MOSBasePlaceInfo object.', num_stages='Number of stages.', div_params='Div2 divider parameters. Same divider per stage', clk_div_buf_params_list='List of divided clock buffer parameters. If an entry is None, removed.' 'If not a list, assumed to have same parameter per stage. Defaults to None.', inv_clk_div_list='List of booleans mapping whether each divider (after the first) should invert its input ' 'clock. If a list, should have length num_stages - 1. If not a list, assumed to have the ' 'same boolean per divider. Defaults to False.', clk_buf_params='Input clock buffer parameters. If None, removed. Defaults to None.', clk_gate_params='Clock gate parameters. If None, removed. Defaults to None.', export_nets='True to export intermediate nets. Defaults to False', clk_div_layer='Divided clock layer. Defaults to vm_layer', output_clk_divb='True to output clk_divb wires (i.e., bring up to clk_div_layer and export as pins). ' 'Defaults to True', clk_wtype='Clock wire type. Defaults to sig', ridx_p='pmos row index.', ridx_n='nmos row index.', sig_locs='Signal track location dictionary.', export_unit_sup='True to export unit supply pins. Defaults to False.', add_taps='True to add substrate columns in this cell; False if taps are added in higher hierarchy. '
'Defaults to True', ) @classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]: return dict( clk_div_buf_params_list=None, inv_clk_div_list=False, clk_buf_params=None, clk_gate_params=None, export_nets=False, clk_div_layer=None, output_clk_divb=True, clk_wtype='sig', sig_locs={}, ridx_p=-1, ridx_n=0, export_unit_sup=False, add_taps=True,
) @property
[docs] def clk_wtype(self): return self.params['clk_wtype']
[docs] def connect_clk_div_warr_to_top(self, clk_div_layer: int, warr_vm: WireArray, nwarr_hm: WireArray, pwarr_hm: WireArray): clk_div_hor_layer = clk_div_layer - int(not self.grid.is_horizontal(clk_div_layer)) clk_wtype = self.clk_wtype vm_layer = self.conn_layer + 2 xm_layer = vm_layer + 1 if clk_div_layer == vm_layer: return warr_vm # compute coord_list_o_override, avoiding the already drawn vm wire cur_vm_tid = warr_vm.track_id coord_list_o_override_r = [] cur_vm_tid = self.tr_manager.get_next_track_obj(cur_vm_tid, 'sig', clk_wtype, 2) while cur_vm_tid.get_bounds(self.grid)[1] < nwarr_hm.upper: coord_list_o_override_r.append(self.grid.track_to_coord(vm_layer, cur_vm_tid.base_index)) cur_vm_tid = self.tr_manager.get_next_track_obj(cur_vm_tid, clk_wtype, clk_wtype, 2) cur_vm_tid = warr_vm.track_id coord_list_o_override_l = [] cur_vm_tid = self.tr_manager.get_next_track_obj(cur_vm_tid, 'sig', clk_wtype, -2) while cur_vm_tid.get_bounds(self.grid)[0] > nwarr_hm.lower: coord_list_o_override_l.append(self.grid.track_to_coord(vm_layer, cur_vm_tid.base_index)) cur_vm_tid = self.tr_manager.get_next_track_obj(cur_vm_tid, clk_wtype, clk_wtype, -2) nwarr_vm = [warr_vm] pwarr_vm = [warr_vm] if coord_list_o_override_r: nrwarr_vm = self.connect_via_stack(self.tr_manager, nwarr_hm, vm_layer, clk_wtype, coord_list_o_override=coord_list_o_override_r) prwarr_vm = self.connect_via_stack(self.tr_manager, pwarr_hm, vm_layer, clk_wtype, coord_list_o_override=coord_list_o_override_r) nwarr_vm.append(nrwarr_vm) pwarr_vm.append(prwarr_vm) if coord_list_o_override_l: nlwarr_vm = self.connect_via_stack(self.tr_manager, nwarr_hm, vm_layer, clk_wtype, coord_list_o_override=coord_list_o_override_l) plwarr_vm = self.connect_via_stack(self.tr_manager, pwarr_hm, vm_layer, clk_wtype, coord_list_o_override=coord_list_o_override_l) nwarr_vm.append(nlwarr_vm) pwarr_vm.append(plwarr_vm) xm_w_sig = self.tr_manager.get_width(xm_layer, 'sig') xm_tidx_n = self.grid.coord_to_track(xm_layer, nwarr_hm.bound_box.ym, RoundMode.NEAREST) xm_tidx_p = self.grid.coord_to_track(xm_layer, pwarr_hm.bound_box.ym, RoundMode.NEAREST) nwarr_hor = self.connect_to_tracks(nwarr_vm, TrackID(xm_layer, xm_tidx_n, width=xm_w_sig), min_len_mode=MinLenMode.MIDDLE) pwarr_hor = self.connect_to_tracks(pwarr_vm, TrackID(xm_layer, xm_tidx_p, width=xm_w_sig), min_len_mode=MinLenMode.MIDDLE) if clk_div_hor_layer > xm_layer: nwarr_hor = self.connect_via_stack(self.tr_manager, nwarr_hor, clk_div_hor_layer, 'sig') pwarr_hor = self.connect_via_stack(self.tr_manager, pwarr_hor, clk_div_hor_layer, 'sig') warr_top = self.connect_wires([nwarr_hor, pwarr_hor]) if not self.grid.is_horizontal(clk_div_hor_layer): tidx_top = self.grid.coord_to_track(clk_div_layer, warr_top[0].bound_box.xm, RoundMode.NEAREST) warr_top = self.connect_to_tracks(warr_top, TrackID(clk_div_layer, tidx_top, width=self.tr_manager.get_width(clk_div_layer, 'sig'))) return warr_top
[docs] def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) num_stages: int = self.params['num_stages'] div_params: Param = self.params['div_params'] clk_div_buf_params_list: Optional[Union[Param, Sequence[Param]]] = self.params['clk_div_buf_params_list'] inv_clk_div_list: Union[bool, Sequence[bool]] = self.params['inv_clk_div_list'] clk_buf_params: Optional[Param] = self.params['clk_buf_params'] clk_gate_params: Optional[Param] = self.params['clk_gate_params'] export_nets: bool = self.params['export_nets'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] export_unit_sup: bool = self.params['export_unit_sup'] sig_locs: Mapping[str, Tuple[HalfInt, int, float]] = self.params['sig_locs'] # TODO: implement clk_div_layer: int = self.params['clk_div_layer'] output_clk_divb: bool = self.params['output_clk_divb'] add_taps: bool = self.params['add_taps'] has_clk_buf = clk_buf_params is not None has_clk_gate = clk_gate_params is not None if clk_div_buf_params_list is None: clk_div_buf_params_list = [None] * num_stages elif isinstance(clk_div_buf_params_list, ImmutableSortedDict): clk_div_buf_params_list = [clk_div_buf_params_list] * num_stages elif len(clk_div_buf_params_list) != num_stages: raise ValueError(f"clk_div_buf_params_list does not have length num_stages = {num_stages}") if isinstance(inv_clk_div_list, bool): inv_clk_div_list = [inv_clk_div_list] * (num_stages - 1) elif len(inv_clk_div_list) != num_stages - 1: raise ValueError(f"inv_clk_div_list does not have length num_stages - 1 = {num_stages - 1}") hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 if not clk_div_layer: clk_div_layer = vm_layer elif clk_div_layer < vm_layer: raise ValueError(f"clk_div_layer = {clk_div_layer} must be at least vm_layer = {vm_layer}") shared_params = dict(pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n) div_params = div_params.copy(append=shared_params) div_master: Div2 = self.new_template(Div2, params=div_params) div_clk_in_hm_tidx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1) div_clk_in_hm_tid = TrackID(hm_layer, div_clk_in_hm_tidx, width=self.tr_manager.get_width(hm_layer, 'sig')) div_clk_out_hm_tidx = div_master.clk_div_hm_tidx div_clk_outb_hm_tidx = div_master.clk_divb_hm_tidx div_ncols = div_master.num_cols blk_sp = self.min_sep_col sub_sep = self.sub_sep_col tap_ncols = self.get_tap_ncol() cur_col = 0 # left tap vdd_conn_list, vss_conn_list = [], [] if add_taps: self.add_tap(cur_col, vdd_conn_list, vss_conn_list) cur_col += tap_ncols + sub_sep next_div_in = None all_insts = [] if has_clk_buf: clk_buf_sig_locs = clk_buf_params.get('sig_locs', ImmutableSortedDict()) if has_clk_gate: clk_buf_sig_locs = clk_buf_sig_locs.copy(append=dict( nin0=div_clk_in_hm_tidx, nin1=div_clk_outb_hm_tidx)) else: clk_buf_sig_locs = clk_buf_sig_locs.copy(append=dict( nin0=div_clk_out_hm_tidx, nin1=div_clk_outb_hm_tidx)) clk_buf_master: InvChainCore = self.new_template(InvChainCore, params=clk_buf_params.copy(append=dict( **shared_params, dual_output=False, sig_locs=clk_buf_sig_locs))) clk_buf_inst = self.add_tile(clk_buf_master, 0, cur_col) cur_col += clk_buf_master.num_cols + blk_sp clk_hm = clk_buf_inst.get_pin('in') self.add_pin('clk', self.extend_wires(clk_hm, min_len_mode=MinLenMode.LOWER)) should_inv_stg_0 = clk_buf_master.out_invert clk_buf_pname = 'outb' if should_inv_stg_0 else 'out' next_div_in = clk_buf_inst.get_pin(clk_buf_pname) self.reexport(clk_buf_inst.get_port(clk_buf_pname), net_name='clk_bufb' if should_inv_stg_0 else 'clk_buf', hide=not export_nets) all_insts.append(clk_buf_inst) else: clk_buf_master = None if has_clk_gate: clk_gate_sig_locs = clk_gate_params.get('sig_locs', ImmutableSortedDict()) clk_gate_master: ClkGate = self.new_template(ClkGate, params=clk_gate_params.copy(append=dict( **shared_params, sig_locs=clk_gate_sig_locs))) clk_gate_inst = self.add_tile(clk_gate_master, 0, cur_col) # cur_col += clk_gate_master.num_cols + self.min_sep_col cur_col += clk_gate_master.num_cols if add_taps: cur_col += sub_sep self.add_tap(cur_col, vdd_conn_list, vss_conn_list) cur_col += tap_ncols + sub_sep else: cur_col += blk_sp for port_name in clk_gate_inst.port_names_iter(): if port_name in ('VDD', 'VSS', 'gclk', 'clk', 'clk_vm', 'en'): continue self.reexport(clk_gate_inst.get_port(port_name), net_name=f'clk_gate_{port_name}') if next_div_in is not None: self.connect_to_track_wires(clk_gate_inst.get_pin('clk'), next_div_in) else: self.reexport(clk_gate_inst.get_port('clk')) next_div_in = clk_gate_inst.get_pin('gclk') self.reexport(clk_gate_inst.get_port('gclk'), hide=not export_nets) self.reexport(clk_gate_inst.get_port('en')) all_insts.append(clk_gate_inst) else: clk_gate_master = None inv_clk_div_list = list(inv_clk_div_list) + [False] div_insts = [] clk_div_buf_insts = [] clk_div_buf_masters = [] for i, (clk_div_buf_params, inv_clk_next) in enumerate(zip(clk_div_buf_params_list, inv_clk_div_list)): if i > 0: if add_taps: cur_col += sub_sep self.add_tap(cur_col, vdd_conn_list, vss_conn_list) cur_col += tap_ncols + sub_sep else: cur_col += blk_sp div_inst = self.add_tile(div_master, 0, cur_col) cur_col += div_ncols div_insts.append(div_inst) if next_div_in is None: self.reexport(div_inst.get_port('clk')) self.reexport(div_inst.get_port('pclk')) else: self.connect_to_tracks([next_div_in, div_inst.get_pin('clk')], div_clk_in_hm_tid) clk_divb_top_warr = None if clk_div_buf_params: clk_div_buf_sig_locs = clk_div_buf_params.get('sig_locs', ImmutableSortedDict()) clk_div_buf_sig_locs = clk_div_buf_sig_locs.copy(append=dict( nin0=div_clk_out_hm_tidx, nin1=div_clk_outb_hm_tidx)) clk_div_buf_params = clk_div_buf_params.copy(append=dict(**shared_params, dual_output=True, sig_locs=clk_div_buf_sig_locs)) clk_div_buf_master: InvChainCore = self.new_template(InvChainCore, params=clk_div_buf_params) cur_col += blk_sp clk_div_buf_inst = self.add_tile(clk_div_buf_master, 0, cur_col) # cur_col += clk_div_buf_master.num_cols + self.min_sep_col cur_col += clk_div_buf_master.num_cols clk_div_buf_masters.append(clk_div_buf_master) clk_div_buf_insts.append(clk_div_buf_inst) self.connect_wires([div_inst.get_pin('clk_div_hm'), clk_div_buf_inst.get_pin('in')]) self.reexport(div_inst.get_port('clk_div'), net_name=f'div_out_{i}', hide=not export_nets) self.reexport(div_inst.get_port('clk_divb'), net_name=f'div_outb_{i}', hide=not export_nets) clk_div_vm = clk_div_buf_inst.get_pin('out') clk_divb_vm = clk_div_buf_inst.get_pin('outb') stg_out_warr = clk_div_vm stg_outb_warr = clk_divb_vm clk_div_top_warr = self.connect_clk_div_warr_to_top(clk_div_layer, clk_div_vm, clk_div_buf_inst.get_pin('nout'), clk_div_buf_inst.get_pin('pout')) if output_clk_divb: clk_divb_top_warr = self.connect_clk_div_warr_to_top(clk_div_layer, clk_divb_vm, clk_div_buf_inst.get_pin('noutb'), clk_div_buf_inst.get_pin('poutb')) else: clk_div_buf_masters.append(None) stg_out_warr = div_inst.get_pin('clk_div') stg_outb_warr = div_inst.get_pin('clk_divb') clk_div_top_warr = self.connect_via_stack(self.tr_manager, stg_out_warr, clk_div_layer, self.clk_wtype) if output_clk_divb: clk_divb_top_warr = self.connect_via_stack(self.tr_manager, stg_outb_warr, clk_div_layer, self.clk_wtype) # self.add_pin(f'clk_div_{i}', stg_out_warr) # self.add_pin(f'clk_divb_{i}', stg_outb_warr) self.add_pin(f'clk_div_{i}', clk_div_top_warr) self.add_pin(f'clk_div_vm_{i}', stg_out_warr, hide=True) if output_clk_divb: self.add_pin(f'clk_divb_{i}', clk_divb_top_warr) else: self.add_pin(f'clk_divb_{i}', stg_outb_warr, hide=not export_nets) next_div_in = stg_outb_warr if inv_clk_next else stg_out_warr if add_taps: cur_col += sub_sep self.add_tap(cur_col, vdd_conn_list, vss_conn_list) cur_col += tap_ncols self.set_mos_size() rst_hm = self.connect_wires([inst.get_pin('rst') for inst in div_insts])[0] self.add_pin('rst', rst_hm) all_insts += div_insts + clk_div_buf_insts vdd_hm = self.connect_wires([inst.get_pin('VDD') for inst in all_insts], lower=self.bound_box.xl, upper=self.bound_box.xh)[0] vss_hm = self.connect_wires([inst.get_pin('VSS') for inst in all_insts], lower=self.bound_box.xl, upper=self.bound_box.xh)[0] if add_taps: self.connect_to_track_wires(vdd_conn_list, vdd_hm) self.connect_to_track_wires(vss_conn_list, vss_hm) if export_unit_sup: for inst in all_insts: if inst is None: continue self.reexport(inst.get_port('VDD')) self.reexport(inst.get_port('VSS')) self.add_pin('VDD', vdd_hm) self.add_pin('VSS', vss_hm) self.sch_params = dict( num_stages=num_stages, div_params_list=div_master.sch_params, clk_div_buf_params_list=[None if master is None else master.sch_params for master in clk_div_buf_masters], inv_clk_div_list=self.params['inv_clk_div_list'], clk_buf_params=clk_buf_master and clk_buf_master.sch_params, clk_gate_params=clk_gate_master and clk_gate_master.sch_params, output_clk_divb=output_clk_divb, export_nets=export_nets,
)