Source code for bag3_digital.layout.sampler.strongarm

# 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 contains layout generators for a classic StrongArm latch."""

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

from pybag.enum import RoundMode

from bag.util.immutable import Param, ImmutableSortedDict
from bag.design.database import Module
from bag.layout.routing.base import TrackID
from bag.layout.template import TemplateDB

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

from ...schematic.strongarm_frontend import bag3_digital__strongarm_frontend


[docs]class SAFrontendHalf(MOSBase): """A inverter with only transistors drawn, no metal connections """ def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None: MOSBase.__init__(self, temp_db, params, **kwargs) @classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: return dict( pinfo='placement information object.', seg_dict='segments dictionary.', w_dict='widths dictionary.', ridx_n='bottom nmos row index.', ridx_p='pmos row index.', has_rstb='True to add rstb functionality.', has_bridge='True to add bridge switch.', vertical_out='True to connect outputs to vm_layer.', vertical_rstb='True to connect rstb to vm_layer.',
) @classmethod
[docs] def get_default_param_values(cls) -> Dict[str, Any]: return dict( w_dict={}, ridx_n=0, ridx_p=-1, has_rstb=False, has_bridge=False, vertical_out=True, vertical_rstb=True,
)
[docs] def draw_layout(self): place_info = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(place_info) seg_dict: ImmutableSortedDict[str, int] = self.params['seg_dict'] ridx_n: int = self.params['ridx_n'] ridx_p: int = self.params['ridx_p'] has_rstb: bool = self.params['has_rstb'] has_bridge: bool = self.params['has_bridge'] vertical_out: bool = self.params['vertical_out'] vertical_rstb: bool = self.params['vertical_rstb'] w_dict, th_dict = self._get_w_th_dict(ridx_n, ridx_p, has_bridge) seg_in = seg_dict['in'] seg_tail = seg_dict['tail'] seg_nfb = seg_dict['nfb'] seg_pfb = seg_dict['pfb'] seg_swm = seg_dict['sw'] w_in = w_dict['in'] w_tail = w_dict['tail'] w_nfb = w_dict['nfb'] w_pfb = w_dict['pfb'] if seg_in & 1 or (seg_tail % 4 != 0) or seg_nfb & 1 or seg_pfb & 1: raise ValueError('in, tail, nfb, or pfb must have even number of segments') # NOTE: make seg_swo even so we can abut transistors seg_swo = seg_swm + (seg_swm & 1) seg_tail = seg_tail // 2 # placement ridx_in = ridx_n + 1 ridx_nfb = ridx_in + 1 m_in = self.add_mos(ridx_in, 0, seg_in, w=w_in) m_nfb = self.add_mos(ridx_nfb, 0, seg_nfb, w=w_nfb) m_pfb = self.add_mos(ridx_p, 0, seg_pfb, w=w_pfb) ng_tid = self.get_track_id(ridx_nfb, MOSWireType.G, wire_name='sig', wire_idx=-1) mid_tid = self.get_track_id(ridx_nfb, MOSWireType.DS, wire_name='sig') pg_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig') vdd_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sup') pclk_tid = pg_tid tail_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='tail') tail_in_tid = self.get_track_id(ridx_in, MOSWireType.DS, wire_name='sig') if has_rstb: vss_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sup') nclk_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0) nrst_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1) prst_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1) m_tail = self.add_mos(ridx_n, 0, seg_tail, w=w_tail, g_on_s=True, stack=2, sep_g=True) m_swo_rst = self.add_mos(ridx_p, seg_pfb, seg_swo, w=w_pfb) m_swo = self.add_mos(ridx_p, seg_pfb + seg_swo, seg_swo, w=w_pfb) m_swm = self.add_mos(ridx_p, seg_pfb + 2 * seg_swo, seg_swm, w=w_pfb) vss_conn = m_tail.s tail_conn = m_tail.d g_conn = m_tail.g rstb_conn = g_conn[0::2] clk_conn = g_conn[1::2] else: vss_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sup') nclk_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=-1) nrst_tid = prst_tid = None m_tail = self.add_mos(ridx_n, 0, seg_tail, w=w_tail) m_swo = self.add_mos(ridx_p, seg_pfb, seg_swo, w=w_pfb) m_swm = self.add_mos(ridx_p, seg_pfb + seg_swo, seg_swm, w=w_pfb) m_swo_rst = None vss_conn = m_tail.s tail_conn = m_tail.d rstb_conn = None clk_conn = m_tail.g # NOTE: force even number of columns to make sure VDD conn_layer wires are on even columns. ncol_tot = self.num_cols self.set_mos_size(num_cols=ncol_tot + (ncol_tot & 1)) # routing conn_layer = self.conn_layer vm_layer = conn_layer + 2 vm_w = self.tr_manager.get_width(vm_layer, 'sig') grid = self.grid if has_rstb: nrst = self.connect_to_tracks(rstb_conn, nrst_tid) prst = self.connect_to_tracks([m_swo_rst.g], prst_tid) self.add_pin('nrstb', nrst) self.add_pin('prstb', prst) if vertical_rstb: xrst = grid.track_to_coord(conn_layer, m_swo_rst.g.track_id.base_index) vm_tidx = grid.coord_to_track(vm_layer, xrst, mode=RoundMode.GREATER_EQ) self.connect_to_tracks([nrst, prst], TrackID(vm_layer, vm_tidx, width=vm_w)) out = self.connect_wires([m_nfb.d, m_pfb.d, m_swo.d, m_swo_rst.d]) vdd = self.connect_to_tracks([m_pfb.s, m_swo.s, m_swm.s, m_swo_rst.s], vdd_tid) mid = self.connect_to_tracks([m_in.s, m_nfb.s, m_swm.d], mid_tid) else: out = self.connect_wires([m_nfb.d, m_pfb.d, m_swo.d]) vdd = self.connect_to_tracks([m_pfb.s, m_swo.s, m_swm.s], vdd_tid) mid = self.connect_to_tracks([m_in.s, m_nfb.s, m_swm.d], mid_tid) tail = self.connect_to_tracks(tail_conn, tail_tid) in_d = m_in.d tail_in = self.connect_to_tracks(in_d, tail_in_tid) self.add_pin('tail_in', tail_in, hide=True) tail_list = [tail, tail_in] for warr in in_d.warr_iter(): xwire = grid.track_to_coord(conn_layer, warr.track_id.base_index) vm_tidx = grid.coord_to_track(vm_layer, xwire, mode=RoundMode.GREATER_EQ) self.connect_to_tracks(tail_list, TrackID(vm_layer, vm_tidx, width=vm_w)) vss = self.connect_to_tracks(vss_conn, vss_tid) nclk = self.connect_to_tracks(clk_conn, nclk_tid) nout = self.connect_to_tracks(m_nfb.g, ng_tid) pout = self.connect_to_tracks(m_pfb.g, pg_tid) pclk = self.connect_to_tracks([m_swo.g, m_swm.g], pclk_tid) xclk = grid.track_to_coord(conn_layer, m_swo.g.track_id.base_index) vm_tidx = grid.coord_to_track(vm_layer, xclk, mode=RoundMode.GREATER_EQ) clk_vm = self.connect_to_tracks([nclk, pclk], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('clk_vm', clk_vm) xout = grid.track_to_coord(conn_layer, m_pfb.g.track_id.base_index) vm_tidx = grid.coord_to_track(vm_layer, xout, mode=RoundMode.GREATER_EQ) if vertical_out: out_vm = self.connect_to_tracks([nout, pout], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('out_vm', out_vm) else: self.add_pin('pout', pout) self.add_pin('nout', nout) self.add_pin('VSS', vss) self.add_pin('VDD', vdd) self.add_pin('tail', tail) self.add_pin('clk', nclk) self.add_pin('in', m_in.g) self.add_pin('out', out) self.add_pin('mid', mid) append_dict = dict(swo=seg_swo, swm=seg_swm) if has_bridge: append_dict['br'] = 1 sch_seg_dict = seg_dict.copy(append=append_dict, remove=['sw']) self.sch_params = dict( lch=self.arr_info.lch, seg_dict=sch_seg_dict, w_dict=w_dict, th_dict=th_dict, has_rstb=has_rstb, has_bridge=has_bridge,
)
[docs] def _get_w_th_dict(self, ridx_n: int, ridx_p: int, has_bridge: bool ) -> Tuple[ImmutableSortedDict[str, int], ImmutableSortedDict[str, str]]: w_dict: Mapping[str, int] = self.params['w_dict'] w_ans = {} th_ans = {} for name, row_idx in [('tail', ridx_n), ('in', ridx_n + 1), ('nfb', ridx_n + 2), ('pfb', ridx_p)]: rinfo = self.get_row_info(row_idx, 0) w = w_dict.get(name, 0) if w == 0: w = rinfo.width w_ans[name] = w th_ans[name] = rinfo.threshold w_ans['swm'] = w_ans['swo'] = w_ans['pfb'] th_ans['swm'] = th_ans['swo'] = th_ans['pfb'] if has_bridge: w_ans['br'] = w_ans['in'] th_ans['br'] = th_ans['in'] return ImmutableSortedDict(w_ans), ImmutableSortedDict(th_ans)
[docs]class SAFrontend(MOSBase): """A inverter with only transistors drawn, no metal connections """ def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None: MOSBase.__init__(self, temp_db, params, **kwargs) @classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]: return bag3_digital__strongarm_frontend
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]: ans = SAFrontendHalf.get_params_info() ans['even_center'] = 'True to force center column to be even.' ans['export_mid'] = 'True to export intermediate nodes; False by default.' return ans
@classmethod
[docs] def get_default_param_values(cls) -> Dict[str, Any]: ans = SAFrontendHalf.get_default_param_values() ans['even_center'] = False ans['export_mid'] = False return ans
[docs] def draw_layout(self): master: SAFrontendHalf = self.new_template(SAFrontendHalf, params=self.params) self.draw_base(master.draw_base_info) ridx_n: int = self.params['ridx_n'] ridx_p: int = self.params['ridx_p'] has_bridge: bool = self.params['has_bridge'] vertical_out: bool = self.params['vertical_out'] vertical_rstb: bool = self.params['vertical_rstb'] even_center: bool = self.params['even_center'] export_mid: bool = self.params['export_mid'] # placement nsep = self.min_sep_col nsep += (nsep & 1) if even_center and nsep % 4 == 2: nsep += 2 nhalf = master.num_cols corel = self.add_tile(master, 0, nhalf, flip_lr=True) corer = self.add_tile(master, 0, nhalf + nsep) self.set_mos_size(num_cols=nsep + 2 * nhalf) # routing ridx_in = ridx_n + 1 ridx_nfb = ridx_in + 1 inn_tidx, hm_w = self.get_track_info(ridx_in, MOSWireType.G, wire_name='sig', wire_idx=0) inp_tidx = self.get_track_index(ridx_in, MOSWireType.G, wire_name='sig', wire_idx=-1) outn_tidx = self.get_track_index(ridx_nfb, MOSWireType.DS, wire_name='sig', wire_idx=-1) outp_tidx = self.get_track_index(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=0) hm_layer = self.conn_layer + 1 inp, inn = self.connect_differential_tracks(corel.get_pin('in'), corer.get_pin('in'), hm_layer, inp_tidx, inn_tidx, width=hm_w) self.add_pin('inp', inp) self.add_pin('inn', inn) outp, outn = self.connect_differential_tracks(corer.get_all_port_pins('out'), corel.get_all_port_pins('out'), hm_layer, outp_tidx, outn_tidx, width=hm_w) if vertical_out: outp_vm = corel.get_pin('out_vm') outn_vm = corer.get_pin('out_vm') self.connect_to_track_wires(outp, outp_vm) self.connect_to_track_wires(outn, outn_vm) self.add_pin('outp', outp_vm) self.add_pin('outn', outn_vm) else: self.add_pin('outp', outp, connect=True) self.add_pin('outn', outn, connect=True) self.add_pin('outp', corel.get_pin('pout'), connect=True) self.add_pin('outp', corel.get_pin('nout'), connect=True) self.add_pin('outn', corer.get_pin('pout'), connect=True) self.add_pin('outn', corer.get_pin('nout'), connect=True) self.add_pin('outp_hm', outp, hide=True) self.add_pin('outn_hm', outn, hide=True) clk = self.connect_wires([corel.get_pin('clk'), corer.get_pin('clk')]) vss = self.connect_wires([corel.get_pin('VSS'), corer.get_pin('VSS')]) vdd = self.connect_wires([corel.get_pin('VDD'), corer.get_pin('VDD')]) tail = self.connect_wires([corel.get_pin('tail'), corer.get_pin('tail')]) self.add_pin('tail', tail, hide=not export_mid) self.reexport(corel.get_port('mid'), net_name='midn', hide=not export_mid) self.reexport(corer.get_port('mid'), net_name='midp', hide=not export_mid) self.add_pin('clk', clk) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) self.reexport(corel.get_port('clk_vm'), net_name='clkl', hide=True) self.reexport(corer.get_port('clk_vm'), net_name='clkr', hide=True) # bridge_switch if has_bridge: m_br0 = self.add_mos(ridx_n + 1, nhalf, 1, w=master.sch_params['w_dict']['br'], stack=nsep) self.connect_to_track_wires(m_br0.g, clk) self.connect_wires([corel.get_pin('tail_in'), corer.get_pin('tail_in')]) if corel.has_port('nrstb'): rstb = self.connect_wires([corel.get_pin('nrstb'), corer.get_pin('nrstb')]) if vertical_rstb: self.add_pin('rstb', rstb) else: rstl = corel.get_pin('prstb') rstr = corer.get_pin('prstb') self.add_pin('rstb', rstb, connect=True) self.add_pin('rstb', rstl, connect=True) self.add_pin('rstb', rstr, connect=True) self.add_pin('nrstb', rstb, hide=True) self.add_pin('prstbl', rstl, hide=True) self.add_pin('prstbr', rstr, hide=True) if has_bridge: self.sch_params = master.sch_params.copy(append=dict(stack_br=nsep, export_mid=export_mid)) else: self.sch_params = master.sch_params.copy(append=dict(export_mid=export_mid))