"""This module contains layout generators for complex AND gate with 2 - 9 inputs"""
from typing import Mapping, Any, Optional, Type, Sequence, List, Tuple
from pybag.enum import MinLenMode, RoundMode
from bag.util.immutable import Param
from bag.design.module import Module
from bag.layout.template import TemplateDB
from bag.layout.routing.base import TrackID, WireArray
from xbase.layout.enum import MOSWireType
from xbase.layout.mos.base import MOSBasePlaceInfo, MOSBase
from .gates import NAND2Core, NAND3Core, NOR2Core, NOR3Core, InvCore
from .logic_unit import LogicUnit
from ...schematic.and_complex import bag3_digital__and_complex
[docs]class AndComplexRow(MOSBase):
"""A complex AND for B2T decoding. Used for passgate mux row decoder"""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
num_in: int = params['num_in']
self._nand_in_list = get_nand_in_list(num_in)
@property
[docs] def nand_in_list(self) -> Sequence[int]:
return self._nand_in_list
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]:
return bag3_digital__and_complex
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg_dict='Dictionary of segments of standard cell components',
num_in='Number of inputs',
export_outb='True to export outb; True by default',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(export_outb=True)
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
self.draw_base(pinfo)
seg_dict: Mapping[str, int] = self.params['seg_dict']
num_in: int = self.params['num_in']
export_outb: bool = self.params['export_outb']
if num_in < 2 or num_in > 9:
raise ValueError(f'num_in={num_in} has to be within 2 and 9.')
sep_col = self.min_sep_col
sep_col += sep_col & 1
hm_layer = self.conn_layer + 1
vm_layer = hm_layer + 1
xm_layer = vm_layer + 1
# pick hm_layer tracks
pd_tidx = self.get_track_index(1, MOSWireType.DS, 'sig', 1)
pg1_tid = self.get_track_id(1, MOSWireType.G, 'sig', -1)
pg1_tidx = self.get_track_index(1, MOSWireType.G, 'sig', -1)
pg0_tidx = self.get_track_index(1, MOSWireType.G, 'sig', -2)
ng1_tidx = self.get_track_index(0, MOSWireType.G, 'sig', 1)
ng0_tidx = self.get_track_index(0, MOSWireType.G, 'sig', 0)
ng0_tid = self.get_track_id(0, MOSWireType.G, 'sig', 0)
nd_tidx = self.get_track_index(0, MOSWireType.DS, 'sig', -2)
sig_locs_inv = {'nin': ng0_tidx, 'nin0': ng0_tidx, 'nin1': ng1_tidx, 'nin2': pg0_tidx, 'nout': nd_tidx,
'pout': pd_tidx}
sig_locs_nor = {'nin': pg1_tidx, 'nin0': pg1_tidx, 'nin1': pg0_tidx, 'nin2': ng1_tidx, 'nout': nd_tidx,
'pout': pd_tidx}
# nand configuration
nand_in_list = self._nand_in_list
nor_in = len(nand_in_list)
nand_params = dict(pinfo=pinfo, seg=seg_dict['nand'], vertical_out=False)
nand_out_list = []
nand_sch_list = []
cur_col = 0
in_idx = 0
vdd_list, vss_list = [], []
prev_out_vm_tidx = self.grid.coord_to_track(vm_layer, 0, RoundMode.NEAREST)
prev_out_vm_tidx = self.tr_manager.get_next_track(vm_layer, prev_out_vm_tidx, 'sig', 'sig', up=-1)
w_sig_vm = self.tr_manager.get_width(vm_layer, 'sig')
for nand_idx, nand_in in enumerate(nand_in_list):
# make master
back_idx = nor_in - 1 - nand_idx
nand_params['sig_locs'] = sig_locs_nor if (back_idx & 1) else sig_locs_inv
if nand_in == 3:
_master = self.new_template(NAND3Core, params=nand_params)
else: # nand_n == 2
_master = self.new_template(NAND2Core, params=nand_params)
# find placement based on nand width and also vm_layer wires
out_vm_tidx = self.tr_manager.get_next_track(vm_layer, prev_out_vm_tidx, 'sig', 'sig', up=nand_in * 2 + 1)
out_vm_tidx2 = self.grid.coord_to_track(vm_layer, self.sd_pitch * (cur_col + _master.num_cols),
RoundMode.NEAREST)
if out_vm_tidx > out_vm_tidx2:
_coord = self.grid.track_to_coord(vm_layer, out_vm_tidx)
avail_col = -(- _coord // self.sd_pitch) - _master.num_cols
offset = avail_col - cur_col
cur_col += offset + (offset & 1)
out_vm_tidx = self.grid.coord_to_track(vm_layer, self.sd_pitch * (cur_col + _master.num_cols),
RoundMode.NEAREST)
else:
out_vm_tidx = out_vm_tidx2
_inst = self.add_tile(_master, 0, cur_col)
nand_sch_list.append(_master.sch_params)
# supplies
vdd_list.append(_inst.get_pin('VDD'))
vss_list.append(_inst.get_pin('VSS'))
# nand inputs
for _in_idx in range(nand_in):
self.reexport(_inst.get_port(f'nin<{_in_idx}>'), net_name=f'in<{in_idx}>', hide=False)
in_idx += 1
# nand output
nand_out_vm = self.connect_to_tracks([_inst.get_pin('pout'), _inst.get_pin('nout')],
TrackID(vm_layer, out_vm_tidx, w_sig_vm))
nand_out_list.append(nand_out_vm)
self.add_pin(f'nand_out{nand_idx}', nand_out_vm, hide=True)
# setup for next instance
cur_col += _master.num_cols + sep_col
prev_out_vm_tidx = out_vm_tidx
# nor configuration
if nor_in == 3:
nor_master = self.new_template(NOR3Core,
params=dict(pinfo=pinfo, seg=seg_dict['nor'], vertical_out=False,
sig_locs=sig_locs_nor))
elif nor_in == 2:
nor_master = self.new_template(NOR2Core,
params=dict(pinfo=pinfo, seg=seg_dict['nor'], vertical_out=False,
sig_locs=sig_locs_nor))
else:
nor_master = self.new_template(InvCore,
params=dict(pinfo=pinfo, seg=seg_dict['inv'], vertical_out=False,
sig_locs=sig_locs_nor))
# find placement based on nor width and also vm_layer wires
out_vm_tidx = self.tr_manager.get_next_track(vm_layer, prev_out_vm_tidx, 'sig', 'sig', up=nor_in + 1)
out_vm_tidx2 = self.grid.coord_to_track(vm_layer, self.sd_pitch * (cur_col + nor_master.num_cols),
RoundMode.NEAREST)
if out_vm_tidx > out_vm_tidx2:
_coord = self.grid.track_to_coord(vm_layer, out_vm_tidx)
avail_col = -(- _coord // self.sd_pitch) - nor_master.num_cols
offset = avail_col - cur_col
cur_col += offset + (offset & 1)
out_vm_tidx = self.grid.coord_to_track(vm_layer, self.sd_pitch * (cur_col + nor_master.num_cols),
RoundMode.NEAREST)
else:
out_vm_tidx = out_vm_tidx2
nor = self.add_tile(nor_master, 0, cur_col)
cur_col += nor_master.num_cols
vdd_list.append(nor.get_pin('VDD'))
vss_list.append(nor.get_pin('VSS'))
# nor output
out_vm = self.connect_to_tracks([nor.get_pin('pout'), nor.get_pin('nout')],
TrackID(vm_layer, out_vm_tidx, w_sig_vm))
out_hm = self.connect_to_tracks(out_vm, ng0_tid, min_len_mode=MinLenMode.UPPER)
# nor inputs: find xm_layer tracks
vdd_xm_tidx = self.grid.coord_to_track(xm_layer, vdd_list[-1].bound_box.ym, RoundMode.NEAREST)
vss_xm_tidx = self.grid.coord_to_track(xm_layer, vss_list[-1].bound_box.ym, RoundMode.NEAREST)
xm_locs = self.tr_manager.spread_wires(xm_layer, ['sup'] + ['sig'] * nor_in + ['sup'], vss_xm_tidx,
vdd_xm_tidx, ('sup', 'sig'))
vm_locs = self.tr_manager.spread_wires(vm_layer, ['sig'] * (nor_in + 2), prev_out_vm_tidx, out_vm_tidx,
('sig', 'sig'))
w_sig_xm = self.tr_manager.get_width(xm_layer, 'sig')
for _idx in range(nor_in):
_in_name = 'nin' if nor_in == 1 else f'nin<{_idx}>'
_nor_in = self.connect_to_tracks(nor.get_pin(_in_name), TrackID(vm_layer, vm_locs[1 + _idx],
w_sig_vm), min_len_mode=MinLenMode.MIDDLE)
self.connect_to_tracks([_nor_in, nand_out_list[_idx]], TrackID(xm_layer, xm_locs[-2 - _idx], w_sig_xm))
# output inverter
if export_outb:
cur_col += sep_col
inv_master = self.new_template(InvCore,
params=dict(pinfo=pinfo, seg=seg_dict['inv'],
sig_locs=sig_locs_inv))
out_inv = self.add_tile(inv_master, 0, cur_col)
cur_col += inv_master.num_cols
inv_sch = inv_master.sch_params
vdd_list.append(out_inv.get_pin('VDD'))
vss_list.append(out_inv.get_pin('VSS'))
out_hm = self.connect_wires([out_hm, out_inv.get_pin('nin')])[0]
outb_hm = self.connect_to_tracks(out_inv.get_pin('out'), pg1_tid, min_len_mode=MinLenMode.UPPER)
self.add_pin('outb', outb_hm)
else:
inv_sch = None
self.add_pin('out', out_hm)
self.set_mos_size()
# supplies
self.add_pin('VDD', self.connect_wires(vdd_list))
self.add_pin('VSS', self.connect_wires(vss_list))
self.sch_params = dict(
nand_params_list=nand_sch_list,
nor_params=nor_master.sch_params,
inv_params=inv_sch,
export_outb=export_outb,
)
[docs]class AndComplexCol(MOSBase):
"""A complex AND for B2T decoding. Used for passgate mux column decoder"""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
num_in: int = params['num_in']
self._nand_in_list = get_nand_in_list(num_in)
@property
[docs] def nand_in_list(self) -> Sequence[int]:
return self._nand_in_list
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]:
return bag3_digital__and_complex
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg_dict='Dictionary of segments of standard cell components',
num_in='Number of inputs',
export_outb='True to export outb; True by default',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(export_outb=True)
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
self.draw_base(pinfo)
seg_dict: Mapping[str, int] = self.params['seg_dict']
num_in: int = self.params['num_in']
export_outb: bool = self.params['export_outb']
if num_in < 2 or num_in > 9:
raise ValueError(f'num_in={num_in} has to be within 2 and 9.')
sep_col = self.min_sep_col
sep_col += sep_col & 1
hm_layer = self.conn_layer + 1
vm_layer = hm_layer + 1
# nand configuration
nand_in_list = self._nand_in_list
nor_in = len(nand_in_list)
nand_params = dict(pinfo=pinfo, seg=seg_dict['nand'], vertical_out=False)
nand_sch_list = []
nand_master_list = []
nand_ncols = 0
num_vm = nor_in + 1 # number of vm_layer wires for nor
# make masters
for nand_idx, nand_in in enumerate(nand_in_list):
if nand_in == 3:
_master = self.new_template(NAND3Core, params=nand_params)
else: # nand_n == 2
_master = self.new_template(NAND2Core, params=nand_params)
nand_master_list.append(_master)
nand_sch_list.append(_master.sch_params)
nand_ncols = max(nand_ncols, _master.num_cols)
num_vm = max(num_vm, nand_in + nand_idx + 1) # number of vm_layer wires for nand in this row,
# and nand output of previous row
# nor configuration
if nor_in == 3:
nor_master = self.new_template(NOR3Core,
params=dict(pinfo=pinfo, seg=seg_dict['nor'], vertical_out=False))
elif nor_in == 2:
nor_master = self.new_template(NOR2Core,
params=dict(pinfo=pinfo, seg=seg_dict['nor'], vertical_out=False))
else:
nor_master = self.new_template(InvCore,
params=dict(pinfo=pinfo, seg=seg_dict['inv'], vertical_out=False))
nor_ncols = nor_master.num_cols
# output inverter
if export_outb:
inv_master = self.new_template(InvCore, params=dict(pinfo=pinfo, seg=seg_dict['inv'], vertical_out=False))
inv_ncols = inv_master.num_cols
inv_sch = inv_master.sch_params
else:
inv_master = None
inv_ncols = 0
inv_sch = None
# size
logic_ncols = max(nand_ncols, nor_ncols, inv_ncols)
num_vm_tr, vm_locs = self.tr_manager.place_wires(vm_layer, ['sig'] * num_vm)
vm_ncols = self.grid.track_to_coord(vm_layer, num_vm_tr) // self.sd_pitch
tot_ncols = max(logic_ncols, vm_ncols)
# --- Placement --- #
tile_idx = 0
w_sig_vm = self.tr_manager.get_width(vm_layer, 'sig')
nor_in_list = []
in_idx = 0
vdd_list, vss_list = [], []
for _master in nand_master_list:
_nand = self.add_tile(_master, tile_idx, nand_ncols, flip_lr=True)
# output on vm_layer
nor_in_list.append(self.connect_to_tracks([_nand.get_pin('pout'), _nand.get_pin('nout')],
TrackID(vm_layer, vm_locs[tile_idx], w_sig_vm)))
# input on vm_layer
nand_in = nand_in_list[tile_idx]
for _in_idx in range(nand_in):
vm_idx = -1 - _in_idx
_in = self.connect_to_tracks(_nand.get_pin(f'nin<{_in_idx}>'),
TrackID(vm_layer, vm_locs[vm_idx], w_sig_vm),
min_len_mode=MinLenMode.MIDDLE)
self.add_pin(f'in<{in_idx}>', _in)
in_idx += 1
# supplies
vdd_list.append(_nand.get_pin('VDD'))
vss_list.append(_nand.get_pin('VSS'))
# setup for next iterationaaa
tile_idx += 1
nor = self.add_tile(nor_master, tile_idx, 0)
# nor inputs
if nor_in == 1:
self.connect_to_track_wires(nor.get_pin('nin'), nor_in_list[0])
else:
for _idx, _nor_in in enumerate(nor_in_list):
self.connect_to_track_wires(nor.get_pin(f'nin<{_idx}>'), nor_in_list[_idx])
# nor output
out_vm = self.connect_to_tracks([nor.get_pin('pout'), nor.get_pin('nout')],
TrackID(vm_layer, vm_locs[nor_in], w_sig_vm))
# supplies
vdd_list.append(nor.get_pin('VDD'))
vss_list.append(nor.get_pin('VSS'))
tile_idx += 1
if export_outb:
inv_col = -(- max(nor_ncols, inv_ncols)) // 2 * 2
inv = self.add_tile(inv_master, tile_idx, inv_col, flip_lr=True)
# input
out_vm = self.connect_to_track_wires(inv.get_pin('nin'), out_vm)
# output
outb_vm = self.connect_to_tracks([inv.get_pin('pout'), inv.get_pin('nout')],
TrackID(vm_layer, vm_locs[nor_in - 1], w_sig_vm))
self.add_pin('outb', outb_vm)
# supplies
vdd_list.append(inv.get_pin('VDD'))
vss_list.append(inv.get_pin('VSS'))
tile_idx += 1
self.add_pin('out', out_vm)
self.set_mos_size(tot_ncols, tile_idx)
# supplies
self.add_pin('VDD', self.connect_wires(vdd_list))
self.add_pin('VSS', self.connect_wires(vss_list))
self.sch_params = dict(
nand_params_list=nand_sch_list,
nor_params=nor_master.sch_params,
inv_params=inv_sch,
export_outb=export_outb,
)
[docs]class AndComplexColTall(MOSBase):
"""A complex AND for B2T decoding. Used for passgate mux column decoder
Based on AndDiffRow from BAG2
"""
def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
MOSBase.__init__(self, temp_db, params, **kwargs)
num_in: int = params['num_in']
self._nand_in_list = get_nand_in_list(num_in)
@property
[docs] def nand_in_list(self) -> Sequence[int]:
return self._nand_in_list
@classmethod
[docs] def get_schematic_class(cls) -> Optional[Type[Module]]:
return bag3_digital__and_complex
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
seg_dict='Dictionary of segments of standard cell components',
num_in='Number of inputs',
export_outb='True to export outb; True by default',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(export_outb=True)
[docs] def get_layout_basename(self) -> str:
return f"androw_diff{self.params['num_in']}_{self.params['seg_dict']['nand']}x"
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
self.draw_base(pinfo)
seg_dict: Mapping[str, int] = self.params['seg_dict']
num_in: int = self.params['num_in']
export_outb: bool = self.params['export_outb']
# TODO: do we need to parametrize these at all?
seg = 2
sig_locs = {}
out_vm = True
if num_in < 2 or num_in > 9:
raise ValueError(f'num_in={num_in} has to be within 2 and 9.')
sep_col = self.min_sep_col
sep_col += sep_col & 1
# calculate the number of nand gates
nand_nin_list = self._nand_in_list
nor_in = num_nand = len(nand_nin_list)
num_nor_unit = 1 if num_nand < 2 else num_nand
num_unit = sum(nand_nin_list) + num_nor_unit
if export_outb:
num_unit += 1
# compute track locations
hm_layer = self.conn_layer + 1
vm_layer = hm_layer + 1
tr_manager = self.tr_manager
tr_w_out_v = tr_manager.get_width(vm_layer, 'sig')
# Create templates
params = self.params.copy(append=dict(seg=seg), remove=seg_dict)
unit_template = self.new_template(params=params, temp_cls=LogicUnit)
# Size. Set either by logic or by wires
logic_ncols = unit_template.num_cols
# Compute number of wires required
num_nand_int = 2 # internal to nands, alternatings. Outputs are in nand_nor
num_nand_nor = nor_in # number of connects from nand to nor
num_nor_int = nor_in # internal to nand, plus the output wire
num_vm = max(num_nand_int, num_nor_int) + num_nand_nor
num_vm_tr, vm_locs = self.tr_manager.place_wires(vm_layer, ['sig'] * num_vm)
vm_ncols = self.grid.track_to_coord(vm_layer, num_vm_tr) // self.sd_pitch
tot_ncols = max(logic_ncols, vm_ncols)
self.set_mos_size(tot_ncols)
# ===== Placement =====
# add instances
unit_inst = []
for idy in range(num_unit):
unit_inst.append(self.add_tile(unit_template, idy, 0))
# make NAND/NOR gates
tile_idx = 0
in_list, vss_list, vdd_list, mid_list = [], [], [], []
# Make NANDS
for cur_nin in nand_nin_list:
nand_tmp = self._make_nand_row(cur_nin, sig_locs, tile_idx, unit_inst, vm_locs, vss_list, vdd_list)
nand_in_warr, nand_out_warr = nand_tmp
in_list += nand_in_warr
mid_list += nand_out_warr
tile_idx += cur_nin
# Make NOR
# Make "1-input nor" = inv
if num_nand == 1:
nor_tmp = self._make_inv_row(sig_locs, tile_idx, unit_inst, vm_locs, vss_list, vdd_list)
else:
nor_tmp = self._make_nor_row(num_nand, sig_locs, tile_idx, unit_inst, vm_locs, vss_list, vdd_list)
nor_in_warr, nor_out_warr = nor_tmp
tile_idx += num_nand
# make outb inverter
inv2_in_warr, inv2_out_warr = None, None
if export_outb:
inv_tmp = self._make_inv_row(sig_locs, tile_idx, unit_inst, vm_locs, vss_list, vdd_list)
inv2_in_warr, inv2_out_warr = inv_tmp
# ===== Routing =====
# export input
in_shift = get_in_shift(num_in)
w_sig_vm = tr_manager.get_width(vm_layer, 'sig')
for idy in range(num_in):
pname = 'in<%d>' % idy
vm_tidx = vm_locs[in_shift[idy]]
_in = self.connect_to_tracks(in_list[idy], TrackID(vm_layer, vm_tidx, w_sig_vm),
min_len_mode=MinLenMode.MIDDLE)
self.add_pin(pname, _in)
# connect mid wires
idx_nand_nor_start = max(num_nand_int, num_nor_int)
idx = 0
for num_mid in range(num_nand):
cur_nin = nand_nin_list[num_mid]
mid_warr = mid_list[idx:cur_nin + 1 + idx]
idx = cur_nin + 1 + idx
mid_warr.append(nor_in_warr[num_mid])
col_idx = idx_nand_nor_start + num_mid
mid_tidy = vm_locs[col_idx]
tid = TrackID(vm_layer, mid_tidy, width=tr_w_out_v)
self.connect_to_tracks(mid_warr, tid)
if export_outb:
nor_out_warr.append(inv2_in_warr)
# connect out wire
out, outb = nor_out_warr, inv2_out_warr
if out_vm:
# Get the highest NOR wire, preallocated
col_idx = num_nor_int - 1
out_tidy = vm_locs[col_idx]
tid = TrackID(vm_layer, out_tidy, width=tr_w_out_v)
out_warrs = nor_out_warr
if export_outb:
out_warrs.append(inv2_in_warr)
out = self.connect_to_tracks(out_warrs, tid)
if export_outb:
# There should be room to the left of the out
col_idx = num_nor_int - 2
mid_tidy = vm_locs[col_idx]
tid = TrackID(vm_layer, mid_tidy, width=tr_w_out_v)
outb = self.connect_to_tracks(inv2_out_warr, tid)
# export output
self.add_pin('out', out)
if export_outb:
self.add_pin('outb', outb)
# connect/export VSS/VDD
self.add_pin('VSS', vss_list, connect=True)
self.add_pin('VDD', vdd_list, connect=True)
# ===== Schematic Params =====
ridx_n, ridx_p = 0, -1
base_params = dict(
lch=self.place_info.lch,
w_p=self.place_info.get_row_place_info(ridx_p).row_info.width,
w_n=self.place_info.get_row_place_info(ridx_n).row_info.width,
th_n=self.place_info.get_row_place_info(ridx_n).row_info.threshold,
th_p=self.place_info.get_row_place_info(ridx_p).row_info.threshold,
stack_p=1,
stack_n=1,
)
nand_sch_list = []
for nand_in in self.nand_in_list:
params = base_params.copy()
params.update(dict(
num_in=nand_in,
seg_p=seg_dict['nand'],
seg_n=seg_dict['nand'],
shared_mid=True,
))
nand_sch_list.append(params)
nor_params = base_params.copy()
nor_params.update(dict(
num_in=nor_in,
seg_p=seg_dict['nor'],
seg_n=seg_dict['nor'],
shared_mid=True,
))
inv_params = base_params.copy()
inv_params.update(dict(
num_in=1,
seg_p=seg_dict['inv'],
seg_n=seg_dict['inv'],
))
nor_sch = nor_params if nor_in > 1 else inv_params
inv_sch = inv_params if export_outb else None
self.sch_params = dict(
nand_params_list=nand_sch_list,
nor_params=nor_sch,
inv_params=inv_sch,
export_outb=export_outb,
)
"""
_make_nand_row, _make_nor_row, and _make_inv_row all have the same signature.
Given some inputs, connect up the appropriate, save VDD + VSS, and return the inputs + outputs
"""
[docs] def _make_nand_row(self, num_in: int, sig_locs: dict, tile_idx: int, unit_inst: list,
vm_locs: list, vss_warr: list, vdd_warr: list) -> Tuple[List[WireArray], List[WireArray]]:
# get sig locations
gate_name = 'nand%d' % num_in
in_tidx = []
pout_tidx = []
nmid_tidxs = []
nmid_tidxd = []
nmid_tidy = []
for idx in range(num_in):
name = '_in%d' % idx
in_tidx.append(sig_locs.get(gate_name + name, None))
name = '_pout%d' % idx
pout_tidx.append(sig_locs.get(gate_name + name, None))
name = '_nmidxs%d' % idx
nmid_tidxs.append(sig_locs.get(gate_name + name, None))
name = '_nmidxd%d' % idx
nmid_tidxd.append(sig_locs.get(gate_name + name, None))
for idy in range(num_in - 1):
name = '_nmidy%d' % idy
nmid_tidy.append(sig_locs.get(gate_name + name, None))
nout_tidx = sig_locs.get(gate_name + 'nout', None)
# get track locations
nidx, pidx = 0, -1
for idy in range(num_in):
if in_tidx[idy] is None:
in_tidx[idy] = self.get_track_index(nidx, MOSWireType.G, 'sig', wire_idx=-1, tile_idx=idy + tile_idx)
if pout_tidx[idy] is None:
pout_tidx[idy] = self.get_track_index(pidx, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=idy + tile_idx)
if nmid_tidxs[idy] is None:
nmid_tidxs[idy] = self.get_track_index(nidx, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=idy + tile_idx)
if nmid_tidxd[idy] is None:
nmid_tidxd[idy] = self.get_track_index(nidx, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=idy + tile_idx)
if nout_tidx is None:
nout_tidx = self.get_track_index(nidx, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile_idx + num_in-1)
# compute track locations
tr_manager = self.tr_manager
hm_layer = self.conn_layer + 1
vm_layer = hm_layer + 1
tr_w_in = tr_manager.get_width(hm_layer, 'sig')
tr_w_nmid_h = tr_manager.get_width(hm_layer, 'sig')
tr_w_nmid_v = tr_manager.get_width(vm_layer, 'sig')
tr_w_out_h = tr_manager.get_width(hm_layer, 'sig')
# connect input, VDD/VSS, output
in_warr, out_warr = [], []
for idy in range(num_in):
_ctidx = idy + tile_idx # Current tile index
# connect input
tid = TrackID(hm_layer, in_tidx[idy], width=tr_w_in)
in_warr.append(self.connect_to_tracks([unit_inst[_ctidx].get_pin('pg'), unit_inst[_ctidx].get_pin('ng')],
tid, min_len_mode=MinLenMode.MIDDLE))
# connect VDD/VSS
vss_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('ns'),
unit_inst[_ctidx].get_pin('VSS').track_id)
if idy == 0 else unit_inst[idy + tile_idx].get_pin('VSS'))
vdd_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('ps'),
unit_inst[_ctidx].get_pin('VDD').track_id))
# connect output
tid = TrackID(hm_layer, pout_tidx[idy], width=tr_w_out_h)
out_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('pd'),
tid, min_len_mode=MinLenMode.MIDDLE))
if idy == num_in - 1:
tid = TrackID(hm_layer, nout_tidx, width=tr_w_out_h)
out_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('nd'),
tid, min_len_mode=MinLenMode.MIDDLE))
# connect mid wires
nmid_warr = []
for idy in range(num_in):
_ctidx = idy + tile_idx # Current tile index
tids = TrackID(hm_layer, nmid_tidxs[idy], width=tr_w_nmid_h)
tidd = TrackID(hm_layer, nmid_tidxd[idy], width=tr_w_nmid_h)
if idy == 0:
nmid_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('nd'), tidd,
min_len_mode=MinLenMode.MIDDLE))
elif idy == num_in - 1:
nmid_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('ns'), tids,
min_len_mode=MinLenMode.MIDDLE))
else:
nmid_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('ns'), tids,
min_len_mode=MinLenMode.MIDDLE))
nmid_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('nd'), tidd,
min_len_mode=MinLenMode.MIDDLE))
unit_h = unit_inst[0].master.bound_box.h
for idy in range(num_in - 1):
warr1_yc = nmid_warr[idy * 2].bound_box.ym
warr2_yc = nmid_warr[idy * 2 + 1].bound_box.ym
col_idx = 0 if warr2_yc - warr1_yc > unit_h else 1
if nmid_tidy[idy] is None:
nmid_tidy[idy] = vm_locs[col_idx]
tid = TrackID(vm_layer, nmid_tidy[idy], width=tr_w_nmid_v)
self.connect_to_tracks(nmid_warr[idy * 2:idy * 2 + 2], tid)
return in_warr, out_warr
[docs] def _make_nor_row(self, num_in: int, sig_locs: dict, tile_idx: int, unit_inst: list,
vm_locs: list, vss_warr: list, vdd_warr: list) -> Tuple[List[WireArray], List[WireArray]]:
# get sig locations
gate_name = 'nor%d' % num_in
in_tidx = []
nout_tidx = []
pmid_tidxs = []
pmid_tidxd = []
pmid_tidy = []
for idx in range(num_in):
name = '_in%d' % idx
in_tidx.append(sig_locs.get(gate_name + name, None))
name = '_nout%d' % idx
nout_tidx.append(sig_locs.get(gate_name + name, None))
name = '_pmidxs%d' % idx
pmid_tidxs.append(sig_locs.get(gate_name + name, None))
name = '_pmidxd%d' % idx
pmid_tidxd.append(sig_locs.get(gate_name + name, None))
for idy in range(num_in - 1):
name = '_pmidy%d' % idy
pmid_tidy.append(sig_locs.get(gate_name + name, None))
pout_tidx = sig_locs.get(gate_name + 'pout', None)
# get track locations
nidx, pidx = 0, -1
for idy in range(num_in):
_ctidx = idy + tile_idx # Current tile index
if in_tidx[idy] is None:
in_tidx[idy] = self.get_track_index(nidx, MOSWireType.G, 'sig', wire_idx=-1, tile_idx=_ctidx)
if nout_tidx[idy] is None:
nout_tidx[idy] = self.get_track_index(nidx, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=_ctidx)
if pmid_tidxs[idy] is None:
pmid_tidxs[idy] = self.get_track_index(pidx, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=_ctidx)
if pmid_tidxd[idy] is None:
pmid_tidxd[idy] = self.get_track_index(pidx, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=_ctidx)
if pout_tidx is None:
pout_tidx = self.get_track_index(pidx, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile_idx + num_in - 1)
# compute track locations
tr_manager = self.tr_manager
hm_layer = self.conn_layer + 1
vm_layer = hm_layer + 1
tr_w_in = tr_manager.get_width(hm_layer, 'sig')
tr_w_pmid_h = tr_manager.get_width(hm_layer, 'sig')
tr_w_pmid_v = tr_manager.get_width(vm_layer, 'sig')
tr_w_out_h = tr_manager.get_width(hm_layer, 'sig')
# connect input, VDD/VSS, output
in_warr, out_warr = [], []
for idy in range(num_in):
_ctidx = idy + tile_idx # Current tile index
# connect input
tid = TrackID(hm_layer, in_tidx[idy], width=tr_w_in)
in_warr.append(self.connect_to_tracks([unit_inst[_ctidx].get_pin('ng'), unit_inst[_ctidx].get_pin('pg')],
tid, min_len_mode=MinLenMode.MIDDLE))
# connect VDD/VSS
vdd_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('ps'),
unit_inst[_ctidx].get_pin('VDD').track_id)
if idy == 0 else unit_inst[_ctidx].get_pin('VDD'))
vss_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('ns'),
unit_inst[_ctidx].get_pin('VSS').track_id))
# connect output
tid = TrackID(hm_layer, nout_tidx[idy], width=tr_w_out_h)
out_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('nd'), tid,
min_len_mode=MinLenMode.MIDDLE))
if idy == num_in - 1:
tid = TrackID(hm_layer, pout_tidx, width=tr_w_out_h)
out_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('pd'), tid,
min_len_mode=MinLenMode.MIDDLE))
# connect mid wires
pmid_warr = []
for idy in range(num_in):
_ctidx = idy + tile_idx # Current tile_index
tids = TrackID(hm_layer, pmid_tidxs[idy], width=tr_w_pmid_h)
tidd = TrackID(hm_layer, pmid_tidxd[idy], width=tr_w_pmid_h)
if idy == 0:
pmid_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('pd'),
tidd, min_len_mode=MinLenMode.MIDDLE))
elif idy == num_in - 1:
pmid_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('ps'),
tids, min_len_mode=MinLenMode.MIDDLE))
else:
pmid_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('ps'),
tids, min_len_mode=MinLenMode.MIDDLE))
pmid_warr.append(self.connect_to_tracks(unit_inst[_ctidx].get_pin('pd'),
tidd, min_len_mode=MinLenMode.MIDDLE))
for idy in range(num_in - 1):
col_idx = 0 if idy % 2 == 0 else 1
if pmid_tidy[idy] is None:
pmid_tidy[idy] = vm_locs[col_idx]
tid = TrackID(vm_layer, pmid_tidy[idy], width=tr_w_pmid_v)
self.connect_to_tracks(pmid_warr[idy * 2:idy * 2 + 2], tid)
return in_warr, out_warr
[docs] def _make_inv_row(self, sig_locs: dict, tile_idx: int, unit_inst: list,
vm_locs: list, vss_warr: list, vdd_warr: list) -> Tuple[WireArray, WireArray]:
# get sig locations
in_tidx = sig_locs.get('inv_in', None)
pout_tidx = sig_locs.get('inv_pout', None)
nout_tidx = sig_locs.get('inv_nout', None)
# get track locations
nidx, pidx = 0, -1
if in_tidx is None:
in_tidx = self.get_track_index(nidx, MOSWireType.G, 'sig', wire_idx=-1, tile_idx=tile_idx)
if pout_tidx is None:
pout_tidx = self.get_track_index(pidx, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile_idx)
if nout_tidx is None:
nout_tidx = self.get_track_index(nidx, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile_idx)
# compute track locations
tr_manager = self.tr_manager
hm_layer = self.conn_layer + 1
tr_w_in = tr_manager.get_width(hm_layer, 'in')
tr_w_out_h = tr_manager.get_width(hm_layer, 'out')
# connect input
tid = TrackID(hm_layer, in_tidx, width=tr_w_in)
in_warr = self.connect_to_tracks([unit_inst[tile_idx].get_pin('pg'),
unit_inst[tile_idx].get_pin('ng')], tid, min_len_mode=MinLenMode.MIDDLE)
# connect VDD/VSS
vss_warr.append(self.connect_to_tracks(unit_inst[tile_idx].get_pin('ns'),
unit_inst[tile_idx].get_pin('VSS').track_id))
vdd_warr.append(self.connect_to_tracks(unit_inst[tile_idx].get_pin('ps'),
unit_inst[tile_idx].get_pin('VDD').track_id))
# connect output
tid = TrackID(hm_layer, pout_tidx, width=tr_w_out_h)
pout_warr = self.connect_to_tracks(unit_inst[tile_idx].get_pin('pd'), tid, min_len_mode=MinLenMode.MIDDLE)
tid = TrackID(hm_layer, nout_tidx, width=tr_w_out_h)
nout_warr = self.connect_to_tracks(unit_inst[tile_idx].get_pin('nd'), tid, min_len_mode=MinLenMode.MIDDLE)
out_warr = self.connect_wires([pout_warr, nout_warr])
return in_warr, out_warr
[docs]def get_nand_in_list(num_in: int) -> Sequence[int]:
q, r = divmod(num_in, 3)
if r == 0:
return [3] * q
elif r == 1:
return [3] * (q - 1) + [2, 2]
else: # r == 2
return [3] * q + [2]
[docs]def get_in_shift(num_in: int) -> Sequence[int]:
nand_in_list = get_nand_in_list(num_in)
ans = []
for nand_num in nand_in_list:
if nand_num == 2:
ans.extend([1, 1])
else:
ans.extend([1, 1, 0])
return ans