# SPDX-License-Identifier: BSD-3-Clause AND Apache-2.0
# Copyright 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.
# 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.
from typing import Any, Dict, Sequence, Optional, Union, Type, Mapping
from pybag.enum import RoundMode, MinLenMode
from bag.util.immutable import Param, ImmutableSortedDict
from bag.layout.template import TemplateDB
from bag.layout.routing.base import TrackID
from bag.design.database import Module
from xbase.layout.enum import MOSWireType
from xbase.layout.mos.base import MOSBasePlaceInfo, MOSBase
from ...schematic.mux2to1_matched import bag3_digital__mux2to1_matched
from .gates import InvTristateCore, InvCore
[docs]class Mux2to1MatchedHalf(MOSBase):
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='The MOSBasePlaceInfo object.',
seg_dict='segments dictionary.',
w_dict='width dictionary.',
ridx_n='nmos row index.',
ridx_p='pmos row index.',
vertical_sel='True to connect select signals to vertical layer.',
)
@classmethod
[docs] def get_default_param_values(cls) -> Dict[str, Any]:
return dict(
w_dict={},
ridx_n=0,
ridx_p=-1,
vertical_sel=True,
)
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
self.draw_base(pinfo)
seg_dict: ImmutableSortedDict[str, int] = self.params['seg_dict']
ridx_n: int = self.params['ridx_n']
ridx_p: int = self.params['ridx_p']
vertical_sel: bool = self.params['vertical_sel']
w_dict = self._get_w_dict(ridx_n, ridx_p)
seg_tri = seg_dict['tri']
seg_buf = seg_dict['buf']
w_pbuf = w_dict['pbuf']
w_nbuf = w_dict['nbuf']
w_ptri = w_dict['ptri']
w_ntri = w_dict['ntri']
if (seg_tri & 1) or (seg_buf % 4 != 0):
raise ValueError('segments of transistors must be even, buf must be multiple of 4.')
seg_buf_half = seg_buf // 2
# placement
vm_layer = self.conn_layer + 2
grid = self.grid
tr_manager = self.tr_manager
vm_w = tr_manager.get_width(vm_layer, 'sig')
vss_tid = self.get_track_id(ridx_n, False, wire_name='sup')
vdd_tid = self.get_track_id(ridx_p, True, wire_name='sup')
nd_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-1)
pd_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=0)
ng2_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=2)
pg0_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-2)
pg1_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1)
in_tid = ng2_tid
pmid_tid = pg0_tid
psel_tid = pg1_tid
tri_ncol = 2 * seg_tri
min_sep = self.min_sep_col
min_sep += (min_sep & 1)
ntri = self.add_mos(ridx_n, 0, seg_tri, w=w_ntri, stack=2, g_on_s=True, sep_g=True)
ptri = self.add_mos(ridx_p, 0, seg_tri, w=w_ptri, stack=2, g_on_s=True, sep_g=True)
cur_col = tri_ncol + min_sep
nbuf = self.add_mos(ridx_n, cur_col, seg_buf_half, w=w_nbuf)
pbuf = self.add_mos(ridx_p, cur_col, seg_buf_half, w=w_pbuf)
self.set_mos_size()
# routing
# supplies
vdd = self.connect_to_tracks([ptri.s, pbuf.s], vdd_tid)
vss = self.connect_to_tracks([ntri.s, nbuf.s], vss_tid)
self.add_pin('VDD', vdd)
self.add_pin('VSS', vss)
# select signals
in_warr = self.connect_to_tracks([ntri.g[0::2], ptri.g[0::2]], in_tid)
nsel = ntri.g[1::2]
psel = self.connect_to_tracks(ptri.g[1::2], psel_tid, min_len_mode=MinLenMode.MIDDLE)
vm_tidx = grid.coord_to_track(vm_layer, psel.middle, mode=RoundMode.GREATER_EQ)
if vertical_sel:
psel_vm = self.connect_to_tracks(psel, TrackID(vm_layer, vm_tidx, width=vm_w),
min_len_mode=MinLenMode.LOWER)
self.add_pin('psel_vm', psel_vm)
self.add_pin('in', in_warr)
self.add_pin('nsel', nsel)
self.add_pin('psel', psel)
# mid
pmid = self.connect_to_tracks(ptri.d, pd_tid, min_len_mode=MinLenMode.UPPER)
nmid = self.connect_to_tracks(ntri.d, nd_tid, min_len_mode=MinLenMode.UPPER)
vm_tidx = tr_manager.get_next_track(vm_layer, vm_tidx, 'sig', 'sig', up=True)
mid = self.connect_to_tracks([pmid, nmid], TrackID(vm_layer, vm_tidx, width=vm_w))
mid = self.connect_to_tracks([mid, nbuf.g, pbuf.g], pmid_tid)
self.add_pin('mid', mid)
# output
pout = self.connect_to_tracks(pbuf.d, pd_tid, min_len_mode=MinLenMode.UPPER)
nout = self.connect_to_tracks(nbuf.d, nd_tid, min_len_mode=MinLenMode.UPPER)
vm_tidx = grid.coord_to_track(vm_layer, self.bound_box.xh)
out = self.connect_to_tracks([pout, nout], TrackID(vm_layer, vm_tidx, width=vm_w))
self.add_pin('out', out)
lch = self.arr_info.lch
th_p = self.get_row_info(ridx_p, 0).threshold
th_n = self.get_row_info(ridx_n, 0).threshold
self.sch_params = dict(
inv_params=ImmutableSortedDict(dict(
lch=lch,
seg_p=seg_buf,
seg_n=seg_buf,
w_p=w_pbuf,
w_n=w_nbuf,
th_p=th_p,
th_n=th_n,
)),
tri_params=ImmutableSortedDict(dict(
lch=lch,
seg=seg_tri,
w_p=w_ptri,
w_n=w_ntri,
th_p=th_p,
th_n=th_n,
)),
)
[docs] def _get_w_dict(self, ridx_n: int, ridx_p: int) -> Mapping[str, int]:
w_dict: Mapping[str, int] = self.params['w_dict']
w_ans = {}
for row_idx, name_list in [(ridx_n, ['ntri', 'nbuf']),
(ridx_p, ['ptri', 'pbuf'])]:
rinfo = self.get_row_info(row_idx, 0)
w_default = rinfo.width
for name in name_list:
w_ans[name] = w_dict.get(name, w_default)
return w_ans
[docs]class Mux2to1Matched(MOSBase):
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__mux2to1_matched
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]:
return Mux2to1MatchedHalf.get_params_info()
@classmethod
[docs] def get_default_param_values(cls) -> Dict[str, Any]:
return Mux2to1MatchedHalf.get_default_param_values()
[docs] def draw_layout(self) -> None:
master: Mux2to1MatchedHalf = self.new_template(Mux2to1MatchedHalf, params=self.params)
self.draw_base(master.draw_base_info)
ridx_n: int = self.params['ridx_n']
vertical_sel: bool = self.params['vertical_sel']
# placement
nhalf = master.num_cols
corel = self.add_tile(master, 0, 0)
corer = self.add_tile(master, 0, 2 * nhalf, flip_lr=True)
self.set_mos_size(num_cols=2 * nhalf)
# routing
hm_layer = self.conn_layer + 1
tr_manager = self.tr_manager
hm_w = tr_manager.get_width(hm_layer, 'sig')
ng0_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)
ng1_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1)
selb_tidx = ng0_tidx
sel_tidx = ng1_tidx
# simple re-exports
for name in ['VDD', 'VSS']:
self.add_pin(name, self.connect_wires([corel.get_pin(name), corer.get_pin(name)]))
self.connect_wires([corel.get_pin('mid'), corer.get_pin('mid')])
self.reexport(corel.get_port('in'), net_name='in<0>')
self.reexport(corer.get_port('in'), net_name='in<1>')
self.reexport(corel.get_port('out'), net_name='out')
# select signals
nselb = corel.get_pin('nsel')
psel = corel.get_pin('psel')
nsel = corer.get_pin('nsel')
pselb = corer.get_pin('psel')
nsel, nselb = self.connect_differential_tracks(nsel, nselb, hm_layer,
sel_tidx, selb_tidx, width=hm_w)
self.add_pin('psel', psel, hide=True)
self.add_pin('pselb', pselb, hide=True)
self.add_pin('nsel', nsel, hide=True)
self.add_pin('nselb', nselb, hide=True)
if vertical_sel:
sel, selb = self.connect_differential_wires(nsel, nselb, corel.get_pin('psel_vm'),
corer.get_pin('psel_vm'))
self.add_pin('sel', sel)
self.add_pin('selb', selb)
else:
self.add_pin('sel', [psel, nsel], connect=True)
self.add_pin('selb', [pselb, nselb], connect=True)
self.sch_params = master.sch_params
[docs]class Mux2to1Core(MOSBase):
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__mux2to1_matched
@classmethod
[docs] def get_params_info(cls) -> Dict[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg='number of segments for the tristate inverter',
sel_seg='number of segments used for side inverters (select inverters)',
fout='fanout of the output inverter',
w_p='pmos width, can be list or integer if all widths are the same.',
w_n='pmos width, can be list or integer if all widths are the same.',
ridx_p='pmos row index.',
ridx_n='nmos row index.',
sig_locs='Optional dictionary of user defined signal locations',
vertical_out='True to have output on vertical layer',
)
@classmethod
[docs] def get_default_param_values(cls) -> Dict[str, Any]:
return dict(
w_p=0,
w_n=0,
ridx_p=-1,
ridx_n=0,
sig_locs=None,
vertical_out=True,
)
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
self.draw_base(pinfo)
hm_layer = self.conn_layer + 1
vm_layer = hm_layer + 1
if pinfo.top_layer < vm_layer:
raise ValueError(f'MOSBasePlaceInfo top layer must be at least {vm_layer}')
seg: int = self.params['seg']
sel_seg: int = self.params['sel_seg']
fout: int = self.params['fout']
w_p: Union[int, Sequence[int]] = self.params['w_p']
w_n: Union[int, Sequence[int]] = self.params['w_n']
ridx_p: int = self.params['ridx_p']
ridx_n: int = self.params['ridx_n']
sig_locs: Optional[Dict[str, float]] = self.params['sig_locs']
vertical_out: bool = self.params['vertical_out']
if seg % 2 != 0:
raise ValueError(f'Mux2to1: seg = {seg} is not even')
if sel_seg % 2 != 0:
raise ValueError(f'Mux2to1: dummy_seg = {sel_seg} is not even')
if sig_locs is None:
sig_locs = {}
inv_seg = int(seg * fout)
en_tidx = sig_locs.get('nen', self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig',
wire_idx=0))
in0_tidx = sig_locs.get('nin0', self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig',
wire_idx=1))
in1_tidx = sig_locs.get('pin1', self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig',
wire_idx=2))
enb_tidx = sig_locs.get('penb', self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig',
wire_idx=1))
tristate0_params = dict(pinfo=pinfo, seg=seg, w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, vertical_out=False,
sig_locs={'nen': en_tidx, 'nin': in0_tidx, 'pen': enb_tidx})
tristate0_master = self.new_template(InvTristateCore, params=tristate0_params)
tristate1_params = dict(pinfo=pinfo, seg=seg, w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, vertical_out=False,
sig_locs={'nen': en_tidx, 'nin': in1_tidx, 'pen': enb_tidx})
tristate1_master = self.new_template(InvTristateCore, params=tristate1_params)
in_tidx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=1)
out_inv_sig_locs = {'pin': in_tidx}
for key in ('pout', 'nout'):
if key in sig_locs:
out_inv_sig_locs[key] = sig_locs[key]
out_inv_params = dict(pinfo=pinfo, seg=inv_seg, w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, sig_locs=out_inv_sig_locs, vertical_out=vertical_out)
out_inv_master = self.new_template(InvCore, params=out_inv_params)
in_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=2)
sel_inv_sig_locs = {'nin': in_tidx}
for key in ('pout', 'nout'):
if f'sel_{key}' in sig_locs:
sel_inv_sig_locs[key] = sig_locs[f'sel_{key}']
sel_inv_params = dict(pinfo=pinfo, seg=sel_seg, w_p=w_p, w_n=w_n, ridx_p=ridx_p,
ridx_n=ridx_n, sig_locs=sel_inv_sig_locs)
sel_inv_master = self.new_template(InvCore, params=sel_inv_params)
sel_ncol = sel_inv_master.num_cols
tristate_ncols = tristate0_master.num_cols
out_inv_ncols = out_inv_master.num_cols
sep = max(self.get_hm_sp_le_sep_col(), self.min_sep_col)
# --- Placement --- #
cur_col = 0
sel = self.add_tile(sel_inv_master, 0, cur_col)
cur_col += sel_ncol + sep
t0 = self.add_tile(tristate0_master, 0, cur_col)
cur_col += tristate_ncols + sep
t1 = self.add_tile(tristate1_master, 0, cur_col)
cur_col += tristate_ncols + sep
out_inv = self.add_tile(out_inv_master, 0, cur_col)
cur_col += out_inv_ncols
self.set_mos_size()
# --- Routing --- #
tr_manager = pinfo.tr_manager
tr_w_v = tr_manager.get_width(vm_layer, 'sig')
# vdd/vss
vdd_list, vss_list = [], []
inst_arr = [sel, t0, t1, out_inv, t1]
for inst in inst_arr:
vdd_list += inst.get_all_port_pins('VDD')
vss_list += inst.get_all_port_pins('VSS')
vdd_list = self.connect_wires(vdd_list)
vss_list = self.connect_wires(vss_list)
# connect sel and selb
sel_warr = sel.get_pin('nin')
selb_idx = sig_locs.get('pselb', self.get_track_index(ridx_p, MOSWireType.G,
wire_name='sig', wire_idx=0))
selb_tid = TrackID(hm_layer, selb_idx)
selb_warr = self.connect_to_tracks(sel.get_pin('out'), selb_tid)
# connect right en and left enb differentially
sel_vms = sel.get_all_port_pins('in', self.conn_layer)
t0_en = t0.get_pin('en')
t1_en = t1.get_pin('en')
l_en_tidx = self.grid.coord_to_track(vm_layer, t0_en.lower, RoundMode.LESS_EQ)
selb_vm = self.connect_to_tracks(t0_en, TrackID(vm_layer, l_en_tidx, width=tr_w_v))
r_en_tidx = self.grid.coord_to_track(vm_layer, t1_en.upper, RoundMode.GREATER_EQ)
sel_vm = self.connect_to_tracks(t1_en, TrackID(vm_layer, r_en_tidx, width=tr_w_v))
self.connect_differential_wires(sel_vm, selb_vm, sel_warr, selb_warr)
sel_vms.append(sel_vm)
# connect right enb and left en differentially
t0_enb = t0.get_pin('enb')
t1_enb = t1.get_pin('enb')
l_enb_tidx = self.grid.coord_to_track(vm_layer, t0_enb.upper, RoundMode.GREATER_EQ)
sel_vm = self.connect_to_tracks(t0_enb, TrackID(vm_layer, l_enb_tidx, width=tr_w_v))
sel_vms.append(sel_vm)
r_en_tidx = self.grid.coord_to_track(vm_layer, t1_enb.lower, RoundMode.LESS_EQ)
selb_vm = self.connect_to_tracks(t1_enb, TrackID(vm_layer, r_en_tidx, width=tr_w_v))
self.connect_differential_wires(sel_vm, selb_vm, sel_warr, selb_warr)
self.add_pin('nsel', sel_warr, hide=True)
self.add_pin('psel', sel_warr, hide=True)
# connect outb to out
if vertical_out:
out_idx = out_inv.get_pin('out').track_id.base_index
mux_out_idx = tr_manager.get_next_track(vm_layer, out_idx, 'sig', 'sig', up=False)
else:
out_hm = out_inv.get_pin('nout')
mux_out_idx = self.grid.coord_to_track(vm_layer, out_hm.middle,
mode=RoundMode.NEAREST)
mux_out_warrs = [t0.get_pin('nout'), t0.get_pin('pout'), t1.get_pin('nout'),
t1.get_pin('pout'), out_inv.get_pin('nin')]
self.connect_to_tracks(mux_out_warrs, TrackID(vm_layer, mux_out_idx, width=tr_w_v))
# add pins
self.add_pin('VDD', vdd_list)
self.add_pin('VSS', vss_list)
self.add_pin('sel', sel_warr)
self.reexport(t0.get_port('nin'), net_name='in<0>', hide=False)
self.reexport(t1.get_port('pin'), net_name='in<1>', hide=False)
if vertical_out:
self.reexport(out_inv.get_port('out'), net_name='out')
self.reexport(out_inv.get_port('pout'), label='out:', hide=vertical_out)
self.reexport(out_inv.get_port('nout'), label='out:', hide=vertical_out)
self.sch_params = dict(sel_params=sel_inv_master.sch_params,
inv_params=out_inv_master.sch_params,
tri_params=tristate0_master.sch_params)