from typing import Any, Optional, Mapping, Type, Union, Tuple
from itertools import chain
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
from xbase.layout.mos.base import MOSBasePlaceInfo, MOSBase
from xbase.layout.enum import MOSWireType
from ..stdcells.gates import InvCore
from ..stdcells.memory import FlopCore
from ...schematic.des1toN import bag3_digital__des1toN
[docs]class Des1toN(MOSBase):
"""
2 rows of FF
This cell requires both clock and divided clock as inputs.
"""
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__des1toN
@classmethod
[docs] def get_params_info(cls) -> Mapping[str, str]:
return dict(
pinfo='The MOSBasePlaceInfo object.',
ridx_p='pch row index',
ridx_n='nch row index',
seg_dict='Dictionary of segments',
ratio='Number of serialized inputs/deserialized outputs',
horz_slow='True to have serialized inputs/deserialized outputs on horizontal layer',
export_nets='True to export intermediate nets',
tap_sep_flop='Horizontal separation between column taps in number of flops. Default is ratio // 2.',
)
@classmethod
[docs] def get_default_param_values(cls) -> Mapping[str, Any]:
return dict(
ridx_p=-1,
ridx_n=0,
horz_slow=True,
export_nets=False,
tap_sep_flop=-1,
)
[docs] def draw_layout(self) -> None:
pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo'])
self.draw_base(pinfo)
ridx_p: int = self.params['ridx_p']
ridx_n: int = self.params['ridx_n']
seg_dict: Mapping[str, int] = self.params['seg_dict']
ratio: int = self.params['ratio']
horz_slow: bool = self.params['horz_slow']
export_nets: bool = self.params['export_nets']
tap_sep_flop: int = self.params['tap_sep_flop']
if tap_sep_flop <= 0:
tap_sep_flop = ratio >> 1
# make masters
ff_params = dict(pinfo=pinfo, seg=seg_dict['flop_fast'])
ff_master = self.new_template(FlopCore, params=ff_params)
fs_params = dict(pinfo=pinfo, seg=seg_dict['flop_slow'])
fs_master = self.new_template(FlopCore, params=fs_params)
f_ncols = max(ff_master.num_cols, fs_master.num_cols)
# sig_locs for inverters in inverter chain
pd1_tidx = self.get_track_index(ridx_p, MOSWireType.DS, 'sig', 1)
pd0_tidx = self.get_track_index(ridx_p, MOSWireType.DS, 'sig', 0)
pg_tidx = self.get_track_index(ridx_p, MOSWireType.G, 'sig', -2)
ng_tidx = self.get_track_index(ridx_n, MOSWireType.G, 'sig', 1)
nd1_tidx = self.get_track_index(ridx_n, MOSWireType.DS, 'sig', -1)
nd0_tidx = self.get_track_index(ridx_n, MOSWireType.DS, 'sig', -2)
seg_fast: Union[int, Tuple[int, int]] = seg_dict['inv_fast']
if isinstance(seg_fast, int):
assert seg_fast & 1 == 0, f'seg_dict["inv_fast"]={seg_fast} has to be even.'
seg_fast0, seg_fast1 = seg_fast, seg_fast
else:
seg_fast0, seg_fast1 = seg_fast
assert seg_fast0 & 1 == 0, f'seg_dict["inv_fast"][0]={seg_fast0} has to be even.'
assert seg_fast1 & 1 == 0, f'seg_dict["inv_fast"][1]={seg_fast1} has to be even.'
invf_0_params = dict(pinfo=pinfo, seg=seg_fast0, vertical_out=False,
sig_locs={'in': ng_tidx, 'pout': pd0_tidx, 'nout': nd1_tidx})
invf_0_master = self.new_template(InvCore, params=invf_0_params)
invf_1_params = dict(pinfo=pinfo, seg=seg_fast1, vertical_out=False,
sig_locs={'in': pg_tidx, 'pout': pd1_tidx, 'nout': nd0_tidx})
invf_1_master = self.new_template(InvCore, params=invf_1_params)
seg_slow: Union[int, Tuple[int, int]] = seg_dict['inv_slow']
if isinstance(seg_slow, int):
assert seg_slow & 1 == 0, f'seg_dict["inv_slow"]={seg_slow} has to be even.'
seg_slow0, seg_slow1 = seg_slow, seg_slow
else:
seg_slow0, seg_slow1 = seg_slow
assert seg_slow0 & 1 == 0, f'seg_dict["inv_slow"][0]={seg_slow0} has to be even.'
assert seg_slow1 & 1 == 0, f'seg_dict["inv_slow"][1]={seg_slow1} has to be even.'
invs_0_params = dict(pinfo=pinfo, seg=seg_slow0, vertical_out=False,
sig_locs={'in': ng_tidx, 'pout': pd0_tidx, 'nout': nd1_tidx})
invs_0_master = self.new_template(InvCore, params=invs_0_params)
invs_1_params = dict(pinfo=pinfo, seg=seg_slow1, vertical_out=False,
sig_locs={'in': pg_tidx, 'pout': pd1_tidx, 'nout': nd0_tidx})
invs_1_master = self.new_template(InvCore, params=invs_1_params)
hm_layer = self.conn_layer + 1
vm_layer = hm_layer + 1
xm_layer = vm_layer + 1
# --- Placement --- #
blk_sp = self.min_sep_col
sub_sep = self.sub_sep_col
tap_ncols = self.get_tap_ncol()
# left tap
vdd_list, vss_list = [], []
self.add_tap(0, vdd_list, vss_list, tile_idx=0)
self.add_tap(0, vdd_list, vss_list, tile_idx=1)
sup_coords = [self.grid.track_to_coord(self.conn_layer, tap_ncols >> 1)]
cur_col = tap_ncols + sub_sep
# clock inverters at beginning for deserializer
invf_0 = self.add_tile(invf_0_master, 0, cur_col)
invs_0 = self.add_tile(invs_0_master, 1, cur_col)
cur_col += max(invf_0_master.num_cols, invs_0_master.num_cols)
_, clk_vm_locs = self.tr_manager.place_wires(vm_layer, ['clk', 'clk', 'clk'],
center_coord=cur_col * self.sd_pitch)
invf_1 = self.add_tile(invf_1_master, 0, cur_col)
invs_1 = self.add_tile(invs_1_master, 1, cur_col)
inv_list = [invf_0, invf_1, invs_0, invs_1]
cur_col += max(invf_1_master.num_cols, invs_1_master.num_cols)
clk_in_idx, clk_out_idx = 0, -1
# flops
ff_list, fs_list = [], []
clk_list, clkb_list = [], []
clk_div_list, clk_divb_list = [], []
dslow_list = []
for idx in range(ratio):
if idx > 0 and idx % tap_sep_flop == 0:
# mid tap
cur_col += sub_sep
self.add_tap(cur_col, vdd_list, vss_list, tile_idx=0)
self.add_tap(cur_col, vdd_list, vss_list, tile_idx=1)
sup_coords.append(self.grid.track_to_coord(self.conn_layer, cur_col + (tap_ncols >> 1)))
cur_col += tap_ncols + sub_sep
else:
cur_col += blk_sp
ff = self.add_tile(ff_master, 0, cur_col)
ff_list.append(ff)
cur_col += f_ncols
fs = self.add_tile(fs_master, 1, cur_col, flip_lr=True)
fs_list.append(fs)
# local routing
clk_vm = ff.get_pin('clk')
clk_list.append(clk_vm)
clkb_list.append(ff.get_pin('clkb'))
clk_div_vm = fs.get_pin('clk')
clk_div_list.append(clk_div_vm)
clk_divb_list.append(fs.get_pin('clkb'))
dslow = fs.get_pin('out')
# check if dslow can be routed down to row 0 for connecting to xm_layer
if horz_slow:
avail_vm_idx = self.tr_manager.get_next_track(vm_layer, clk_vm.track_id.base_index, 'sig', 'sig', up=-1)
if dslow.track_id.base_index > avail_vm_idx:
raise ValueError(f'dslow on vm_layer={vm_layer} cannot be routed down to row 0 for connecting to '
f'xm_layer={xm_layer} because of collision / spacing error on vm_layer={vm_layer}')
dslow_list.append(dslow)
else:
prefix = 'dout'
self.add_pin(f'{prefix}<{idx}>', dslow)
d_int = self.connect_to_track_wires(fs.get_pin('pin'), ff.get_pin('out'))
self.add_pin(f'd<{idx}>', d_int, hide=not export_nets)
if idx == 0:
self.reexport(ff.get_port('pin'), net_name='din', hide=False)
else:
self.connect_wires([ff.get_pin('pin'), ff_list[-2].get_pin('pout')])
# right tap
cur_col += sub_sep
self.add_tap(cur_col, vdd_list, vss_list, tile_idx=0)
self.add_tap(cur_col, vdd_list, vss_list, tile_idx=1)
sup_coords.append(self.grid.track_to_coord(self.conn_layer, cur_col + (tap_ncols >> 1)))
self.set_mos_size()
xh = self.bound_box.xh
yh = self.bound_box.yh
# --- Routing --- #
# supplies
vss_hm_list, vdd_hm_list = [], []
for inst in chain(inv_list, ff_list, fs_list):
vss_hm_list.append(inst.get_pin('VSS'))
vdd_hm_list.append(inst.get_pin('VDD'))
vss_hm = self.connect_to_track_wires(vss_list, self.connect_wires(vss_hm_list, lower=0, upper=xh))[0]
vss0_xm_idx = self.grid.coord_to_track(xm_layer, vss_hm[0].bound_box.ym, RoundMode.NEAREST)
vss1_xm_idx = self.grid.coord_to_track(xm_layer, vss_hm[1].bound_box.ym, RoundMode.NEAREST)
vdd_hm = self.connect_to_track_wires(vdd_list, self.connect_wires(vdd_hm_list, lower=0, upper=xh))[0]
vdd_xm_idx = self.grid.coord_to_track(xm_layer, vdd_hm.bound_box.ym, RoundMode.NEAREST)
w_vm_sup = self.tr_manager.get_width(vm_layer, 'sup')
w_xm_sup = self.tr_manager.get_width(xm_layer, 'sup')
vss_vm_list, vdd_vm_list = [], []
for _coord in sup_coords:
_, _locs = self.tr_manager.place_wires(vm_layer, ['sup', 'sup'], center_coord=_coord)
vss_tid = TrackID(vm_layer, _locs[0], w_vm_sup)
vss_vm_list.append(self.connect_to_tracks(vss_hm, vss_tid, track_lower=0, track_upper=yh))
vdd_tid = TrackID(vm_layer, _locs[-1], w_vm_sup)
vdd_vm_list.append(self.connect_to_tracks(vdd_hm, vdd_tid, track_lower=0, track_upper=yh))
vss0_xm = self.connect_to_tracks(vss_vm_list, TrackID(xm_layer, vss0_xm_idx, w_xm_sup))
vss1_xm = self.connect_to_tracks(vss_vm_list, TrackID(xm_layer, vss1_xm_idx, w_xm_sup))
vdd_xm = self.connect_to_tracks(vdd_vm_list, TrackID(xm_layer, vdd_xm_idx, w_xm_sup))
self.add_pin('VSS', [vss_hm, self.connect_wires([vss0_xm, vss1_xm])[0]])
self.add_pin('VDD', [vdd_hm, vdd_xm])
self.add_pin('VSS_vm', vss_vm_list, hide=True)
self.add_pin('VDD_vm', vdd_vm_list, hide=True)
# find xm_layer tracks using supply tracks as reference
xm_order = ['sup', 'clk', 'clk', 'sup']
if horz_slow:
num_out = - (- ratio // 2)
xm_order[2:2] = ['sig'] * num_out
try:
xm_locs0 = self.tr_manager.spread_wires(xm_layer, xm_order, lower=vss0_xm_idx, upper=vdd_xm_idx,
sp_type=('clk', 'clk'))
xm_locs1 = self.tr_manager.spread_wires(xm_layer, xm_order, lower=vdd_xm_idx, upper=vss1_xm_idx,
sp_type=('clk', 'clk'))
except ValueError:
raise ValueError(f'Not enough space to route slow speed signals on horizontal layer={xm_layer}. '
f'Use horz_slow=False.')
# get slow serializer inputs / deserializer outputs on xm_layer
w_xm_sig = self.tr_manager.get_width(xm_layer, 'sig')
if horz_slow:
# row 1
num_slow1 = - (- ratio // 2)
for idx in range(num_slow1):
xm_tid = TrackID(xm_layer, xm_locs1[-3 - idx], w_xm_sig)
dout_xm = self.connect_to_tracks(dslow_list[idx], xm_tid, track_upper=xh)
self.add_pin(f'dout<{idx}>', dout_xm)
# row 0
num_slow0 = ratio - num_slow1
for idx in range(num_slow0):
xm_tid = TrackID(xm_layer, xm_locs0[-3 - idx], w_xm_sig)
widx = idx + num_slow1
dout_xm = self.connect_to_tracks(dslow_list[widx], xm_tid, track_upper=xh)
self.add_pin(f'dout<{widx}>', dout_xm)
# clkb_buf
w_vm_clk = self.tr_manager.get_width(vm_layer, 'clk')
clkb_vm = self.connect_to_tracks([invf_0.get_pin('pout'), invf_0.get_pin('nout'), invf_1.get_pin('nin')],
TrackID(vm_layer, clk_vm_locs[1], w_vm_clk))
clkb_list.append(clkb_vm)
w_xm_clk = self.tr_manager.get_width(xm_layer, 'clk')
clkb_xm = self.connect_to_tracks(clkb_list, TrackID(xm_layer, xm_locs0[1], w_xm_clk))
self.add_pin('clkb_buf', [clkb_vm, clkb_xm], hide=not export_nets)
# clk_buf
clk_vm = self.connect_to_tracks([invf_1.get_pin('pout'), invf_1.get_pin('nout')],
TrackID(vm_layer, clk_vm_locs[clk_out_idx], w_vm_clk))
clk_list.append(clk_vm)
clk_xm = self.connect_to_tracks(clk_list, TrackID(xm_layer, xm_locs0[-2], w_xm_clk))
self.add_pin('clk_buf', [clk_vm, clk_xm], hide=not export_nets)
# clk
clk_in = self.connect_to_tracks(invf_0.get_pin('nin'), TrackID(vm_layer, clk_vm_locs[clk_in_idx], w_vm_clk),
min_len_mode=MinLenMode.MIDDLE)
self.add_pin('clk', clk_in)
# clk_divb_buf
clk_divb_vm = self.connect_to_tracks([invs_0.get_pin('pout'), invs_0.get_pin('nout'), invs_1.get_pin('nin')],
TrackID(vm_layer, clk_vm_locs[1], w_vm_clk))
clk_divb_list.append(clk_divb_vm)
clk_divb_xm = self.connect_to_tracks(clk_divb_list, TrackID(xm_layer, xm_locs1[-2], w_xm_clk))
self.add_pin('clk_divb_buf', [clk_divb_vm, clk_divb_xm], hide=not export_nets)
# clk_div_buf
clk_div_vm = self.connect_to_tracks([invs_1.get_pin('pout'), invs_1.get_pin('nout')],
TrackID(vm_layer, clk_vm_locs[clk_out_idx], w_vm_clk))
clk_div_list.append(clk_div_vm)
clk_div_xm = self.connect_to_tracks(clk_div_list, TrackID(xm_layer, xm_locs1[1], w_xm_clk))
self.add_pin('clk_div_buf', [clk_div_vm, clk_div_xm], hide=not export_nets)
# clk_div
clk_div = self.connect_to_tracks(invs_0.get_pin('nin'), TrackID(vm_layer, clk_vm_locs[clk_in_idx], w_vm_clk),
min_len_mode=MinLenMode.MIDDLE)
self.add_pin('clk_div', clk_div)
# get schematic parameters
self.sch_params = dict(
flop_fast=ff_master.sch_params,
flop_slow=fs_master.sch_params,
inv_fast=dict(inv_params=[invf_0_master.sch_params, invf_1_master.sch_params], dual_output=True),
inv_slow=dict(inv_params=[invs_0_master.sch_params, invs_1_master.sch_params], dual_output=True),
ratio=ratio,
export_nets=export_nets,
)