Source code for bag3_digital.layout.serdes.serNto1

from typing import Any, Optional, Mapping, Type
from itertools import chain

from pybag.enum import MinLenMode, RoundMode, PinMode

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, InvTristateCore
from ..stdcells.memory import FlopCore
from ...schematic.serNto1 import bag3_digital__serNto1


[docs]class SerNto1(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__serNto1
@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', 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, 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'] 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['ff']) ff_master = self.new_template(FlopCore, params=ff_params) ff_ncols = ff_master.num_cols ff_rst_params = dict(pinfo=pinfo, seg=seg_dict['ff'], resetable=True, rst_type='RESET') ff_rst_master = self.new_template(FlopCore, params=ff_rst_params) ff_rst_ncols = ff_rst_master.num_cols ff_set_params = dict(pinfo=pinfo, seg=seg_dict['ff'], resetable=True, rst_type='SET') ff_set_master = self.new_template(FlopCore, params=ff_set_params) # sig_locs for inverters in clock 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) pg1_tidx = self.get_track_index(ridx_p, MOSWireType.G, 'sig', -2) pg0_tidx = self.get_track_index(ridx_p, MOSWireType.G, 'sig', -3) 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_clk: int = seg_dict['inv_clk'] assert seg_clk & 1 == 0, f'seg_dict["inv_clk"]={seg_clk} has to be even.' inv_0_params = dict(pinfo=pinfo, seg=seg_clk, vertical_out=False, sig_locs={'in': ng_tidx, 'pout': pd0_tidx, 'nout': nd1_tidx}) inv_0_master = self.new_template(InvCore, params=inv_0_params) inv_1_params = dict(pinfo=pinfo, seg=seg_clk, vertical_out=False, sig_locs={'in': pg1_tidx, 'pout': pd1_tidx, 'nout': nd0_tidx}) inv_1_master = self.new_template(InvCore, params=inv_1_params) inv_clk_ncols = inv_0_master.num_cols inv_en_params = dict(pinfo=pinfo, seg=seg_dict['inv_en'], vertical_out=False, sig_locs={'in': pg0_tidx, 'pout': pd0_tidx, 'nout': nd0_tidx}) inv_en_master = self.new_template(InvCore, params=inv_en_params) inv_en_ncols = inv_en_master.num_cols inv_r_params = dict(pinfo=pinfo, seg=seg_dict['inv_rst'], vertical_out=False, sig_locs={'in': ng_tidx, 'pout': pd1_tidx, 'nout': nd1_tidx}) inv_r_master = self.new_template(InvCore, params=inv_r_params) inv_r_ncols = inv_r_master.num_cols tinv_params = dict(pinfo=pinfo, seg=seg_dict['tinv'], vertical_out=False, sig_locs={'nin': pg0_tidx, 'pout': pd1_tidx, 'nout': nd0_tidx}) tinv_master = self.new_template(InvTristateCore, params=tinv_params) tinv_ncols = tinv_master.num_cols 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 - blk_sp # flops ff_list, ff_rst_list = [], [] clk_list, clkb_list = [], [] clk_div_list, clk_divb_list = [], [] inv_en_list, tinv_list = [], [] inv_r = None _p_prev = None _p_ini = None rst_vm = None w_sig_vm = self.tr_manager.get_width(vm_layer, 'sig') out_list = [] rst_list = [] in_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 # tile 0 cur_col0 = cur_col ff_rst = self.add_tile(ff_rst_master if idx > 0 else ff_set_master, 0, cur_col0) ff_rst_list.append(ff_rst) cur_col0 += ff_rst_ncols clk_list.append(ff_rst.get_pin('clk')) clkb_list.append(ff_rst.get_pin('clkb')) cur_col0 += blk_sp inv_en = self.add_tile(inv_en_master, 0, cur_col0) inv_en_list.append(inv_en) cur_col0 += inv_en_ncols if idx == 0: cur_col0 += blk_sp + inv_r_ncols inv_r = self.add_tile(inv_r_master, 0, cur_col0, flip_lr=True) _, rst_vm_locs = self.tr_manager.place_wires(vm_layer, ['sig', 'sig', 'sig'], center_coord=(cur_col0 - inv_r_ncols // 2) * self.sd_pitch) # rstb self.connect_to_tracks([ff_rst.get_pin('psetb'), inv_r.get_pin('pout'), inv_r.get_pin('nout')], TrackID(vm_layer, rst_vm_locs[0], w_sig_vm)) rst_vm = self.connect_to_tracks(inv_r.get_pin('nin'), TrackID(vm_layer, rst_vm_locs[-1], w_sig_vm), min_len_mode=MinLenMode.UPPER) else: rst_list.append(ff_rst.get_pin('prst')) # tile 1 cur_col1 = cur_col ff = self.add_tile(ff_master, 1, cur_col1) ff_list.append(ff) cur_col1 += ff_ncols clk_div_vm = ff.get_pin('clk') clk_div_list.append(clk_div_vm) clk_divb_list.append(ff.get_pin('clkb')) cur_col1 += blk_sp tinv = self.add_tile(tinv_master, 1, cur_col1) tinv_list.append(tinv) cur_col1 += tinv_ncols cur_col = max(cur_col0, cur_col1) # local routing # ff output to tinv input _d = self.connect_to_track_wires(tinv.get_pin('nin'), ff.get_pin('out')) self.add_pin(f'd<{idx}>', _d, hide=not export_nets) # ff input in_vm_tid = self.tr_manager.get_next_track_obj(clk_div_vm, 'sig', 'sig', count_rel_tracks=-1) in_vm = self.connect_to_tracks(ff.get_pin('pin'), in_vm_tid, min_len_mode=MinLenMode.MIDDLE) in_list.append(in_vm) # ff_rst output to inv_en input to tinv en _p0 = ff_rst.get_pin('out') _, en_vm_locs = self.tr_manager.place_wires(vm_layer, ['sig', 'sig', 'sig', 'sig', 'sig'], _p0.track_id.base_index) _avail_vm_tidx = self.tr_manager.get_next_track(vm_layer, _d.track_id.base_index, 'sig', 'sig', 1) if _p0.track_id.base_index >= _avail_vm_tidx: en_tidx = _p0.track_id.base_index else: en_tidx = en_vm_locs[-3] _p = self.connect_to_tracks([ff_rst.get_pin('pout'), ff_rst.get_pin('nout'), inv_en.get_pin('nin'), tinv.get_pin('en')], TrackID(vm_layer, en_tidx, w_sig_vm)) self.add_pin(f'p<{idx}>', _p, hide=not export_nets) # inv_en output to tinv enb _pb = self.connect_to_tracks([inv_en.get_pin('pout'), inv_en.get_pin('nout'), tinv.get_pin('enb')], TrackID(vm_layer, en_vm_locs[-2], w_sig_vm)) # output _out = self.connect_to_tracks([tinv.get_pin('pout'), tinv.get_pin('nout')], TrackID(vm_layer, en_vm_locs[-1], w_sig_vm)) out_list.append(_out) # ff_rst input to previous ff_rst output if idx == 0: _p_ini = self.connect_to_tracks(ff_rst.get_pin('pin'), in_vm_tid, min_len_mode=MinLenMode.MIDDLE) else: self.connect_to_track_wires(ff_rst.get_pin('nin'), _p_prev) # setup for next iteration _p_prev = _p # clock inverters chains cur_col += blk_sp + inv_clk_ncols invf_1 = self.add_tile(inv_1_master, 0, cur_col, flip_lr=True) invs_1 = self.add_tile(inv_1_master, 1, cur_col, flip_lr=True) _, clk_vm_locs = self.tr_manager.place_wires(vm_layer, ['clk', 'clk', 'clk'], center_coord=cur_col * self.sd_pitch) cur_col += inv_clk_ncols invf_0 = self.add_tile(inv_0_master, 0, cur_col, flip_lr=True) invs_0 = self.add_tile(inv_0_master, 1, cur_col, flip_lr=True) inv_clk_list = [invf_0, invf_1, invs_0, invs_1] # 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(ff_rst_list, inv_en_list, ff_list, tinv_list, [inv_r], inv_clk_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_locs0 = self.tr_manager.spread_wires(xm_layer, ['sup', 'clk', 'clk', 'clk', 'sup'], lower=vss0_xm_idx, upper=vdd_xm_idx, sp_type=('clk', 'clk')) xm_locs1 = self.tr_manager.spread_wires(xm_layer, ['sup', 'clk', 'sig', 'clk', 'clk', 'sup'], lower=vdd_xm_idx, upper=vss1_xm_idx, sp_type=('clk', 'clk')) # 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_xm, hide=not export_nets, mode=PinMode.LOWER) # clk_buf clk_vm = self.connect_to_tracks([invf_1.get_pin('pout'), invf_1.get_pin('nout')], TrackID(vm_layer, clk_vm_locs[0], 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_xm, hide=not export_nets, mode=PinMode.LOWER) # clk clk_in_vm = self.connect_to_tracks(invf_0.get_pin('nin'), TrackID(vm_layer, clk_vm_locs[-1], w_vm_clk), min_len_mode=MinLenMode.MIDDLE) clk_in_xm = self.connect_to_tracks(clk_in_vm, TrackID(xm_layer, xm_locs0[2], w_xm_clk), min_len_mode=MinLenMode.UPPER) self.add_pin('clk', clk_in_xm) # 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_xm, hide=not export_nets, mode=PinMode.LOWER) # 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[0], 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_xm, hide=not export_nets, mode=PinMode.LOWER) # clk_div clk_div_vm = self.connect_to_tracks(invs_0.get_pin('nin'), TrackID(vm_layer, clk_vm_locs[-1], w_vm_clk), min_len_mode=MinLenMode.MIDDLE) w_sig_xm = self.tr_manager.get_width(xm_layer, 'sig') clk_div_xm = self.connect_to_tracks(clk_div_vm, TrackID(xm_layer, xm_locs1[2], w_sig_xm), min_len_mode=MinLenMode.UPPER) self.add_pin('clk_div', [clk_div_vm, clk_div_xm]) # connect last p<> to complete shift register self.connect_to_tracks([_p_ini, _p_prev], TrackID(xm_layer, xm_locs0[2], w_xm_clk)) # reset rst_vm = self.connect_to_track_wires(rst_list, rst_vm) self.add_pin('rst', rst_vm) # output doutb = self.connect_to_tracks(out_list, TrackID(xm_layer, xm_locs1[-3], w_xm_clk)) self.add_pin('doutb', doutb, mode=PinMode.UPPER) # inputs on xm_layer in_xm_tid = TrackID(xm_layer, xm_locs1[2], w_sig_xm) for idx, in_vm in enumerate(in_list): in_xm = self.connect_to_tracks(in_vm, in_xm_tid, min_len_mode=MinLenMode.MIDDLE) self.add_pin(f'din<{idx}>', in_xm) # get schematic parameters self.sch_params = dict( ff_rst=ff_rst_master.sch_params, ff_set=ff_set_master.sch_params, inv_r=inv_r_master.sch_params, inv_en=inv_en_master.sch_params, ff=ff_master.sch_params, tinv=tinv_master.sch_params, inv_clk=dict(inv_params=[inv_0_master.sch_params, inv_1_master.sch_params], dual_output=True), ratio=ratio, export_nets=export_nets,
)