def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) # create masters tri_params = self.params.copy(append=dict(pinfo=pinfo, ridx_p=-1, ridx_n=0, vertical_out=True, vertical_sup=True)) tri_master = self.new_template(InvTristateCore, params=tri_params) # floorplanning hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 grid = self.grid arr_info = self.arr_info tr_manager = self.tr_manager vm_w = tr_manager.get_width(vm_layer, 'sig') vm_w_sup = tr_manager.get_width(vm_layer, 'sup') vm_l = grid.get_next_length(vm_layer, vm_w, 0, even=True) vm_l_sup = grid.get_next_length(vm_layer, vm_w_sup, 0, even=True) tri_ncol = tri_master.num_cols loc_list = tr_manager.place_wires(vm_layer, ['sup', 'sig', 'sig_hs', 'sig', 'sup'])[1] ntr = loc_list[4] - loc_list[0] + tr_manager.get_sep(vm_layer, ('sup', 'sup')) ncol_tot = max(tri_ncol, arr_info.get_column_span(vm_layer, ntr)) blk_ncol = arr_info.get_block_ncol(vm_layer, half_blk=True) ncol_tot = -(-ncol_tot // blk_ncol) * blk_ncol col_diff = ncol_tot - tri_ncol if col_diff & 1: if blk_ncol & 1 == 0: raise ValueError('Cannot center tristate inverter with number of tracks.') ncol_tot += blk_ncol col_diff += blk_ncol self._col_margin = col_diff // 2 core = self.add_tile(tri_master, 0, self._col_margin) self.set_mos_size(num_cols=ncol_tot) # routing out = core.get_pin('out') tr_off = out.track_id.base_index - loc_list[2] sig_l = out.middle - vm_l // 2 sup_l = out.middle - vm_l_sup // 2 sup_u = sup_l + vm_l_sup enl = self.add_wires(vm_layer, loc_list[1] + tr_off, sig_l, sig_l + vm_l, width=vm_w) enr = self.add_wires(vm_layer, loc_list[3] + tr_off, enl.lower, enl.upper, width=vm_w) vss = self.add_wires(vm_layer, loc_list[0] + tr_off, sup_l, sup_u, width=vm_w_sup) vdd = self.add_wires(vm_layer, loc_list[4] + tr_off, sup_l, sup_u, width=vm_w_sup) self.add_pin('out', out) self.add_pin('enl', enl) self.add_pin('enr', enr) self.add_pin('VSS_vm', vss) self.add_pin('VDD_vm', vdd) for name in ['nin', 'en', 'enb', 'VDD', 'VSS', 'pout', 'nout']: self.reexport(core.get_port(name)) self.sch_params = tri_master.sch_params
def draw_layout(self): lch: int = self.params['lch'] fg: int = self.params['fg'] row_list: List[Tuple[str, int, str]] = self.params['row_list'] min_ntr: int = self.params['min_ntr'] w_list: Optional[List[int]] = self.params['w_list'] if not row_list: raise ValueError('Cannot draw empty rows.') if w_list is None: w_list = [info[1] for info in row_list] elif len(w_list) != len(row_list): raise ValueError('width list length mismatch') row_specs = [] hm_layer = MOSBasePlaceInfo.get_conn_layer(self.grid.tech_info, lch) + 1 empty_wires = WireData.make_wire_data([], Alignment.CENTER_COMPACT, '') for info in row_list: mtype, w, th = info[:3] if len(info) > 3: flip = info[3] else: flip = False row_specs.append( MOSRowSpecs(MOSType[mtype], w, th, empty_wires, empty_wires, flip=flip)) ainfo = MOSArrayPlaceInfo(self.grid, lch, {}, {}) min_height = self.grid.get_track_pitch(hm_layer) * min_ntr pinfo = make_pinfo_compact(ainfo, row_specs, True, True, min_height=min_height) self.draw_base(pinfo) self.set_mos_size(fg) for ridx in range(len(row_list)): self.add_mos(ridx, 0, fg, w=w_list[ridx])
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) ncol: int = self.params['ncol'] ntile: int = self.params['ntile'] self.set_mos_size(ncol, ntile) for tile_idx in range(ntile): pinfo = self.get_tile_pinfo(tile_idx) for row_idx in range(pinfo.num_rows): rinfo = pinfo.get_row_place_info(row_idx).row_info if rinfo.row_type.is_substrate: self.add_substrate_contact(row_idx, 0, tile_idx=tile_idx, seg=ncol) else: self.add_mos(row_idx, 0, ncol, tile_idx=tile_idx)
def draw_layout(self): lch: int = self.params['lch'] fg: int = self.params['fg'] fg_sp: int = self.params['fg_sp'] row_list: List[Tuple[str, int, str]] = self.params['row_list'] min_ntr: int = self.params['min_ntr'] mos_sub: bool = self.params['mos_sub'] if not row_list: raise ValueError('Cannot draw empty rows.') row_specs = [] empty_wires = WireData.make_wire_data([], Alignment.CENTER_COMPACT, '') hm_layer = MOSBasePlaceInfo.get_conn_layer(self.grid.tech_info, lch) + 1 for mtype, w, th in row_list: row_specs.append( MOSRowSpecs(MOSType[mtype], w, th, empty_wires, empty_wires)) ainfo = MOSArrayPlaceInfo(self.grid, lch, {}, {}) min_height = self.grid.get_track_pitch(hm_layer) * min_ntr pinfo = make_pinfo_compact(ainfo, row_specs, True, True, min_height=min_height) self.draw_base(pinfo) self.set_mos_size(2 * fg + fg_sp) for ridx in range(len(row_list)): self.add_mos(ridx, 0, fg) if mos_sub: self.add_substrate_contact(ridx, fg + fg_sp, seg=fg) else: self.add_mos(ridx, fg + fg_sp, fg)
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) seg_p: int = self.params['seg_p'] seg_n: int = self.params['seg_n'] w_p: int = self.params['w_p'] w_n: int = self.params['w_n'] stack: int = self.params['stack'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] sig_locs: Mapping[str, Union[float, HalfInt]] = self.params['sig_locs'] hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 #if (seg_p - seg_n) % 2 != 0: # raise ValueError('seg_p - seg_n should be even') if w_p == 0: w_p = self.place_info.get_row_place_info(ridx_p).row_info.width if w_n == 0: w_n = self.place_info.get_row_place_info(ridx_n).row_info.width pin_tidx = sig_locs.get( 'pin', self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1)) nin_tidx = sig_locs.get( 'nin', self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)) nout_tid = self.get_track_id(ridx_n, MOSWireType.DS_GATE, wire_name='padout') pout_tid = self.get_track_id(ridx_p, MOSWireType.DS_GATE, wire_name='padout') fg_p = seg_p * stack fg_n = seg_n * stack num_col = max(fg_p, fg_n) p_col = (num_col - fg_p) // 2 n_col = (num_col - fg_n) // 2 pmos = self.add_mos(ridx_p, p_col, seg_p, w=w_p, stack=stack) nmos = self.add_mos(ridx_n, n_col, seg_n, w=w_n, stack=stack) self.set_mos_size(num_cols=num_col) # connect supplies vdd_tid = self.get_track_id(ridx_p, MOSWireType.DS_GATE, wire_name='sup') vss_tid = self.get_track_id(ridx_n, MOSWireType.DS_GATE, wire_name='sup') vdd = self.connect_to_tracks(pmos.s, vdd_tid) vss = self.connect_to_tracks(nmos.s, vss_tid) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) # connect inputs hm_w = self.tr_manager.get_width(hm_layer, 'sig') pden = self.connect_to_tracks(nmos.g, TrackID(hm_layer, nin_tidx, width=hm_w), min_len_mode=MinLenMode.MIDDLE) puenb = self.connect_to_tracks(pmos.g, TrackID(hm_layer, pin_tidx, width=hm_w), min_len_mode=MinLenMode.MIDDLE) self.add_pin('pden', pden) self.add_pin('puenb', puenb) # connect output vm_w_pad = self.tr_manager.get_width(vm_layer, 'padout') nout = self.connect_to_tracks(nmos.d, nout_tid, min_len_mode=MinLenMode.MIDDLE) pout = self.connect_to_tracks(pmos.d, pout_tid, min_len_mode=MinLenMode.MIDDLE) vm_tidx = sig_locs.get( 'out', self.grid.coord_to_track(vm_layer, nout.middle, mode=RoundMode.NEAREST)) out = self.connect_to_tracks([nout, pout], TrackID(vm_layer, vm_tidx, width=vm_w_pad)) self.add_pin('out', out) self.add_pin('nout', nout, hide=True) self.add_pin('pout', pout, hide=True) # compute schematic parameters. th_p = self.place_info.get_row_place_info(ridx_p).row_info.threshold th_n = self.place_info.get_row_place_info(ridx_n).row_info.threshold self.sch_params = dict( seg_p=seg_p, seg_n=seg_n, lch=self.place_info.lch, w_p=w_p, w_n=w_n, th_p=th_p, th_n=th_n, stack_p=stack, stack_n=stack, )
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) arr_info = self.arr_info tr_manager = self.tr_manager pupd_params: Param = self.params['pupd_params'] unit_params: Param = self.params['unit_params'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 ym_layer = xm_layer + 1 # create masters unit_params = unit_params.copy( append=dict(pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n)) unit_master = self.new_template(OutputDriverCore, params=unit_params) vm_tidx = unit_master.get_port('out').get_pins()[0].track_id.base_index pupd_params = pupd_params.copy(append=dict(pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n, sig_locs=dict(out=vm_tidx))) pupd_master = self.new_template(PullUpDown, params=pupd_params) # placement # initialize supply data structures lay_range = range(conn_layer, xm_layer + 1) vdd_table: Dict[int, List[WireArray]] = {lay: [] for lay in lay_range} vss_table: Dict[int, List[WireArray]] = {lay: [] for lay in lay_range} sup_info = self.get_supply_column_info(xm_layer) sub_sep = self.sub_sep_col sub_sep2 = sub_sep // 2 # instantiate cells and collect pins pin_dict = {'din': [], 'out': [], 'out_hm': [], 'en': [], 'enb': []} cur_col = 0 din_type = 'sig_hs' tap_list = [] sup_unit_sep = sub_sep2 # first column tap_list.append((cur_col, False)) cur_col = self._draw_supply_column(cur_col, sup_info, vdd_table, vss_table, ridx_p, ridx_n, False) # make sure we have room for din wire sup_tidx = max(vdd_table[vm_layer][-1].track_id.base_index, vss_table[vm_layer][-1].track_id.base_index) din_tidx = tr_manager.get_next_track(vm_layer, sup_tidx, 'sup', din_type, up=True) en_tidx = tr_manager.get_next_track(vm_layer, din_tidx, din_type, 'sig', up=True) unit_col = arr_info.coord_to_col(self.grid.track_to_coord( vm_layer, en_tidx), round_mode=RoundMode.GREATER_EQ) sup_unit_sep = max(sup_unit_sep, unit_col - cur_col) # make sure unit element is in pitch on ym_layer ncol_core = unit_master.num_cols + sup_info.ncol ncol_unit = 2 * sup_unit_sep + ncol_core if self.top_layer >= ym_layer: ncol_unit = arr_info.round_up_to_block_size(ym_layer, ncol_unit, even_diff=True, half_blk=False) sup_unit_sep = (ncol_unit - ncol_core) // 2 cur_col += sup_unit_sep cur_col = self._draw_unit_cells(cur_col, unit_master, pin_dict, range(4)) cur_col += sup_unit_sep # second column tap_list.append((cur_col, False)) cur_col = self._draw_supply_column(cur_col, sup_info, vdd_table, vss_table, ridx_p, ridx_n, False) cur_col += sup_unit_sep new_col = self._draw_unit_cells(cur_col, unit_master, pin_dict, range(1, 3)) # pull up/pull down pupd_inst = self.add_tile(pupd_master, 0, cur_col) self._record_pins(pupd_inst, pin_dict, False) cur_col = max(new_col + sup_unit_sep, cur_col + pupd_master.num_cols + sub_sep2) # last supply column tap_list.append((cur_col, True)) cur_col = self._draw_supply_column(cur_col, sup_info, vdd_table, vss_table, ridx_p, ridx_n, True) self._tap_columns = ImmutableList(tap_list) self.set_mos_size(num_cols=cur_col) # routing # supplies vss = vdd = None for lay in range(hm_layer, xm_layer + 1, 2): vss = vss_table[lay] vdd = vdd_table[lay] if lay == hm_layer: vss.append(pupd_inst.get_pin('VSS')) vdd.append(pupd_inst.get_pin('VDD')) vdd = self.connect_wires(vdd) vss = self.connect_wires(vss) self.add_pin(f'VDD_{lay}', vdd, hide=True) self.add_pin(f'VSS_{lay}', vss, hide=True) vdd = vdd[0] vss = vss[0] self.add_pin('VDD', vdd) self.add_pin('VSS', vss) self.add_pin('VDD_vm', self.connect_wires(vdd_table[vm_layer]), hide=True) self.add_pin('VSS_vm', vss_table[vm_layer], hide=True) self.add_pin('VDD_conn', vdd_table[conn_layer], hide=True) self.add_pin('VSS_conn', vss_table[conn_layer], hide=True) # signals self.connect_wires(pin_dict['out_hm']) din = self.connect_wires(pin_dict['din']) out_list = pin_dict['out'] out_upper = max((warr.upper for warr in out_list)) out = self.connect_wires(out_list, upper=out_upper)[0] en_list = pin_dict['en'] enb_list = pin_dict['enb'] p_en_1 = self.connect_wires(en_list[4:])[0] n_enb_1 = self.connect_wires(enb_list[4:])[0] din = self.connect_to_tracks( din, TrackID(vm_layer, din_tidx, width=tr_manager.get_width(vm_layer, din_type))) pub = self.connect_to_tracks(pupd_inst.get_pin('puenb'), p_en_1.track_id, min_len_mode=MinLenMode.LOWER) pd = self.connect_to_tracks(pupd_inst.get_pin('pden'), n_enb_1.track_id, min_len_mode=MinLenMode.UPPER) self.add_pin('din', din) self.add_pin('p_en_drv<0>', en_list[0]) self.add_pin('n_enb_drv<0>', enb_list[0]) self.add_pin('tristateb', self.connect_wires(en_list[1:4])) self.add_pin('tristate', self.connect_wires(enb_list[1:4])) self.add_pin('p_en_drv<1>', p_en_1) self.add_pin('n_enb_drv<1>', n_enb_1) self.add_pin('weak_puenb', pub) self.add_pin('weak_pden', pd) self.add_pin('txpadout', out) self.sch_params = dict( unit_params=unit_master.sch_params, pupd_params=pupd_master.sch_params, )
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, )), )
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'] w_dict, th_dict = self._get_w_th_dict(ridx_n, ridx_p) seg_in = seg_dict['in'] seg_tail = seg_dict['tail'] seg_fb = seg_dict['fb'] seg_swm = seg_dict['sw'] w_in = w_dict['in'] w_tail = w_dict['tail'] w_pfb = w_dict['pfb'] min_sep = self.min_sep_col min_sep += (min_sep & 1) if seg_in & 1 or seg_tail & 1 or seg_fb & 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 if 2 * seg_swo + seg_swm > seg_in + seg_tail + min_sep: raise ValueError( 'the reset switches are too large compared to input devices.') # placement hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 grid = self.grid tr_manager = self.tr_manager hm_w = tr_manager.get_width(hm_layer, 'sig') 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') nd0_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-3) nd1_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-2) nd2_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-1) pd0_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=0) pd1_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=1) ng0_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0) ng1_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1) 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) self._out_tinfo = (hm_w, pg0_tid.base_index, ng1_tid.base_index) m_tail = self.add_mos(ridx_n, 0, seg_tail, w=w_tail, g_on_s=True, stack=2, sep_g=True) cur_col = seg_tail * 2 + min_sep m_in = self.add_mos(ridx_n, cur_col, seg_in, w=w_in) cur_col += seg_in m_nfb = self.add_mos(ridx_n, cur_col, seg_fb, w=w_in) m_pfb = self.add_mos(ridx_p, cur_col, seg_fb, w=w_pfb) cur_col -= seg_swo m_swo_rst = self.add_mos(ridx_p, cur_col, seg_swo, w=w_pfb) cur_col -= seg_swo m_swo = self.add_mos(ridx_p, cur_col, seg_swo, w=w_pfb) # flip middle node switch so it works for both even and odd number of segments m_swm = self.add_mos(ridx_p, cur_col, seg_swm, w=w_pfb, flip_lr=True) self.set_mos_size() # routing # supplies vdd = self.connect_to_tracks([m_pfb.s, m_swo.s, m_swo_rst.s, m_swm.s], vdd_tid) vss = self.connect_to_tracks(m_tail.s, vss_tid) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) # drain/source connections tail = self.connect_to_tracks([m_tail.d, m_in.d], nd0_tid) nmid = self.connect_to_tracks([m_in.s, m_nfb.s], nd2_tid) nout = self.connect_to_tracks(m_nfb.d, nd1_tid, min_len_mode=MinLenMode.LOWER) pout = self.connect_to_tracks([m_pfb.d, m_swo.d, m_swo_rst.d], pd1_tid) pmid = self.connect_to_tracks(m_swm.d, pd0_tid, min_len_mode=MinLenMode.MIDDLE) self.add_pin('pout', pout) self.add_pin('nout', nout) self.add_pin('tail', tail) # mid node connection vm_tidx = grid.coord_to_track(vm_layer, pmid.middle, mode=RoundMode.NEAREST) self.connect_to_tracks([nmid, pmid], TrackID(vm_layer, vm_tidx, width=vm_w)) # gate connections nclk = self.connect_to_tracks(m_tail.g[1::2], ng0_tid, min_len_mode=MinLenMode.MIDDLE) pclk = self.connect_to_tracks([m_swo.g, m_swm.g], pg0_tid) vm_tidx = tr_manager.get_next_track(vm_layer, vm_tidx, 'sig', 'sig', up=False) clk_vm = self.connect_to_tracks([nclk, pclk], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('clk_vm', clk_vm) self.add_pin('clk', nclk) prstb = self.connect_to_tracks([m_tail.g[0::2], m_swo_rst.g], pg1_tid, min_len_mode=MinLenMode.LOWER) self.add_pin('outb', self.connect_wires([m_nfb.g, m_pfb.g])) self.add_pin( 'in', self.connect_to_tracks(m_in.g, ng1_tid, min_len_mode=MinLenMode.LOWER)) self.add_pin('rstb', prstb) sch_seg_dict = seg_dict.copy(append=dict(swo=seg_swo, swm=seg_swm, nfb=seg_fb, pfb=seg_fb, br=1), remove=['sw', 'fb']) self.sch_params = dict( lch=self.arr_info.lch, seg_dict=sch_seg_dict, w_dict=w_dict, th_dict=th_dict, has_rstb=True, has_bridge=True, )
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) flip_tile: bool = self.params['flip_tile'] self.draw_base(pinfo, flip_tile=flip_tile) seg_dict: Dict[str, int] = self.params['seg_dict'] w_dict: Dict[str, int] = self.params['w_dict'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] sig_locs: Mapping[str, Union[float, HalfInt]] = self.params['sig_locs'] vertical_rst: bool = self.params['vertical_rst'] substrate_row: bool = self.params['substrate_row'] min_sep = self.min_sep_col seg_buf = seg_dict.get('buf', 1) seg_in = seg_dict.get('in', 1) seg_mux = seg_dict.get('mux', 1) seg_keep = seg_dict.get('keep', 1) seg_pass = seg_dict.get('pass', 1) seg_rst = seg_dict.get('rst', 1) seg_out = seg_dict.get('out', 1) tile0 = self.params['tile0'] tile1 = self.params['tile1'] if substrate_row: vss0_tid = None vdd_tid = None vss1_tid = None else: vss0_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=tile0) vdd_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sup') vss1_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=tile1) place_info = self.get_tile_pinfo(tile0) default_wn = place_info.get_row_place_info(ridx_n).row_info.width default_wp = place_info.get_row_place_info(ridx_p).row_info.width wp_buf = w_dict.get('p_buf', default_wp) wn_buf = w_dict.get('n_buf', default_wn) wp_in = w_dict.get('p_in', default_wp) wn_in = w_dict.get('n_in', default_wn) wp_mux = w_dict.get('p_mux', default_wp) wn_mux = w_dict.get('n_mux', default_wn) wp_keep = w_dict.get('p_keep', default_wp) wn_keep = w_dict.get('n_keep', default_wn) wp_pass = w_dict.get('p_pass', default_wp) wn_pass = w_dict.get('n_pass', default_wn) wp_rst = w_dict.get('p_rst', default_wp) wn_rst = w_dict.get('n_rst', default_wn) wp_out = w_dict.get('p_out', default_wp) wn_out = w_dict.get('n_out', default_wn) # tracks t0_nd0_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile0) t0_nd1_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile0) t0_ng0_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile0) t0_ng1_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile0) t0_pg0_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile0) t0_pg1_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile0) t0_pd0_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile0) t0_pd1_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile0) t1_pd1_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile1) t1_pd0_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile1) t1_pg1_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile1) t1_pg0_tid = self.get_track_id(ridx_p, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile1) t1_ng1_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=1, tile_idx=tile1) t1_ng0_tid = self.get_track_id(ridx_n, MOSWireType.G, 'sig', wire_idx=0, tile_idx=tile1) t1_nd1_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=1, tile_idx=tile1) t1_nd0_tid = self.get_track_id(ridx_n, MOSWireType.DS, 'sig', wire_idx=0, tile_idx=tile1) # placement # tile 0 cur_col = 0 mos_in_n = self.add_mos(ridx_n, cur_col, seg_in, w=wn_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) mos_in_p = self.add_mos(ridx_p, cur_col, seg_in, w=wp_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) cur_col += 2 * seg_in + min_sep mos_keep_n = self.add_mos(ridx_n, cur_col, seg_keep, w=wn_keep, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) mos_keep_p = self.add_mos(ridx_p, cur_col, seg_keep, w=wp_keep, g_on_s=True, stack=2, sep_g=True, tile_idx=tile0) cur_col += 2 * seg_keep + min_sep mos_pass_n = self.add_mos(ridx_n, cur_col, seg_pass, w=wn_pass, g_on_s=False, tile_idx=tile0) mos_pass_p = self.add_mos(ridx_p, cur_col, seg_pass, w=wp_pass, g_on_s=False, tile_idx=tile0) cur_col += seg_pass + min_sep mos_buf_n = self.add_mos(ridx_n, cur_col, 2 * seg_buf, w=wn_buf, g_on_s=True, sep_g=True, tile_idx=tile0) mos_buf_p = self.add_mos(ridx_p, cur_col, 2 * seg_buf, w=wp_buf, g_on_s=True, sep_g=True, tile_idx=tile0) cur_col += 2 * seg_buf + min_sep mos_out_n = self.add_mos(ridx_n, cur_col, 2 * seg_out, w=wn_out, tile_idx=tile0) mos_out_p = self.add_mos(ridx_p, cur_col, 2 * seg_out, w=wp_out, tile_idx=tile0) col_tot = cur_col + 2 * seg_out # tile 1 cur_col = 0 mos_si_n = self.add_mos(ridx_n, cur_col, seg_in, w=wn_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) mos_si_p = self.add_mos(ridx_p, cur_col, seg_in, w=wp_in, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) cur_col += 2 * seg_in + min_sep mos_mux_n = self.add_mos(ridx_n, cur_col, seg_mux, w=wn_mux, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) mos_mux_p = self.add_mos(ridx_p, cur_col, seg_mux, w=wp_mux, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) cur_col = col_tot - seg_rst - min_sep - 4 * seg_rst sub_col = cur_col mos_rst_n = self.add_mos(ridx_n, cur_col, 2 * seg_rst, w=wn_rst, g_on_s=True, stack=2, sep_g=True, tile_idx=tile1) mos_rst_p = self.add_mos(ridx_p, cur_col, 4 * seg_rst, w=wp_rst, g_on_s=True, sep_g=True, tile_idx=tile1) cur_col = col_tot - seg_rst mos_rst2_n = self.add_mos(ridx_n, cur_col, seg_rst, w=wn_rst, g_on_s=True, tile_idx=tile1) mos_rst2_p = self.add_mos(ridx_p, cur_col, seg_rst, w=wp_rst, g_on_s=False, tile_idx=tile1) if substrate_row: self._substrate_row_intvl.append((sub_col, self.num_cols-sub_col)) for _tidx in range(pinfo[0].num_tiles): _pinfo = self.get_tile_pinfo(_tidx) for _ridx in range(_pinfo.num_rows): rtype = _pinfo.get_row_place_info(_ridx).row_info.row_type if rtype.is_substrate: warrs = self.add_substrate_contact(_ridx, sub_col, tile_idx=_tidx, seg=self.num_cols-sub_col) sup_name = 'VDD' if rtype is MOSType.ntap else 'VSS' self.add_pin(f'{sup_name}_sub', warrs, label=f'{sup_name}:') self.set_mos_size() # vertical routing track planning grid = self.grid tr_manager = self.tr_manager conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 vm_w = tr_manager.get_width(vm_layer, 'sig') vm_se_tidx = grid.coord_to_track(vm_layer, 0) vm_seb_tidx = tr_manager.get_next_track(vm_layer, vm_se_tidx, 'sig', 'sig') vm_o1_tidx = tr_manager.get_next_track(vm_layer, vm_seb_tidx, 'sig', 'sig') vm_ck1_tidx = tr_manager.get_next_track(vm_layer, vm_o1_tidx, 'sig', 'sig') x_ck1 = grid.track_to_coord(conn_layer, mos_mux_p.g0.track_id.base_index) vm_ck1_tidx = max(vm_ck1_tidx, grid.coord_to_track(vm_layer, x_ck1, mode=RoundMode.LESS_EQ)) x_ck2 = grid.track_to_coord(conn_layer, mos_rst_p.g[-1].track_id.base_index) vm_ck2_tidx = grid.coord_to_track(vm_layer, x_ck2, mode=RoundMode.GREATER_EQ) vm_o5_tidx = tr_manager.get_next_track(vm_layer, vm_ck2_tidx, 'sig', 'sig') vm_o42_tidx = tr_manager.get_next_track(vm_layer, vm_o5_tidx, 'sig', 'sig') x_ckb = grid.track_to_coord(conn_layer, mos_buf_n.s[0].track_id.base_index) vm_ckb_tidx = grid.coord_to_track(vm_layer, x_ckb, mode=RoundMode.GREATER_EQ) vm_seb2_tidx = tr_manager.get_next_track(vm_layer, vm_ck2_tidx, 'sig', 'sig', up=False) x_o41 = grid.track_to_coord(conn_layer, mos_pass_n.d.track_id.base_index) vm_o41_tidx = grid.coord_to_track(vm_layer, x_o41, mode=RoundMode.LESS_EQ) x_o2 = x_o41 - self.place_info.sd_pitch * 2 vm_o2_tidx = grid.coord_to_track(vm_layer, x_o2, mode=RoundMode.LESS_EQ) # routing # supplies vdd_list = [mos_in_p.s, mos_keep_p.d, mos_buf_p.d, mos_out_p.d, mos_si_p.s, mos_mux_p.d, mos_rst_p.s[0:2], mos_rst2_p.s] vss0_list = [mos_in_n.s, mos_keep_n.d, mos_buf_n.d, mos_out_n.d] vss1_list = [mos_si_n.s, mos_mux_n.d, mos_rst_n.d] if substrate_row: if tile0 < tile1: self.add_pin('VSS1', vss1_list, label='VSS:') self.add_pin('VSS0', vss0_list, label='VSS:') else: self.add_pin('VSS1', vss0_list, label='VSS:') self.add_pin('VSS0', vss1_list, label='VSS:') self.add_pin('VDD', vdd_list, label='VDD:') else: self.add_pin('VDD', self.connect_to_tracks(vdd_list, vdd_tid)) self.add_pin('VSS', self.connect_to_tracks(vss0_list, vss0_tid), connect=True) self.add_pin('VSS', self.connect_to_tracks(vss1_list, vss1_tid), connect=True) # input self.add_pin('in', self.connect_to_tracks([mos_in_n.g[0], mos_in_p.g[0]], t0_pg1_tid)) # scan in self.add_pin('scan_in', self.connect_to_tracks([mos_si_n.g[0], mos_si_p.g[0]], t1_ng1_tid)) # scan enable se = self.connect_to_tracks([mos_in_p.g[1], mos_buf_p.g[1], mos_buf_n.g[1]], t0_pg0_tid) se_top = self.connect_to_tracks(mos_si_n.g[1], t1_ng0_tid, min_len_mode=MinLenMode.LOWER) se_vm = self.connect_to_tracks([se, se_top], TrackID(vm_layer, vm_se_tidx, width=vm_w)) self.add_pin('scan_en', se) self.add_pin('scan_en_vm', se_vm, hide=True) # scan enable bar seb0 = self.connect_to_tracks(mos_buf_n.s[1], t0_nd0_tid, min_len_mode=MinLenMode.UPPER) seb1 = self.connect_to_tracks(mos_in_n.g[1], t0_ng1_tid, min_len_mode=MinLenMode.LOWER) seb2 = self.connect_to_tracks(mos_buf_p.s[1], t0_pd0_tid, min_len_mode=MinLenMode.LOWER) seb3 = self.connect_to_tracks(mos_si_p.g[1], t1_pg0_tid, min_len_mode=MinLenMode.LOWER) self.connect_to_tracks([seb1, seb2, seb3], TrackID(vm_layer, vm_seb_tidx, width=vm_w)) self.connect_to_tracks([seb0, seb2], TrackID(vm_layer, vm_seb2_tidx, width=vm_w)) # clk ck = self.connect_to_tracks([mos_keep_n.g[0], mos_pass_n.g, mos_buf_n.g[0], mos_buf_p.g[0]], t0_ng0_tid) ck1 = self.connect_to_tracks(mos_mux_p.g0, t1_pg0_tid, min_len_mode=MinLenMode.UPPER) ck_vm = self.connect_to_tracks([ck, ck1], TrackID(vm_layer, vm_ck1_tidx, width=vm_w)) ck2 = self.connect_to_tracks(mos_rst_p.g[-1], t1_pg0_tid, min_len_mode=MinLenMode.UPPER) self.connect_to_tracks([ck, ck2], TrackID(vm_layer, vm_ck2_tidx, width=vm_w)) self.add_pin('clk', ck) self.add_pin('clk_vm', ck_vm, hide=True) # clkb ckb0 = self.connect_to_tracks(mos_buf_n.s[0], t0_nd1_tid, min_len_mode=MinLenMode.UPPER) ckb1 = self.connect_to_tracks([mos_keep_p.g[0], mos_pass_p.g], t0_pg1_tid) ckb2 = self.connect_to_tracks(mos_buf_p.s[0], t0_pd1_tid, min_len_mode=MinLenMode.UPPER) ckb3 = self.connect_to_tracks([mos_mux_n.g[0], mos_rst2_n.g], t1_ng0_tid) self.connect_to_tracks([ckb0, ckb1, ckb2, ckb3], TrackID(vm_layer, vm_ckb_tidx, width=vm_w)) # o1 o10 = self.connect_to_tracks(mos_in_n.d, t0_nd1_tid, min_len_mode=MinLenMode.LOWER) o11 = self.connect_to_tracks([mos_in_p.d, mos_si_p.d], t1_pd0_tid, min_len_mode=MinLenMode.MIDDLE) o12 = self.connect_to_tracks([mos_mux_n.g[1], mos_mux_p.g[1]], t1_ng1_tid) o13 = self.connect_to_tracks(mos_si_n.d, t1_nd1_tid, min_len_mode=MinLenMode.MIDDLE) self.connect_to_tracks([o10, o11, o12, o13], TrackID(vm_layer, vm_o1_tidx, width=vm_w)) # o2 o20 = self.connect_to_tracks(mos_keep_n.s, t0_nd1_tid) o21 = self.connect_to_tracks([mos_keep_p.s, mos_mux_p.s], t1_pd1_tid) o22 = self.connect_to_tracks([mos_rst_n.g[0], mos_rst_p.g[0]], t1_pg0_tid) o23 = self.connect_to_tracks(mos_mux_n.s, t1_nd0_tid) self.connect_to_tracks([o20, o21, o22, o23], TrackID(vm_layer, vm_o2_tidx, width=vm_w)) # o3 o30 = self.connect_to_tracks([mos_keep_n.g[1], mos_keep_p.g[1]], t0_ng1_tid) o31 = self.connect_to_tracks(mos_rst_p.d[0], t1_pd0_tid) o32 = self.connect_to_tracks(mos_rst_n.s[0], t1_nd1_tid) o3_track = self.connect_wires([mos_pass_n.s, mos_pass_p.s]) self.connect_to_track_wires([o30, o31, o32], o3_track) # o4 o40 = self.connect_to_tracks(mos_pass_n.d, t0_nd0_tid, min_len_mode=MinLenMode.LOWER) o41 = self.connect_to_tracks([mos_out_n.g, mos_out_p.g], t0_pg1_tid) o42 = self.connect_to_tracks(mos_pass_p.d, t0_pd1_tid, min_len_mode=MinLenMode.LOWER) o43 = self.connect_to_tracks(mos_rst_p.d[1], t1_pd1_tid) o44 = self.connect_to_tracks(mos_rst2_n.s, t1_nd0_tid) self.connect_to_tracks([o40, o42, o43, o44], TrackID(vm_layer, vm_o41_tidx, width=vm_w)) self.connect_to_tracks([o41, o43], TrackID(vm_layer, vm_o42_tidx, width=vm_w)) # tp and tn self.connect_to_tracks([mos_rst_n.s[-1], mos_rst2_n.d], t1_nd1_tid) self.connect_to_tracks([mos_rst_p.s[-1], mos_rst2_p.d], t1_pd0_tid) # o5 o50 = self.connect_to_tracks([mos_out_n.s[0], mos_out_p.s[0]], t0_pd1_tid, min_len_mode=MinLenMode.UPPER) o51 = self.connect_to_tracks([mos_rst_n.g[-1], mos_rst2_p.g], t1_ng1_tid) self.connect_to_tracks([o50, o51], TrackID(vm_layer, vm_o5_tidx, width=vm_w)) # output out_idx = sig_locs.get('out', t0_ng1_tid.base_index) out = self.connect_to_tracks([mos_out_n.s[1], mos_out_p.s[1]], TrackID(hm_layer, out_idx)) self.add_pin('out', out) # rstlb rstlb = self.connect_to_tracks([mos_rst_n.g[1], mos_rst_p.g[1]], t1_pg1_tid) self.add_pin('rstlb', rstlb) if vertical_rst: vm_idx = self.grid.coord_to_track(vm_layer, rstlb.middle, mode=RoundMode.NEAREST) avail = self.get_available_tracks(vm_layer, vm_ck1_tidx, vm_ck2_tidx, 0, self.bound_box.h) if vm_idx not in avail: raise ValueError(f'Recheck routing on layer {vm_layer}') rstlb_vm = self.connect_to_tracks(rstlb, TrackID(vm_layer, vm_idx, width=vm_w)) self.add_pin('rstlb_vm', rstlb_vm) self.sch_params = dict( lch=place_info.lch, seg_dict={'buf': seg_buf, 'in': seg_in, 'mux': seg_mux, 'keep': seg_keep, 'pass': seg_pass, 'rst': seg_rst, 'out': seg_out}, w_dict=dict(p_buf=wp_buf, n_buf=wn_buf, p_in=wp_in, n_in=wn_in, p_mux=wp_mux, n_mux=wn_mux, p_keep=wp_keep, n_keep=wn_keep, p_pass=wp_pass, n_pass=wn_pass, p_rst=wp_rst, n_rst=wn_rst, p_out=wp_out, n_out=wn_out), th_p=place_info.get_row_place_info(ridx_p).row_info.threshold, th_n=place_info.get_row_place_info(ridx_n).row_info.threshold, )
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_outbuf: bool = self.params['has_outbuf'] has_inbuf: bool = self.params['has_inbuf'] out_pitch: HalfInt = HalfInt.convert(self.params['out_pitch']) w_dict, th_dict = self._get_w_th_dict(ridx_n, ridx_p, has_rstb) seg_fb = seg_dict['fb'] seg_ps = seg_dict['ps'] seg_nr = seg_dict['nr'] seg_obuf = seg_dict['obuf'] if has_outbuf else 0 seg_ibuf = seg_dict['ibuf'] if has_inbuf else 0 w_pfb = w_dict['pfb'] w_nfb = w_dict['nfb'] w_ps = w_dict['ps'] w_nr = w_dict['nr'] w_rst = w_dict.get('pr', 0) w_nbuf = w_nr w_pbuf = w_ps sch_seg_dict = dict(nfb=seg_fb, pfb=seg_fb, ps=seg_ps, nr=seg_nr) if has_rstb: sch_seg_dict['pr'] = seg_rst = seg_dict['rst'] else: seg_rst = 0 if seg_ps & 1 or seg_nr & 1 or seg_rst & 1 or seg_obuf & 1: raise ValueError( 'ps, nr, rst, and buf must have even number of segments') # placement min_sep = self.min_sep_col # use even step size to maintain supply conn_layer wires parity. min_sep += (min_sep & 1) hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 grid = self.grid arr_info = self.arr_info tr_manager = self.tr_manager hm_w = tr_manager.get_width(hm_layer, 'sig') vm_w = tr_manager.get_width(vm_layer, 'sig') hm_sep_col = self.get_hm_sp_le_sep_col(ntr=hm_w) mid_sep = max(hm_sep_col - 2, 0) mid_sep = (mid_sep + 1) // 2 if has_inbuf: m_nibuf = self.add_mos(ridx_n, mid_sep, seg_ibuf, w=w_nbuf) m_pibuf = self.add_mos(ridx_p, mid_sep, seg_ibuf, w=w_pbuf) cur_col = mid_sep + seg_ibuf psrb_list = [m_nibuf.g, m_pibuf.g] else: m_nibuf = m_pibuf = None cur_col = mid_sep psrb_list = [] nr_col = cur_col m_nr = self.add_mos(ridx_n, cur_col, seg_nr, w=w_nr) m_ps = self.add_mos(ridx_p, cur_col, seg_ps, w=w_ps) psrb_list.append(m_ps.g) pcol = cur_col + seg_ps if has_rstb: m_rst = self.add_mos(ridx_p, pcol, seg_rst, w=w_rst) pcol += seg_rst else: m_rst = None cur_col = max(cur_col + seg_nr, pcol) if has_outbuf: m_pinv = self.add_mos(ridx_p, cur_col, seg_obuf, w=w_pbuf) m_ninv = self.add_mos(ridx_n, cur_col, seg_obuf, w=w_nbuf) cur_col += seg_obuf else: m_pinv = m_ninv = None cur_col += min_sep fb_col = cur_col m_pfb = self.add_mos(ridx_p, cur_col, seg_fb, w=w_pfb, g_on_s=True, stack=2, sep_g=True) m_nfb = self.add_mos(ridx_n, cur_col, seg_fb, w=w_nfb, g_on_s=True, stack=2, sep_g=True) self.set_mos_size() # track planning vss_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sup') nbuf_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-2) nq_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-1) psr_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1) pq_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=0) pbuf_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=1) vdd_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sup') # try to spread out gate wires to lower parasitics on differential Q wires ng_lower = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0) ng_upper = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=-1) g_idx_list = tr_manager.spread_wires( hm_layer, ['sig', 'sig_hs', 'sig_hs', 'sig'], ng_lower, ng_upper, ('sig_hs', 'sig_hs'), alignment=0) self._q_tr_info = (hm_w, g_idx_list[2], g_idx_list[1]) self._sr_hm_tr_info = (hm_w, g_idx_list[3], g_idx_list[0]) if has_rstb: rst_tid = self.get_track_id(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-2) pq_conn_list = [m_ps.d, m_rst.d, m_pfb.s] vdd_list = [m_ps.s, m_rst.s, m_pfb.d] vss_list = [m_nr.s, m_nfb.d] rstb = self.connect_to_tracks(m_rst.g, rst_tid, min_len_mode=MinLenMode.MIDDLE) rst_vm_tidx = grid.coord_to_track(vm_layer, rstb.middle, mode=RoundMode.GREATER_EQ) rstb_vm = self.connect_to_tracks( rstb, TrackID(vm_layer, rst_vm_tidx, width=vm_w)) self.add_pin('rstb', rstb_vm) else: pq_conn_list = [m_ps.d, m_pfb.s] vdd_list = [m_ps.s, m_pfb.d] vss_list = [m_nr.s, m_nfb.d] self.add_pin('nsr', m_nr.g) self.add_pin('nsrb', m_nfb.g[0::2]) nq = self.connect_to_tracks([m_nr.d, m_nfb.s], nq_tid) pq = self.connect_to_tracks(pq_conn_list, pq_tid) psrb = self.connect_to_tracks(psrb_list, psr_tid, min_len_mode=MinLenMode.UPPER) psr = self.connect_to_tracks(m_pfb.g[0::2], psr_tid, min_len_mode=MinLenMode.LOWER) qb = self.connect_wires([m_nfb.g[1::2], m_pfb.g[1::2]]) self.add_pin('qb', qb) if has_inbuf: vdd_list.append(m_pibuf.s) vss_list.append(m_nibuf.s) nbuf = self.connect_to_tracks(m_nibuf.d, nbuf_tid, min_len_mode=MinLenMode.UPPER) pbuf = self.connect_to_tracks(m_pibuf.d, pbuf_tid, min_len_mode=MinLenMode.UPPER) vm_tidx = grid.coord_to_track(vm_layer, nbuf.middle, mode=RoundMode.LESS_EQ) buf = self.connect_to_tracks([nbuf, pbuf], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('sr_buf', buf) out_p_htr = out_pitch.dbl_value vm_ref = grid.coord_to_track(vm_layer, 0) srb_vm_tidx = arr_info.col_to_track(vm_layer, nr_col + 1, mode=RoundMode.GREATER_EQ) if has_outbuf: vdd_list.append(m_pinv.s) vss_list.append(m_ninv.s) nbuf = self.connect_to_tracks(m_ninv.d, nbuf_tid, min_len_mode=MinLenMode.MIDDLE) pbuf = self.connect_to_tracks(m_pinv.d, pbuf_tid, min_len_mode=MinLenMode.MIDDLE) vm_delta = grid.coord_to_track( vm_layer, nbuf.middle, mode=RoundMode.LESS_EQ) - vm_ref vm_htr = -(-vm_delta.dbl_value // out_p_htr) * out_p_htr vm_tidx = vm_ref + HalfInt(vm_htr) buf = self.connect_to_tracks([nbuf, pbuf], TrackID(vm_layer, vm_tidx, width=vm_w)) self.add_pin('buf_out', buf) buf_in = self.connect_wires([m_ninv.g, m_pinv.g]) self.add_pin('buf_in', buf_in) q_vm_tidx = tr_manager.get_next_track(vm_layer, srb_vm_tidx, 'sig', 'sig') else: vm_delta = tr_manager.get_next_track(vm_layer, srb_vm_tidx, 'sig', 'sig') - vm_ref vm_htr = -(-vm_delta.dbl_value // out_p_htr) * out_p_htr q_vm_tidx = vm_ref + HalfInt(vm_htr) sr_vm_tidx = arr_info.col_to_track(vm_layer, fb_col, mode=RoundMode.LESS_EQ) self._sr_vm_tr_info = (vm_w, sr_vm_tidx, srb_vm_tidx) q_vm = self.connect_to_tracks([nq, pq], TrackID(vm_layer, q_vm_tidx, width=vm_w)) self.add_pin('q_vm', q_vm) self.add_pin('psr', psr) self.add_pin('psrb', psrb) self.add_pin('VDD', self.connect_to_tracks(vdd_list, vdd_tid)) self.add_pin('VSS', self.connect_to_tracks(vss_list, vss_tid)) lch = arr_info.lch buf_params = ImmutableSortedDict( dict( lch=lch, w_p=w_pbuf, w_n=w_nbuf, th_p=th_dict['ps'], th_n=th_dict['nr'], seg=seg_obuf, )) obuf_params = buf_params if has_outbuf else None ibuf_params = buf_params.copy(append=dict( seg=seg_ibuf)) if has_inbuf else None self.sch_params = dict( core_params=ImmutableSortedDict( dict( lch=lch, seg_dict=ImmutableSortedDict(sch_seg_dict), w_dict=w_dict, th_dict=th_dict, )), outbuf_params=obuf_params, inbuf_params=ibuf_params, has_rstb=has_rstb, )
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) core_params: Param = self.params['core_params'] sync_params: Param = self.params['sync_params'] buf_params: Param = self.params['buf_params'] nsync: int = self.params['nsync'] vm_pitch: HalfInt = HalfInt.convert(self.params['vm_pitch']) # create masters core_params = core_params.copy(append=dict(pinfo=pinfo, vm_pitch=vm_pitch)) half_params = dict(pinfo=pinfo, sync_params=sync_params, buf_params=buf_params, nsync=nsync) core_master: DCCHelperCore = self.new_template(DCCHelperCore, params=core_params) sync_master: SyncChain = self.new_template(SyncChain, params=half_params) buf_ncol = sync_master.buf_ncol # placement hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 vm_w = self.tr_manager.get_width(vm_layer, 'sig') min_sep = self.min_sep_col min_sep += (min_sep & 1) core_ncol = core_master.num_cols sync_ncol = sync_master.num_cols center_ncol = max(2 * buf_ncol + min_sep, core_ncol) half_sep = center_ncol - 2 * buf_ncol core_off = sync_ncol - buf_ncol + (center_ncol - core_ncol) // 2 # add masters core = self.add_tile(core_master, 1, core_off) syncl = self.add_tile(sync_master, 0, sync_ncol, flip_lr=True) syncr = self.add_tile(sync_master, 0, sync_ncol + half_sep) self.set_mos_size(num_cols=2 * sync_ncol + half_sep) bbox = self.bound_box xl = bbox.xl xh = bbox.xh xm = (xl + xh) // 2 # routing # supplies for lay in [hm_layer, xm_layer]: for name in ['VDD', 'VSS']: cur_name = f'{name}_{lay}' warrs = syncl.get_all_port_pins(cur_name) warrs.extend(syncr.port_pins_iter(cur_name)) warrs = self.connect_wires(warrs, lower=xl, upper=xh) if lay == xm_layer: self.add_pin(name, warrs) # rstb for synchronizers grid = self.grid vm_p_htr = vm_pitch.dbl_value vm_center = grid.coord_to_track(vm_layer, xm) vm_delta = grid.coord_to_track(vm_layer, xh, mode=RoundMode.LESS_EQ) - vm_center vm_dhtr = -(-vm_delta.dbl_value // vm_p_htr) * vm_p_htr vm_delta = HalfInt(vm_dhtr) rstlb_r_tidx = vm_center + vm_delta rstlb_l_tidx = vm_center - vm_delta rstlbr_list = syncr.get_all_port_pins('rstlb') vss_bot = syncr.get_pin('VSS_bot') rstlbr_list.append(vss_bot) rstb_ref = self.connect_to_tracks(rstlbr_list, TrackID(vm_layer, rstlb_r_tidx, width=vm_w)) rstlbl_list = syncl.get_all_port_pins('rstlb') rstb = self.connect_to_tracks(rstlbl_list, TrackID(vm_layer, rstlb_l_tidx, width=vm_w), track_lower=rstb_ref.lower) self.add_pin('rstb', rstb) # clk for synchronizers clk_ref = self.connect_to_track_wires(vss_bot, syncr.get_pin('clk_sync')) clkl = self.extend_wires(syncl.get_pin('clk_sync'), lower=clk_ref.lower) clk_ref = self.connect_to_track_wires(clkl, syncl.get_pin('clk_buf')) self.extend_wires(syncr.get_pin('clk_buf'), upper=2 * xm - clk_ref.lower) # rstb_sync warr_list = [] rstb_sync = syncl.get_pin('out') rstb_ref = self.connect_to_tracks(core.get_pin('rstlb_vm_l'), rstb_sync.track_id, ret_wire_list=warr_list) self.extend_wires(core.get_pin('rsthb'), upper=warr_list[0].upper) rstb_ref = self.connect_wires([rstb_ref, syncl.get_pin('out')])[0] self.extend_wires(syncr.get_pin('out'), lower=2 * xm - rstb_ref.upper) # input clocks launch = self.connect_to_track_wires(syncl.get_pin('clk_in'), core.get_pin('launch')) measure = self.connect_to_track_wires(syncr.get_pin('clk_in'), core.get_pin('measure')) self.add_pin('launch', self.extend_wires(launch, lower=0)) self.add_pin('measure', self.extend_wires(measure, lower=0)) # reexports self.reexport(core.get_port('clk_out'), net_name='ckout') self.reexport(core.get_port('clk_dcd')) self.reexport(core.get_port('dcc_byp')) self.sch_params = sync_master.sch_params.copy(append=dict( core_params=core_master.sch_params))
def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) grid = self.grid seg: int = self.params['seg'] seg_p: int = self.params['seg_p'] seg_n: int = self.params['seg_n'] w_p: int = self.params['w_p'] w_n: int = self.params['w_n'] stack_p: int = self.params['stack_p'] stack_n: int = self.params['stack_n'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] is_guarded: bool = self.params['is_guarded'] sig_locs: Mapping[str, Union[float, HalfInt]] = self.params['sig_locs'] vertical_out: bool = self.params['vertical_out'] vertical_sup: bool = self.params['vertical_sup'] vertical_in: bool = self.params['vertical_in'] if seg_p <= 0: seg_p = seg if seg_n <= 0: seg_n = seg if seg_p <= 0 or seg_n <= 0: raise ValueError('Invalid segments.') hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 if self.top_layer < vm_layer: raise ValueError(f'MOSBasePlaceInfo top layer must be at least {vm_layer}') # set is_guarded = True if both rows has same orientation rpinfo_n = self.get_row_info(ridx_n) rpinfo_p = self.get_row_info(ridx_p) is_guarded = is_guarded or rpinfo_n.flip == rpinfo_p.flip # Placement nports = self.add_mos(ridx_n, 0, seg_n, w=w_n, stack=stack_n) pports = self.add_mos(ridx_p, 0, seg_p, w=w_p, stack=stack_p) self.set_mos_size() # get wire_indices from sig_locs tr_manager = self.tr_manager tr_w_h = tr_manager.get_width(hm_layer, 'sig') tr_w_v = tr_manager.get_width(vm_layer, 'sig') nout_tidx = sig_locs.get('nout', self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)) pout_tidx = sig_locs.get('pout', self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=-1)) nout_tid = TrackID(hm_layer, nout_tidx, tr_w_h) pout_tid = TrackID(hm_layer, pout_tidx, tr_w_h) pout = self.connect_to_tracks(pports.d, pout_tid, min_len_mode=MinLenMode.NONE) nout = self.connect_to_tracks(nports.d, nout_tid, min_len_mode=MinLenMode.NONE) if vertical_out: vm_tidx = sig_locs.get('out', grid.coord_to_track(vm_layer, pout.middle, mode=RoundMode.NEAREST)) vm_tid = TrackID(vm_layer, vm_tidx, width=tr_w_v) self.add_pin('out', self.connect_to_tracks([pout, nout], vm_tid)) else: self.add_pin('out', [pout, nout], connect=True) vm_tidx = None if is_guarded: nin_tidx = sig_locs.get('nin', self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)) pin_tidx = sig_locs.get('pin', self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1)) nin = self.connect_to_tracks(nports.g, TrackID(hm_layer, nin_tidx, width=tr_w_h)) pin = self.connect_to_tracks(pports.g, TrackID(hm_layer, pin_tidx, width=tr_w_h)) self.add_pin('pin', pin, hide=True) self.add_pin('nin', nin, hide=True) if vertical_in: in_tidx = self.grid.find_next_track(vm_layer, nin.lower, tr_width=tr_w_v, mode=RoundMode.GREATER_EQ) if vm_tidx is not None: in_tidx = min(in_tidx, self.tr_manager.get_next_track(vm_layer, vm_tidx, 'sig', 'sig', up=False)) in_tidx = sig_locs.get('in', in_tidx) self.add_pin('in', self.connect_to_tracks([pin, nin], TrackID(vm_layer, in_tidx, width=tr_w_v))) else: self.add_pin('in', [pin, nin], connect=True) else: in_tidx = sig_locs.get('in', None) if in_tidx is None: in_tidx = sig_locs.get('nin', None) if in_tidx is None: default_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0) in_tidx = sig_locs.get('pin', default_tidx) in_warr = self.connect_to_tracks([nports.g, pports.g], TrackID(hm_layer, in_tidx, width=tr_w_h)) self.add_pin('in', in_warr) self.add_pin('pin', in_warr, hide=True) self.add_pin('nin', in_warr, hide=True) self.add_pin(f'pout', pout, hide=True) self.add_pin(f'nout', nout, hide=True) xr = self.bound_box.xh if vertical_sup: self.add_pin('VDD', pports.s, connect=True) self.add_pin('VSS', nports.s, connect=True) else: ns_tid = self.get_track_id(ridx_n, False, wire_name='sup') ps_tid = self.get_track_id(ridx_p, True, wire_name='sup') vss = self.connect_to_tracks(nports.s, ns_tid, track_lower=0, track_upper=xr) vdd = self.connect_to_tracks(pports.s, ps_tid, track_lower=0, track_upper=xr) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) default_wp = self.place_info.get_row_place_info(ridx_p).row_info.width default_wn = self.place_info.get_row_place_info(ridx_n).row_info.width thp = self.place_info.get_row_place_info(ridx_p).row_info.threshold thn = self.place_info.get_row_place_info(ridx_n).row_info.threshold lch = self.place_info.lch self.sch_params = dict( seg_p=seg_p, seg_n=seg_n, lch=lch, w_p=default_wp if w_p == 0 else w_p, w_n=default_wn if w_n == 0 else w_n, th_n=thn, th_p=thp, stack_p=stack_p, stack_n=stack_n, )
def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) seg: int = self.params['seg'] seg_p: int = self.params['seg_p'] seg_n: int = self.params['seg_n'] if seg_p <= 0: seg_p = seg if seg_n <= 0: seg_n = seg if seg_p <= 0 or seg_n <= 0: raise ValueError('Invalid segments.') w_p: int = self.params['w_p'] w_n: int = self.params['w_n'] stack_p: int = self.params['stack_p'] stack_n: int = self.params['stack_n'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] sig_locs: Dict[str, float] = self.params['sig_locs'] is_guarded: bool = self.params['is_guarded'] mlm: Dict[str, MinLenMode] = self.params['min_len_mode'] vertical_out: bool = self.params['vertical_out'] vertical_in: bool = self.params['vertical_in'] vertical_sup: bool = self.params['vertical_sup'] hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 if self.top_layer < vm_layer: raise ValueError(f'MOSBasePlaceInfo top layer must be at least {vm_layer}') tot_col = 2 * max(seg_p * stack_p, seg_n * stack_n) self.set_mos_size(tot_col) if is_guarded is False and stack_n != stack_p: raise ValueError(f'If is_guarded is False, then the layout generator requires that ' f'stack_n = {stack_n} == stack_p = {stack_p}.') pports = self.add_nand2(ridx_p, 0, seg_p, w=w_p, stack=stack_p) nports = self.add_nand2(ridx_n, 0, seg_n, w=w_n, stack=stack_n, other=True) xr = self.bound_box.xh if vertical_sup: self.add_pin('VDD', pports.s, connect=True) self.add_pin('VSS', nports.s, connect=True) else: ns_tid = self.get_track_id(ridx_n, MOSWireType.DS_GATE, wire_name='sup') ps_tid = self.get_track_id(ridx_p, MOSWireType.DS_GATE, wire_name='sup') vdd = self.connect_to_tracks(pports.s, ps_tid, track_lower=0, track_upper=xr) vss = self.connect_to_tracks(nports.s, ns_tid, track_lower=0, track_upper=xr) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) tr_manager = self.tr_manager tr_w_h = tr_manager.get_width(hm_layer, 'sig') tr_w_v = tr_manager.get_width(vm_layer, 'sig') mlm_in0 = mlm.get('in0', None) mlm_in1 = mlm.get('in1', None) nin_tid = get_adj_tid_list(self, ridx_n, sig_locs, MOSWireType.G, 'nin', True, tr_w_h) pin_tid = get_adj_tid_list(self, ridx_p, sig_locs, MOSWireType.G, 'pin', False, tr_w_h) if is_guarded: n_in0 = self.connect_to_tracks(nports.g0, nin_tid[0], min_len_mode=mlm_in0) n_in1 = self.connect_to_tracks(nports.g1, nin_tid[1], min_len_mode=mlm_in1) p_in0 = self.connect_to_tracks(pports.g0, pin_tid[0], min_len_mode=mlm_in0) p_in1 = self.connect_to_tracks(pports.g1, pin_tid[1], min_len_mode=mlm_in1) in0_tidx = sig_locs.get('in0', self.grid.coord_to_track(vm_layer, self.bound_box.xl, RoundMode.GREATER_EQ)) in1_tidx = sig_locs.get('in1', self.grid.coord_to_track(vm_layer, self.bound_box.xh, RoundMode.LESS_EQ)) if vertical_in: in0_vm = self.connect_to_tracks([n_in0, p_in0], TrackID(vm_layer, in0_tidx, width=tr_w_v)) in1_vm = self.connect_to_tracks([n_in1, p_in1], TrackID(vm_layer, in1_tidx, width=tr_w_v)) self.add_pin('in<0>', in0_vm) self.add_pin('in<1>', in1_vm) self.add_pin('nin<0>', n_in0, hide=True) self.add_pin('nin<1>', n_in1, hide=True) self.add_pin('pin<0>', p_in0, hide=True) self.add_pin('pin<1>', p_in1, hide=True) else: self.add_pin('nin<0>', n_in0, label='in<0>:') self.add_pin('nin<1>', n_in1, label='in<1>:') self.add_pin('pin<0>', p_in0, label='in<0>:') self.add_pin('pin<1>', p_in1, label='in<1>:') else: key_name = 'nin' if any(k in sig_locs for k in ['nin', 'nin0']) else 'pin' in_tid = get_adj_tid_list(self, ridx_n, sig_locs, MOSWireType.G, key_name, True, tr_w_h) in0_vm, in1_vm = [], [] in0 = self.connect_to_tracks(list(chain(nports.g0, pports.g0)), in_tid[0], min_len_mode=mlm_in0, ret_wire_list=in0_vm) in1 = self.connect_to_tracks(list(chain(nports.g1, pports.g1)), in_tid[1], min_len_mode=mlm_in0, ret_wire_list=in1_vm) self.add_pin('nin<0>', in0, hide=True) self.add_pin('pin<0>', in0, hide=True) self.add_pin('nin<1>', in1, hide=True) self.add_pin('pin<1>', in1, hide=True) self.add_pin('in<0>', in0_vm) self.add_pin('in<1>', in1_vm) pd_tidx = sig_locs.get('pout', self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)) pd_tid = TrackID(hm_layer, pd_tidx, width=tr_w_h) mlm_out = mlm.get('out', MinLenMode.MIDDLE) if self.can_short_adj_tracks and not is_guarded: # we can short adjacent source/drain tracks together, so we can avoid going to vm_layer out = self.connect_to_tracks([pports.d, nports.d], pd_tid, min_len_mode=mlm_out) self.add_pin('pout', out, hide=True) self.add_pin('out', nports.d) else: # need to use vm_layer to short adjacent tracks. nd_tidx = sig_locs.get('nout', self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=-1)) nd_tid = TrackID(hm_layer, nd_tidx, width=tr_w_h) nout = self.connect_to_tracks(nports.d, nd_tid, min_len_mode=mlm_out) pout = self.connect_to_tracks(pports.d, pd_tid, min_len_mode=mlm_out) vm_tidx = sig_locs.get('out', self.grid.coord_to_track(vm_layer, nout.middle, mode=RoundMode.GREATER_EQ)) if vertical_out: out = self.connect_to_tracks([pout, nout], TrackID(vm_layer, vm_tidx)) self.add_pin('pout', pout, hide=True) self.add_pin('nout', nout, hide=True) self.add_pin('out', out) else: self.add_pin('pout', pout, label='out:') self.add_pin('nout', nout, label='out:') self.sch_params = dict( seg_p=seg_p, seg_n=seg_n, lch=self.place_info.lch, w_p=self.place_info.get_row_place_info(ridx_p).row_info.width if w_p == 0 else w_p, w_n=self.place_info.get_row_place_info(ridx_n).row_info.width if w_n == 0 else w_n, 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=stack_p, stack_n=stack_n, )
def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) seg: int = self.params['seg'] seg_p: int = self.params['seg_p'] seg_n: int = self.params['seg_n'] if seg_p <= 0: seg_p = seg if seg_n <= 0: seg_n = seg if seg_p <= 0 or seg_n <= 0: raise ValueError('Invalid segments.') w_p: int = self.params['w_p'] w_n: int = self.params['w_n'] stack_p: int = self.params['stack_p'] stack_n: int = self.params['stack_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'] vertical_sup: bool = self.params['vertical_sup'] hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 if vertical_out and self.top_layer < vm_layer: raise ValueError(f'MOSBasePlaceInfo top layer must be at least {vm_layer}') if stack_p != stack_n: raise ValueError(f'The layout generator does not support stack_n = {stack_n} != ' f'stack_p = {stack_p}') # place instances and set size self.set_mos_size(2 * max(seg_p * stack_p, seg_n * stack_n)) nports = self.add_nand2(ridx_n, 0, seg_n, w=w_n, stack=stack_n) pports = self.add_nand2(ridx_p, 0, seg_p, w=w_p, stack=stack_p) # get track information tr_manager = pinfo.tr_manager tr_w_h = tr_manager.get_width(hm_layer, 'sig') if sig_locs is None: sig_locs = {} en_tidx = sig_locs.get('nen', self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0)) in_key = 'nin' if 'nin' in sig_locs else 'pin' in_tidx = sig_locs.get(in_key, self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=1)) enb_tidx = sig_locs.get('pen', self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=-1)) pout_tidx = sig_locs.get('pout', self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0)) nout_tidx = sig_locs.get('nout', self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=-1)) # connect wires tid = TrackID(hm_layer, in_tidx, width=tr_w_h) in_warr_list = [] hm_in = self.connect_to_tracks(list(chain(nports.g0, pports.g0)), tid, ret_wire_list=in_warr_list) self.add_pin('nin', hm_in, hide=True) self.add_pin('pin', hm_in, hide=True) self.add_pin('in', in_warr_list) tid = TrackID(hm_layer, en_tidx, width=tr_w_h) self.add_pin('en', self.connect_to_tracks(nports.g1, tid)) tid = TrackID(hm_layer, enb_tidx, width=tr_w_h) self.add_pin('enb', self.connect_to_tracks(pports.g1, tid)) tid = TrackID(hm_layer, nout_tidx, width=tr_w_h) nout = self.connect_to_tracks(nports.d, tid) tid = TrackID(hm_layer, pout_tidx, width=tr_w_h) pout = self.connect_to_tracks(pports.d, tid) # connect output if vertical_out: tr_w_v = tr_manager.get_width(vm_layer, 'sig') out_tidx = sig_locs.get('out', self.grid.coord_to_track(vm_layer, pout.middle, mode=RoundMode.NEAREST)) tid = TrackID(vm_layer, out_tidx, width=tr_w_v) self.add_pin('out', self.connect_to_tracks([pout, nout], tid)) # connect supplies xr = self.bound_box.xh if vertical_sup: self.add_pin('VDD', pports.s, connect=True) self.add_pin('VSS', nports.s, connect=True) else: ns_tid = self.get_track_id(ridx_n, MOSWireType.DS_GATE, wire_name='sup') ps_tid = self.get_track_id(ridx_p, MOSWireType.DS_GATE, wire_name='sup') vdd = self.connect_to_tracks(pports.s, ps_tid, track_lower=0, track_upper=xr) vss = self.connect_to_tracks(nports.s, ns_tid, track_lower=0, track_upper=xr) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) self.add_pin('pout', pout, label='out:', hide=vertical_out) self.add_pin('nout', nout, label='out:', hide=vertical_out) # set properties self.sch_params = dict( seg_p=seg_p, seg_n=seg_n, lch=self.place_info.lch, w_n=self.place_info.get_row_place_info(ridx_n).row_info.width if w_n == 0 else w_n, w_p=self.place_info.get_row_place_info(ridx_p).row_info.width if w_p == 0 else w_p, 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=stack_p, stack_n=stack_n, )
def draw_layout_helper(self, differential: bool) -> None: params = self.params pinfo = MOSBasePlaceInfo.make_place_info(self.grid, params['pinfo']) self.draw_base(pinfo) nand_params: Param = params['nand_params'] nor_params: Param = params['nor_params'] core_params: Param = params['core_params'] ridx_p: int = params['ridx_p'] ridx_n: int = params['ridx_n'] is_guarded: bool = params['is_guarded'] swap_tiles: bool = params['swap_tiles'] vertical_out: bool = params['vertical_out'] en_ncol_min: int = params['en_ncol_min'] buf_col_list: Optional[Sequence[int]] = params['buf_col_list'] nd0_tidx = self.get_track_index(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-2) nd1_tidx = self.get_track_index(ridx_n, MOSWireType.DS, wire_name='sig', wire_idx=-1) pd0_tidx = self.get_track_index(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=0) pd1_tidx = self.get_track_index(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=1) ng0_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=-2) ng1_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=-1) append = dict(pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n, is_guarded=is_guarded, vertical_out=False, sig_locs=dict(nout=nd1_tidx, pout=pd0_tidx)) nand_params = nand_params.copy(append=append) nor_params = nor_params.copy(append=append) core_append = dict( pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n, is_guarded=is_guarded, vertical_out=vertical_out, swap_tiles=swap_tiles, sig_locs=dict(nout0=nd0_tidx, nout1=nd1_tidx, pout0=pd1_tidx, pout1=pd0_tidx, nin0=ng1_tidx, nin1=ng0_tidx), ) if differential: core_cls = InvChainCore core_append['sep_stages'] = True core_append['buf_col_list'] = buf_col_list else: core_cls = SingleToDiff core_append['swap_tiles'] = swap_tiles core_params = core_params.copy(append=core_append) nand_master = self.new_template(NAND2Core, params=nand_params) nor_master = self.new_template(NOR2Core, params=nor_params) core_master = self.new_template(core_cls, params=core_params) self._core = core_master # placement nand_ncols = nand_master.num_cols nor_ncols = nor_master.num_cols self._en_ncol = max(nand_ncols, nor_ncols, en_ncol_min) sep = self.min_sep_col tile_outp = int(swap_tiles) tile_outn = 1 - tile_outp # NOTE: right-align NAND/NOR to reduce wire resistance nand = self.add_tile(nand_master, tile_outp, self._en_ncol - nand_ncols) nor = self.add_tile(nor_master, tile_outn, self._en_ncol - nor_ncols) core_ports = self.draw_buffers(core_master, tile_outp, tile_outn, self._en_ncol + sep) self.set_mos_size() # routing # vdd/vss vdd_list = core_ports['VDD'] vss_list = core_ports['VSS'] for inst in [nand, nor]: vdd_list.extend(inst.port_pins_iter('VDD')) vss_list.extend(inst.port_pins_iter('VSS')) vdd = self.connect_wires(vdd_list)[0] self.add_pin('VDD', vdd) self.add_pin('VSS', self.connect_wires(vss_list)) # connect NAND/NOR ports nor_list = [nor.get_pin('pout')] nand_list = [nand.get_pin('nout')] if nor.has_port('nout'): nor_list.append(nor.get_pin('nout')) nand_list.append(nand.get_pin('pout')) grid = self.grid tr_manager = self.tr_manager vm_layer = self.conn_layer + 2 vm_w = tr_manager.get_width(vm_layer, 'sig') in0_nand = nand.get_pin('nin<0>') in0_nor = nor.get_pin('nin<0>') in1_nand = nand.get_pin('nin<1>') in1_nor = nor.get_pin('nin<1>') in1_xl = max(in1_nand.lower, in1_nor.lower) vm_en_tidx = grid.coord_to_track(vm_layer, in1_xl, mode=RoundMode.GREATER_EQ) vm_en_tid = TrackID(vm_layer, vm_en_tidx, width=vm_w) in1_nand = self.connect_to_tracks(in1_nand, vm_en_tid, min_len_mode=MinLenMode.UPPER) in1_nor = self.connect_to_tracks(in1_nor, vm_en_tid, min_len_mode=MinLenMode.LOWER) self.add_pin('en', in1_nand) self.add_pin('enb', in1_nor) # TODO: hack: add extra spacing to avoid corner spacing vm_in_tid = TrackID(vm_layer, tr_manager.get_next_track(vm_layer, vm_en_tidx, 'sig', 'sig', up=-2), width=vm_w) if differential: core_sch_params = core_master.sch_params.copy( remove=['dual_output']) self.add_pin( 'inp', self.connect_to_tracks(in0_nand, vm_in_tid, min_len_mode=MinLenMode.UPPER)) self.add_pin( 'inn', self.connect_to_tracks(in0_nor, vm_in_tid, min_len_mode=MinLenMode.LOWER)) core_inp = core_ports['inp'][0] core_inn = core_ports['inn'][0] if core_inp.layer_id == vm_layer: self.connect_to_track_wires(nor_list, core_inn) self.connect_to_track_wires(nand_list, core_inp) else: # core_inp and core_inn are on hm_layer tidx_r = grid.coord_to_track(vm_layer, min(core_inp.middle, core_inn.middle), mode=RoundMode.LESS_EQ) tidx = tr_manager.get_next_track(vm_layer, tidx_r, 'sig', 'sig', up=False) vm_tid = TrackID(vm_layer, tidx, width=vm_w) nor_list.append(core_inn) nand_list.append(core_inp) self.connect_to_tracks(nor_list, vm_tid) self.connect_to_tracks(nand_list, vm_tid) else: core_sch_params = core_master.sch_params self.add_pin( 'in', self.connect_to_tracks(in0_nand, vm_in_tid, min_len_mode=MinLenMode.UPPER)) self.connect_to_tracks([in0_nor, vdd], vm_in_tid) core_in = core_ports['in'][0] if len(nor_list) > 1: # NAND and NOR outputs are disconnected tidx = grid.coord_to_track(vm_layer, nor_list[0].middle, mode=RoundMode.LESS_EQ) # avoid shorts tidx = max( tr_manager.get_next_track(vm_layer, vm_en_tidx, 'sig', 'sig', up=True), tidx) self.connect_to_tracks(nor_list, TrackID(vm_layer, tidx)) self.connect_to_track_wires(nand_list, core_in) # because of the input nand there is an inversion of polarity outp = core_ports['outp'] outn = core_ports['outn'] connect = len(outp) > 1 self.add_pin('outp', outn, connect=connect) self.add_pin('outn', outp, connect=connect) self.sch_params = dict( core_params=core_sch_params, nand_params=nand_master.sch_params, nor_params=nor_master.sch_params, )
def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) se_params: Param = self.params['se_params'] flop_params: Param = self.params['flop_params'] inv_params: Param = self.params['inv_params'] vm_pitch: HalfInt = HalfInt.convert(self.params['vm_pitch']) # create masters flop_pinfo = self.get_draw_base_sub_pattern(2, 4) flop_params = flop_params.copy( append=dict(pinfo=flop_pinfo, out_pitch=vm_pitch)) se_params = se_params.copy(append=dict(pinfo=self.get_tile_pinfo(0), vertical_out=False, vertical_in=False)) inv_params = inv_params.copy( append=dict(pinfo=self.get_tile_pinfo(2), ridx_n=0, ridx_p=-1)) flop_master = self.new_template(FlopStrongArm, params=flop_params) se_master = self.new_template(SingleToDiff, params=se_params) inv_master = self.new_template(InvCore, params=inv_params) # floorplanning tr_manager = self.tr_manager hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 vm_w = tr_manager.get_width(vm_layer, 'sig') vm_w_hs = tr_manager.get_width(vm_layer, 'sig_hs') # get flop column quantization sd_pitch = self.sd_pitch vm_coord_pitch = int(vm_pitch * self.grid.get_track_pitch(vm_layer)) sep_half = max(-(-self.min_sep_col // 2), -(-vm_coord_pitch // sd_pitch)) blk_ncol = lcm([sd_pitch, vm_coord_pitch]) // sd_pitch sep_ncol = self.min_sep_col flop_ncol2 = flop_master.num_cols // 2 se_col = sep_half center_col = sep_half + inv_master.num_cols + sep_ncol + flop_ncol2 center_col = -(-center_col // blk_ncol) * blk_ncol cur_col = center_col - flop_ncol2 if (cur_col & 1) != (se_col & 1): se_col += 1 se = self.add_tile(se_master, 0, se_col) inv = self.add_tile(inv_master, 2, cur_col - sep_ncol, flip_lr=True) flop = self.add_tile(flop_master, 2, cur_col) cur_col += flop_master.num_cols + self.sub_sep_col // 2 lay_range = range(self.conn_layer, xm_layer + 1) vdd_table: Dict[int, List[WireArray]] = {lay: [] for lay in lay_range} vss_table: Dict[int, List[WireArray]] = {lay: [] for lay in lay_range} sup_info = self.get_supply_column_info(xm_layer) for tile_idx in range(self.num_tile_rows): self.add_supply_column(sup_info, cur_col, vdd_table, vss_table, ridx_p=-1, ridx_n=0, tile_idx=tile_idx, flip_lr=False) self.set_mos_size() # connections # supplies for lay in range(hm_layer, xm_layer + 1, 2): vss = vss_table[lay] vdd = vdd_table[lay] if lay == hm_layer: for inst in [inv, flop, se]: vdd.extend(inst.get_all_port_pins('VDD')) vss.extend(inst.get_all_port_pins('VSS')) vdd = self.connect_wires(vdd) vss = self.connect_wires(vss) self.add_pin(f'VDD_{lay}', vdd, hide=True) self.add_pin(f'VSS_{lay}', vss, hide=True) inp = flop.get_pin('inp') inn = flop.get_pin('inn') loc_list = tr_manager.place_wires(vm_layer, ['sig_hs'] * 2, center_coord=inp.middle)[1] inp, inn = self.connect_differential_tracks(inp, inn, vm_layer, loc_list[0], loc_list[1], width=vm_w_hs) self.add_pin('sa_inp', inp) self.add_pin('sa_inn', inn) in_vm_ref = self.grid.coord_to_track(vm_layer, 0) in_vm_tidx = tr_manager.get_next_track(vm_layer, in_vm_ref, 'sig', 'sig', up=True) vm_phtr = vm_pitch.dbl_value in_vm_dhtr = -(-(in_vm_tidx - in_vm_ref).dbl_value // vm_phtr) * vm_phtr in_vm_tidx = in_vm_ref + HalfInt(in_vm_dhtr) in_warr = self.connect_to_tracks(se.get_all_port_pins('in'), TrackID(vm_layer, in_vm_tidx, width=vm_w), track_lower=0) self.add_pin('in', in_warr) out = flop.get_pin('outp') self.add_pin('out', self.extend_wires(out, upper=self.bound_box.yh)) self.reexport(flop.get_port('clkl'), net_name='clk', hide=False) self.reexport(flop.get_port('rstlb')) self.reexport(se.get_port('outp'), net_name='midp') self.reexport(se.get_port('outn'), net_name='midn') self.reexport(inv.get_port('in'), net_name='dum') self.sch_params = dict( se_params=se_master.sch_params, flop_params=flop_master.sch_params, inv_params=inv_master.sch_params, )
def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) sa_params: Param = self.params['sa_params'] sr_params: Param = self.params['sr_params'] has_rstlb: bool = self.params['has_rstlb'] swap_outbuf: bool = self.params['swap_outbuf'] out_pitch: HalfInt = HalfInt.convert(self.params['out_pitch']) # create masters sr_pinfo = self.get_tile_pinfo(1) sr_params = sr_params.copy(append=dict(pinfo=sr_pinfo, has_rstb=has_rstlb, has_inbuf=True, swap_outbuf=swap_outbuf, out_pitch=out_pitch)) sr_master: SRLatchSymmetric = self.new_template(SRLatchSymmetric, params=sr_params) even_center = sr_master.num_cols % 4 == 0 sa_pinfo = self.get_tile_pinfo(0) dig_style = (sa_pinfo == sr_pinfo) if dig_style: # digital style sa_params = sa_params.copy(append=dict( pinfo=sa_pinfo, has_rstb=has_rstlb, even_center=even_center)) sa_master: MOSBase = self.new_template(SAFrontendDigital, params=sa_params) else: # analog style sa_params = sa_params.copy(append=dict(pinfo=sa_pinfo, has_rstb=has_rstlb, even_center=even_center, vertical_out=False, vertical_rstb=False)) sa_master: MOSBase = self.new_template(SAFrontend, params=sa_params) # placement sa_ncol = sa_master.num_cols sr_ncol = sr_master.num_cols ncol = max(sa_ncol, sr_ncol) sa = self.add_tile(sa_master, 0, (ncol - sa_ncol) // 2) # NOTE: flip SR latch so outputs and inputs lines up nicely sr = self.add_tile(sr_master, 1, (ncol - sr_ncol) // 2 + sr_ncol, flip_lr=True) self.set_mos_size() # supplies self.add_pin('VSS', [sr.get_pin('VSS'), sa.get_pin('VSS')], connect=True) vdd = self.connect_wires([sr.get_pin('VDD'), sa.get_pin('VDD')])[0] self.add_pin('VDD', vdd) # connect outputs rb = sr.get_pin('rb') sb = sr.get_pin('sb') outp = sa.get_all_port_pins('outp') outn = sa.get_all_port_pins('outn') self.connect_to_track_wires(outp, rb) self.connect_to_track_wires(outn, sb) # connect reset signals if has_rstlb: rstlb = sr.get_pin('rstlb') rsthb = sr.get_pin('rsthb') if dig_style: sa_rstb = sa.get_pin('rstb') rstlbl = self.connect_to_track_wires(sa_rstb, rstlb) rsthb = self.connect_to_tracks(vdd, rsthb.track_id, track_lower=rstlbl.lower, track_upper=rsthb.upper) self.add_pin('rstlb', sa_rstb) self.add_pin('rsthb', rsthb, hide=True) else: sa_rstbl = [sa.get_pin('prstbl'), sa.get_pin('nrstb')] sa_rstbr = [sa.get_pin('prstbr'), sa_rstbl[1]] rstlbl = self.connect_to_track_wires(sa_rstbl, rstlb) rstlbr = self.connect_to_tracks(sa_rstbr, rsthb.track_id) self.connect_to_tracks(rsthb, vdd.track_id) self.add_pin('rstlb', sa_rstbl[1]) self.add_pin('rstlb_vm_r', rstlbr, hide=True) self.add_pin('rstlb_vm_l', rstlbl, hide=True) # reexport pins for name in ['inp', 'inn', 'clk', 'clkl', 'clkr']: self.reexport(sa.get_port(name)) self.reexport(sr.get_port('q'), net_name='outp') self.reexport(sr.get_port('qb'), net_name='outn') self.sch_params = dict( sa_params=sa_master.sch_params.copy(remove=['has_rstb']), sr_params=sr_master.sch_params.copy(remove=['has_rstb']), has_rstlb=has_rstlb)
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo, flip_tile=True) mux_params: Param = self.params['mux_params'] flop_params: Param = self.params['flop_params'] inv_params: Param = self.params['inv_params'] vm_pitch: HalfInt = HalfInt.convert(self.params['vm_pitch']) vm_pitch_htr = vm_pitch.dbl_value # create masters mux_params = mux_params.copy( append=dict(pinfo=pinfo, vertical_sel=False)) flop_params = flop_params.copy(append=dict( pinfo=pinfo, has_rstlb=True, swap_outbuf=True, out_pitch=vm_pitch)) mux_master: Mux2to1Matched = self.new_template(Mux2to1Matched, params=mux_params) flop_master: FlopStrongArm = self.new_template(FlopStrongArm, params=flop_params) inv_in_tidx = mux_master.get_port( 'in<0>').get_pins()[0].track_id.base_index inv_params = inv_params.copy( append=dict(pinfo=pinfo, sig_locs={'in': inv_in_tidx})) inv_master: InvCore = self.new_template(InvCore, params=inv_params) # placement min_sep = self.min_sep_col min_sep += (min_sep & 1) buf_ncol = inv_master.num_cols mux_ncol = mux_master.num_cols flop_ncol = flop_master.num_cols ncol_tot = max(flop_ncol, mux_ncol + 2 * min_sep + 2 * buf_ncol) mux_off = (ncol_tot - mux_ncol) // 2 flop_off = (ncol_tot - flop_ncol) // 2 mux_in = self.add_tile(mux_master, 0, mux_off) flop = self.add_tile(flop_master, 1, flop_off) mux_out = self.add_tile(mux_master, 3, mux_off) invl = self.add_tile(inv_master, 3, 0) invr = self.add_tile(inv_master, 3, ncol_tot, flip_lr=True) self.set_mos_size() yh = self.bound_box.yh xm = self.bound_box.xm # routing vm_layer = self.conn_layer + 2 grid = self.grid tr_manager = self.tr_manager vm_w = tr_manager.get_width(vm_layer, 'sig') vm_w_ck = tr_manager.get_width(vm_layer, 'clk') # supplies vdd_list = [] vss_list = [] for inst in [mux_in, flop, mux_out, invl, invr]: vdd_list.extend(inst.port_pins_iter('VDD')) vss_list.extend(inst.port_pins_iter('VSS')) self.add_pin('VDD', self.connect_wires(vdd_list), connect=True) self.add_pin('VSS', self.connect_wires(vss_list), connect=True) # input mux flop_clk = mux_in.get_pin('out') self.connect_to_track_wires(flop_clk, flop.get_pin('clk')) in0 = mux_in.get_pin('in<0>') in1 = mux_in.get_pin('in<1>') clk_vm_tidx = flop_clk.track_id.base_index in_vm_test = tr_manager.get_next_track(vm_layer, clk_vm_tidx, 'sig', 'clk', up=True) in_dhtr = (-(in_vm_test - clk_vm_tidx).dbl_value // vm_pitch_htr) * vm_pitch_htr in_delta = HalfInt(in_dhtr) in0 = self.connect_to_tracks(in0, TrackID(vm_layer, clk_vm_tidx + in_delta, width=vm_w_ck), min_len_mode=MinLenMode.LOWER) in1 = self.connect_to_tracks(in1, TrackID(vm_layer, clk_vm_tidx - in_delta, width=vm_w_ck), min_len_mode=MinLenMode.LOWER) self.add_pin('launch', in0) self.add_pin('measure', in1) # divider flop clkp = flop.get_pin('outp') clkn = flop.get_pin('outn') self.connect_differential_wires(mux_in.get_pin('nsel'), mux_in.get_pin('nselb'), clkp, clkn) self.connect_to_track_wires(clkp, mux_in.get_pin('psel')) self.connect_to_track_wires(clkn, mux_in.get_pin('pselb')) self.connect_to_track_wires(flop.get_pin('inn'), clkp) self.connect_to_track_wires(flop.get_pin('inp'), clkn) self.connect_to_tracks(mux_out.get_pin('in<0>'), clkp.track_id, track_upper=yh, track_lower=clkp.lower) self.reexport(flop.get_port('rstlb')) self.reexport(flop.get_port('rstlb_vm_l')) self.reexport(flop.get_port('rsthb')) # output mux clkn_tid = clkn.track_id vm_sp_le = grid.get_line_end_space(vm_layer, clkn_tid.width) clk_dcd = self.connect_to_tracks(mux_out.get_pin('in<1>'), clkn_tid, track_lower=clkn.upper + vm_sp_le, track_upper=yh) clk_out = self.extend_wires(mux_out.get_pin('out'), upper=yh) self.add_pin('clk_dcd', clk_dcd) self.add_pin('clk_out', clk_out) # output mux select signals out_sel = invl.get_pin('out') out_selb = invr.get_pin('out') dcc_byp_delta = tr_manager.get_next_track( vm_layer, out_selb.track_id.base_index, 'sig', 'sig', up=True) - clk_vm_tidx dcc_byp_dhtr = -(-dcc_byp_delta.dbl_value // vm_pitch_htr) * vm_pitch_htr dcc_byp_delta = HalfInt(dcc_byp_dhtr) dcc_byp_tidx = clk_vm_tidx + dcc_byp_delta out_selb_vm_tidx = clk_vm_tidx - dcc_byp_delta self.connect_to_track_wires(mux_out.get_pin('psel'), out_sel) self.connect_to_track_wires(mux_out.get_pin('pselb'), out_selb) nsel, nselb = self.connect_differential_wires(out_sel, out_selb, mux_out.get_pin('nsel'), mux_out.get_pin('nselb')) sel_hm_list = [] selb_vm = self.connect_to_tracks([nselb, invl.get_pin('in')], TrackID(vm_layer, out_selb_vm_tidx, width=vm_w), track_upper=yh, ret_wire_list=sel_hm_list) # get differential matching xl = min((wire.lower for wire in sel_hm_list)) xh = 2 * xm - xl self.extend_wires([nsel, nselb], lower=xl, upper=xh) dcc_byp = self.connect_to_tracks(invr.get_pin('in'), TrackID(vm_layer, dcc_byp_tidx, width=vm_w), track_lower=selb_vm.lower, track_upper=yh) self.add_pin('dcc_byp', dcc_byp) self.sch_params = dict( mux_params=mux_master.sch_params, flop_params=flop_master.sch_params, inv_params=inv_master.sch_params, )
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) pi_params: Param = self.params['pi_params'] dc_params: Param = self.params['dc_params'] inv_params: Param = self.params['inv_params'] nand_params: Param = self.params['nand_params'] ncores: int = self.params['num_core'] nbits: int = self.params['nbits'] split_nand: bool = self.params['split_nand'] split_inv: bool = self.params['split_inv'] pi_tile: int = self.params['pi_tile'] dc_tile: int = self.params['dc_tile'] inv_tile_l = dc_tile + 1 inv_tile_h = dc_tile + 3 hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 grid = self.grid tr_manager = self.tr_manager # Recompute sizes for the inverter and nand inv_seg: int = inv_params['seg'] nand_seg: int = nand_params['seg'] split_inv = split_inv and (inv_seg > 1) split_nand = split_nand and (nand_seg > 1) if split_inv: inv_seg_2 = inv_seg // 2 inv_seg_1 = inv_seg - inv_seg_2 else: inv_seg_1 = inv_seg inv_seg_2 = None if split_nand: nand_seg_2 = nand_seg // 2 nand_seg_1 = nand_seg - nand_seg_2 else: nand_seg_1 = nand_seg nand_seg_2 = None # create masters low_pinfo = self.get_tile_pinfo(inv_tile_l) high_pinfo = self.get_tile_pinfo(inv_tile_h) pi_params = pi_params.copy( append=dict(pinfo=pinfo, nbits=nbits + 1, flip_b_en=True)) dc_params = dc_params.copy(append=dict(pinfo=pinfo, substrate_row=True, vertical_out=True, draw_substrate_row=False, tile0=3, tile1=1, flip_vm=True)) pi_master: PhaseInterpolator = self.new_template(PhaseInterpolator, params=pi_params) dc_master: DelayCellCore = self.new_template(DelayCellCore, params=dc_params) dc0_params = dc_params.copy(append=dict(feedback=True)) dc0_master: DelayCellCore = self.new_template(DelayCellCore, params=dc0_params) # Place all the cells ntr, vert_trs = tr_manager.place_wires(vm_layer, ['sig', 'sig', 'sup']) ncol = self.arr_info.get_column_span(vm_layer, ntr) pi = self.add_tile(pi_master, pi_tile, ncol) dc_arr = [self.add_tile(dc0_master, dc_tile, 0)] sep = max(self.min_sep_col, self.get_hm_sp_le_sep_col()) curr_col = dc_master.num_cols + sep for _ in range(ncores - 1): dc_arr.append(self.add_tile(dc_master, dc_tile, curr_col)) curr_col += dc_master.num_cols + sep nand_params_l = nand_params.copy(append=dict(pinfo=low_pinfo, vertical_sup=True, vertical_out=False, seg=nand_seg_1)) nand_master_l: NAND2Core = self.new_template(NAND2Core, params=nand_params_l) nand_l = self.add_tile(nand_master_l, inv_tile_l, curr_col) if split_nand: nand_params_h = nand_params.copy(append=dict(pinfo=high_pinfo, vertical_sup=True, vertical_out=False, seg=nand_seg_2)) nand_master_h: NAND2Core = self.new_template(NAND2Core, params=nand_params_h) inv_in_tid_h = nand_master_h.get_track_index(1, MOSWireType.G, wire_name='sig', wire_idx=0) nand_h = self.add_tile(nand_master_h, inv_tile_h, curr_col) nand_sch_params = [ nand_master_l.sch_params, nand_master_h.sch_params ] else: inv_in_tid_h = None nand_h = None nand_sch_params = [nand_master_l.sch_params] inv_col = curr_col + nand_master_l.num_cols + sep inv_in_tid = nand_master_l.get_track_index(1, MOSWireType.G, wire_name='sig', wire_idx=0) inv_params_l = inv_params.copy(append=dict(pinfo=low_pinfo, vertical_sup=True, seg=inv_seg_1, vertical_out=False, sig_locs=dict( nin=inv_in_tid))) inv_master_l: InvCore = self.new_template(InvCore, params=inv_params_l) inv_l = self.add_tile(inv_master_l, inv_tile_l, inv_col) if split_inv: if inv_in_tid_h: inv_params_h = inv_params.copy( append=dict(pinfo=high_pinfo, vertical_sup=True, seg=inv_seg_2, vertical_out=False, sig_locs=dict(nin=inv_in_tid_h))) else: inv_params_h = inv_params.copy(append=dict(pinfo=high_pinfo, vertical_sup=True, seg=inv_seg_2, vertical_out=False)) inv_master_h: InvCore = self.new_template(InvCore, params=inv_params_h) inv_h = self.add_tile(inv_master_h, inv_tile_h, inv_col) inv_sch_params = [inv_master_l.sch_params, inv_master_h.sch_params] else: inv_h = None inv_sch_params = [inv_master_l.sch_params] # substrate taps vss_hm = [ pi.get_pin('VSS0'), pi.get_pin('VSS1'), self.get_track_id(0, MOSWireType.DS, 'sup', tile_idx=8) ] vdd_hm = [ pi.get_pin('VDD_hm'), self.get_track_id(0, MOSWireType.DS, 'sup', tile_idx=6) ] ncol_tot = self.num_cols sub = self.add_substrate_contact(0, 0, tile_idx=0, seg=ncol_tot) self.connect_to_track_wires(sub, vss_hm[0]) sub = self.add_substrate_contact(0, 0, tile_idx=2, seg=ncol_tot) self.connect_to_track_wires(sub, vdd_hm[0]) sub = self.add_substrate_contact(0, 0, tile_idx=4, seg=ncol_tot) self.connect_to_track_wires(sub, vss_hm[1]) sub = self.add_substrate_contact(0, 0, tile_idx=6, seg=ncol_tot) sub_vdd1 = self.connect_to_tracks(sub, vdd_hm[1]) vdd_hm[1] = sub_vdd1 sub = self.add_substrate_contact(0, 0, tile_idx=8, seg=ncol_tot) sub_vss2 = self.connect_to_tracks(sub, vss_hm[2]) vss_hm[2] = sub_vss2 self.set_mos_size() xh = self.bound_box.xh # routing xm_w = tr_manager.get_width(xm_layer, 'sig') xm_w_sup = tr_manager.get_width(xm_layer, 'sup') xm_w_hs = tr_manager.get_width(xm_layer, 'sig_hs') vm_w = tr_manager.get_width(vm_layer, 'sig') vm_w_sup = tr_manager.get_width(vm_layer, 'sup') vm_sep_sup = tr_manager.get_sep(vm_layer, ('sup', 'sup')) xm_vss_tids, xm_vdd_tids = [], [] for tid_arr, sup_hm_arr in [(xm_vss_tids, vss_hm), (xm_vdd_tids, vdd_hm)]: for sup_w in sup_hm_arr: y = grid.track_to_coord(hm_layer, sup_w.track_id.base_index) tid_arr.append( TrackID(xm_layer, grid.coord_to_track(xm_layer, y, mode=RoundMode.NEAREST), width=xm_w_sup)) tr_list = ['sup'] + ['sig'] * (nbits + 1) pidx_list_l = tr_manager.place_wires( xm_layer, tr_list, align_track=xm_vss_tids[0].base_index, align_idx=0)[1] pidx_list_h = tr_manager.place_wires( xm_layer, tr_list, align_track=xm_vss_tids[1].base_index, align_idx=0)[1] tr_list.reverse() nidx_list_l = tr_manager.place_wires( xm_layer, tr_list, align_track=xm_vss_tids[1].base_index, align_idx=-1)[1] nidx_list_h = tr_manager.place_wires( xm_layer, tr_list, align_track=xm_vss_tids[2].base_index, align_idx=-1)[1] io_idx_list = tr_manager.place_wires( xm_layer, ['sig_hs', 'sup', 'sig_hs'], align_track=xm_vdd_tids[0].base_index, align_idx=1)[1] int_idx_list = tr_manager.place_wires( xm_layer, ['sig_hs', 'sup', 'sig_hs'], align_track=xm_vdd_tids[1].base_index, align_idx=1)[1] # PI signals vss_list = pi.get_all_port_pins('VSS') vdd_list = pi.get_all_port_pins('VDD') suf = f'<{nbits}>' en_vss = self.connect_to_track_wires(vss_hm[0:2], pi.get_pin('a_enb' + suf)) en_l = en_vss.lower en_u = en_vss.upper self.connect_to_tracks(vdd_hm[0], pi.get_pin('a_en' + suf).track_id, track_lower=en_l, track_upper=en_u) en_xm_upper = None for idx in range(nbits): suf = f'<{idx}>' enb = self.connect_wires( [pi.get_pin('a_en' + suf), pi.get_pin('b_enb' + suf)], lower=en_l, upper=en_u) en = self.connect_wires( [pi.get_pin('a_enb' + suf), pi.get_pin('b_en' + suf)], lower=en_l, upper=en_u) ptid = TrackID(xm_layer, pidx_list_l[1 + idx], width=xm_w) ntid = TrackID(xm_layer, nidx_list_l[-idx - 2], width=xm_w) if idx == 0: en = self.connect_to_tracks(en, ptid, track_lower=0) en_xm_upper = en.upper else: en = self.connect_to_tracks(en, ptid, track_lower=0, track_upper=en_xm_upper) enb = self.connect_to_tracks(enb, ntid, track_lower=0, track_upper=en_xm_upper) self.add_pin('sp' + suf, en) self.add_pin('sn' + suf, enb) intout = self.connect_to_tracks(pi.get_pin('out'), TrackID(xm_layer, io_idx_list[2], width=xm_w_hs), track_lower=0) self.add_pin('intout', intout) vdd0 = self.connect_to_tracks(vdd_list, xm_vdd_tids[0], track_lower=0, track_upper=xh) vss0 = self.connect_to_tracks(vss_list, xm_vss_tids[0], track_lower=0, track_upper=xh) vss1 = self.connect_to_tracks(vss_list, xm_vss_tids[1], track_lower=0, track_upper=xh) # delay cell signals vdd_list, vss0_list, vss1_list = [], [], [] for dc in dc_arr: vdd_list += dc.get_all_port_pins('VDD') vss0_list += dc.get_all_port_pins('VSS1') vss1_list += dc.get_all_port_pins('VSS0') vdd_list.extend(nand_l.get_all_port_pins('VDD')) vss0_list.extend(nand_l.get_all_port_pins('VSS')) if split_nand: vdd_list.extend(nand_h.get_all_port_pins('VDD')) vss1_list.extend(nand_h.get_all_port_pins('VSS')) vss0_list.extend(inv_l.get_all_port_pins('VSS')) vdd_list.extend(inv_l.get_all_port_pins('VDD')) if split_inv: vdd_list.extend(inv_h.get_all_port_pins('VDD')) vss1_list.extend(inv_h.get_all_port_pins('VSS')) vdd_list.extend([dc.get_pin('bk1_vm') for dc in dc_arr]) self.connect_to_track_wires(vdd_list, vdd_hm[1]) self.connect_to_track_wires(vss0_list, vss_hm[1]) self.connect_to_track_wires(vss1_list, vss_hm[2]) if ncores > 1: for idx in range(ncores - 1, 0, -1): cur_cop = dc_arr[idx].get_pin('co_p') next_in = dc_arr[idx - 1].get_pin('in_p') ptid = TrackID(xm_layer, pidx_list_h[1 + (idx % 2)], width=xm_w) self.connect_to_tracks([cur_cop, next_in], ptid) cur_ci = dc_arr[idx].get_pin('ci_p') next_out = dc_arr[idx - 1].get_pin('out_p') ntid = TrackID(xm_layer, nidx_list_h[-2 - (idx % 2)], width=xm_w) self.connect_to_tracks([cur_ci, next_out], ntid) # NAND to delay cell and PI nand_out_vm_tr = self.grid.coord_to_track( vm_layer, nand_l.get_pin('pout').middle, RoundMode.NEAREST) nand_out_pins = [nand_l.get_pin('pout'), nand_l.get_pin('nout')] if split_nand: nand_out_pins.extend( [nand_h.get_pin('pout'), nand_h.get_pin('nout')]) nand_out_vm_w = self.connect_to_tracks( nand_out_pins, TrackID(vm_layer, nand_out_vm_tr, vm_w)) a_in_buf_xm = self.connect_to_tracks( [nand_out_vm_w, dc_arr[-1].get_pin('in_p')], TrackID(xm_layer, int_idx_list[0], xm_w)) a_in_buf_vm = self.connect_to_tracks( [a_in_buf_xm, pi.get_pin('a_in')], TrackID(vm_layer, vert_trs[0], vm_w)) # Delay Cell to PI b_in_xm = self.connect_to_tracks( dc_arr[-1].get_pin("out_p"), TrackID(xm_layer, int_idx_list[-1], xm_w)) b_in_vm = self.connect_to_tracks([b_in_xm, pi.get_pin("b_in")], TrackID(vm_layer, vert_trs[1], vm_w)) nand_in_hm = [nand_l.get_pin("nin<0>"), vdd_hm[1]] if split_nand: nand_in_hm.append(nand_h.get_pin("nin<0>")) nand_in_vm_tr = tr_manager.get_next_track_obj(nand_out_vm_w.track_id, 'sig', 'sig') self.connect_to_tracks(nand_in_hm, nand_in_vm_tr) # Inv out to nand in inv_out_tr = grid.coord_to_track(vm_layer, inv_l.get_pin("pout").middle, RoundMode.NEAREST) inv_out_hm_w = [ inv_l.get_pin("pout"), inv_l.get_pin("nout"), nand_l.get_pin("nin<1>") ] inv_in_tr = tr_manager.get_next_track(vm_layer, inv_out_tr, 'sig', 'sig') inv_in_hm_w = [inv_l.get_pin('in')] if split_inv: inv_out_hm_w.extend([inv_h.get_pin("nout"), inv_h.get_pin("pout")]) inv_in_hm_w.append(inv_h.get_pin("in")) if split_nand: inv_out_hm_w.append(nand_h.get_pin("nin<1>")) inv_out_w = self.connect_to_tracks(inv_out_hm_w, TrackID(vm_layer, inv_out_tr, vm_w)) a_in = self.connect_to_tracks(inv_in_hm_w, TrackID(vm_layer, inv_in_tr, vm_w)) self.add_pin('a_in', a_in) # Supply routing over nand, dc, and inv vm_wires = [nand_out_vm_w, inv_out_w] for dc in dc_arr: for port_name in dc.port_names_iter(): if port_name not in ['VDD', 'VSS0', 'VSS1']: vm_wires.extend(dc.get_all_port_pins(port_name, vm_layer)) # for idx, wire in enumerate(vm_wires): # self.add_pin(f'vm_wire_{idx}', wire) vss_vm_arr = pi.get_all_port_pins('VSS')[0] vdd_vm_arr = pi.get_all_port_pins('VDD')[0] vss_vm, vdd_vm = [], [] for wire_arr, target_wire, vm_list in [(vss_vm_arr, vss_hm[2], vss_vm), (vdd_vm_arr, vdd_hm[1], vdd_vm) ]: for wire in wire_arr.warr_iter(): tr_idx = wire.track_id.base_index min_idx = tr_manager.get_next_track(vm_layer, tr_idx, 'sup', 'sig', up=False) max_idx = tr_manager.get_next_track(vm_layer, tr_idx, 'sup', 'sig', up=True) will_intersect = False for sig_wire in vm_wires: if min_idx < sig_wire.track_id.base_index <= max_idx: will_intersect = True if not will_intersect: self.connect_to_tracks(wire, target_wire.track_id) vm_list.append(wire) vdd1 = self.connect_to_tracks(vdd_vm, xm_vdd_tids[1], track_lower=0, track_upper=xh) vss2 = self.connect_to_tracks(vss_vm, xm_vss_tids[2], track_lower=0, track_upper=xh) self.add_pin('VDD', [vdd0, vdd1], connect=True) self.add_pin('VSS', [vss0, vss1, vss2], connect=True) tidx_list = self.get_available_tracks(vm_layer, 0, inv_in_tr, 0, self.bound_box.yh, width=vm_w_sup, sep=vm_sep_sup) for tidx in tidx_list: tid = TrackID(vm_layer, tidx, vm_w_sup) for wire in vdd_hm + [vss_hm[1], vdd0, vdd1, vss1]: self.connect_to_tracks(wire, tid, min_len_mode=MinLenMode.MIDDLE) for wire in [vss_hm[0], vss0]: self.connect_to_tracks(wire, tid, min_len_mode=MinLenMode.UPPER) for wire in [vss_hm[2], vss2]: self.connect_to_tracks(wire, tid, min_len_mode=MinLenMode.LOWER) if self.params['export_dc_in']: self.add_pin('a_in_buf', a_in_buf_vm) if self.params['export_dc_out']: self.add_pin('b_in', b_in_vm) # Set the schematic parameters self.sch_params = dict( pi_params=pi_master.sch_params, dc_params=dc_master.sch_params, inv_params=inv_sch_params, nand_params=nand_sch_params, num_core=ncores, export_dc_out=self.params['export_dc_out'], export_dc_in=self.params['export_dc_in'], )
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo) sync_params: Param = self.params['sync_params'] buf_params: Param = self.params['buf_params'] nsync: int = self.params['nsync'] if nsync < 1: raise ValueError('nsync must be positive.') if nsync & 1: raise ValueError('nsync must be even.') nsync2 = nsync // 2 # create masters sync_params = sync_params.copy(append=dict(pinfo=pinfo, has_rstlb=True)) buf_params = buf_params.copy(append=dict(pinfo=pinfo)) sync_master: FlopStrongArm = self.new_template(FlopStrongArm, params=sync_params) buf_master: InvChainCore = self.new_template(InvChainCore, params=buf_params) # placement conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 grid = self.grid tr_manager = self.tr_manager xm_w = tr_manager.get_width(xm_layer, 'sig') xm_w_clk = tr_manager.get_width(xm_layer, 'clk') min_sep = self.min_sep_col min_sep += (min_sep & 1) sub_sep = self.sub_sep_col sub_sep2 = sub_sep // 2 sync_ncol = sync_master.num_cols self._buf_ncol = buf_master.num_cols sup_info = self.get_supply_column_info(xm_layer) tap_ncol = sup_info.ncol tap_off = self._buf_ncol + sub_sep2 sync_off = tap_off + tap_ncol + sub_sep2 + sync_ncol sync_delta = sync_ncol + min_sep ncol_tot = sync_off + (nsync2 - 1) * sync_delta # add buffer and set sized buf = self.add_tile(buf_master, 0, 0) self.set_mos_size(num_cols=ncol_tot, num_tiles=5) # add flops bot_pins = self._draw_sync_row(sync_master, 0, nsync2, sync_off, sync_delta, True) top_pins = self._draw_sync_row(sync_master, 2, nsync2, ncol_tot, -sync_delta, False) # add supply connections lay_range = range(conn_layer, xm_layer + 1) vdd_table: Dict[int, List[WireArray]] = {lay: [] for lay in lay_range} vss_table: Dict[int, List[WireArray]] = {lay: [] for lay in lay_range} for tile_idx in range(self.num_tile_rows): self.add_supply_column(sup_info, tap_off, vdd_table, vss_table, ridx_p=-1, ridx_n=0, tile_idx=tile_idx) # routing # supplies vdd_hm_list = bot_pins['VDD'] vdd_hm_list.extend(top_pins['VDD']) vss_hm_list = bot_pins['VSS'] vss_hm_list.extend(top_pins['VSS']) vdd_hm_list.extend(buf.port_pins_iter('VDD')) vss_hm_list.extend(buf.port_pins_iter('VSS')) vdd_hm_list = self.connect_wires(vdd_hm_list) vss_hm_list = self.connect_wires(vss_hm_list) for lay in range(hm_layer, xm_layer + 1, 2): vss = vss_table[lay] vdd = vdd_table[lay] if lay == hm_layer: vss.extend(vss_hm_list) vdd.extend(vdd_hm_list) vdd = self.connect_wires(vdd) vss = self.connect_wires(vss) self.add_pin(f'VDD_{lay}', vdd) self.add_pin(f'VSS_{lay}', vss) # datapath self.connect_to_track_wires(bot_pins['outp'], top_pins['inp']) self.connect_to_track_wires(bot_pins['outn'], top_pins['inn']) self.connect_to_track_wires(bot_pins['inp'], vdd_hm_list[0]) self.connect_to_track_wires(bot_pins['inn'], vss_hm_list[0]) self.add_pin('VSS_bot', vss_hm_list[0]) outp = top_pins['outp'] xm_tidx = grid.coord_to_track(xm_layer, outp.middle, mode=RoundMode.NEAREST) out = self.connect_to_tracks(outp, TrackID(xm_layer, xm_tidx, width=xm_w), min_len_mode=MinLenMode.UPPER) self.add_pin('out', out) # clk clk_list = bot_pins['clk'] clk_list.extend(top_pins['clk']) clk = self.connect_wires(clk_list) buf_out = buf.get_pin('out') xm_tidx = grid.coord_to_track(xm_layer, buf_out.middle, mode=RoundMode.NEAREST) buf_out = self.connect_to_tracks(buf_out, TrackID(xm_layer, xm_tidx, width=xm_w_clk), min_len_mode=MinLenMode.UPPER) self.add_pin('clk_buf', buf_out) self.add_pin('clk_sync', clk) self.reexport(buf.get_port('in'), net_name='clk_in') # rstlb rstlb_list = [bot_pins['rstlb'], top_pins['rstlb']] self.add_pin('rstlb', rstlb_list) self.sch_params = dict( sync_params=sync_master.sch_params, buf_params=buf_master.sch_params.copy(remove=['dual_output']), nsync=nsync, )
def draw_layout(self) -> None: pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo, flip_tile=True) grid = self.grid tr_manager = self.tr_manager drv_params: Param = self.params['drv_params'] data_lv_params: Param = self.params['data_lv_params'] ctrl_lv_params: Param = self.params['ctrl_lv_params'] buf_data_lv_params: Param = self.params['buf_data_lv_params'] buf_ctrl_lv_params: Param = self.params['buf_ctrl_lv_params'] ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] rxhalf_ncol: int = self.params['rxhalf_ncol'] conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 # setup master parameters append = dict(pinfo=pinfo, ridx_p=ridx_p, ridx_n=ridx_n) din_params = data_lv_params.copy(append=dict( dual_output=False, **append, )) itx_en_params = ctrl_lv_params.copy(append=dict( dual_output=True, **append, )) ctrl_params = itx_en_params.copy(append=dict(dual_output=False)) ndrv_params = ctrl_params.copy(append=dict(invert_out=True)) drv_params = drv_params.copy(append=append) # create masters lv_din_master = self.new_template(LevelShifterCoreOutBuffer, params=din_params) lv_itx_en_master = self.new_template(LevelShifterCoreOutBuffer, params=itx_en_params) lv_ndrv_master = self.new_template(LevelShifterCoreOutBuffer, params=ndrv_params) lv_ctrl_master = self.new_template(LevelShifterCoreOutBuffer, params=ctrl_params) drv_master: AIBOutputDriver = self.new_template(AIBOutputDriver, params=drv_params) buf_data_lv_master = self._get_buf_lv_master(buf_data_lv_params, append) buf_ctrl_lv_master = self._get_buf_lv_master(buf_ctrl_lv_params, append) # shift output tracks of weak_pullupenb, so it can cross into the other tile. tmp_tid = lv_ctrl_master.get_port('out').get_pins()[0].track_id pub_tidx = tr_manager.get_next_track(tmp_tid.layer_id, tmp_tid.base_index, 'sig', 'sig', up=True) pub_params = ctrl_params.copy(append=dict(sig_locs=dict(outr=pub_tidx))) lv_pub_master = self.new_template(LevelShifterCoreOutBuffer, params=pub_params) # track definitions bot_vss = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=1) top_vss = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=2) in_pins = ['ipdrv_buf<0>', 'itx_en_buf', 'weak_pulldownen', 'weak_pullupenb', 'indrv_buf<0>', 'indrv_buf<1>', 'din', 'ipdrv_buf<1>'] # Placement din_ncol = lv_din_master.num_cols ctrl_ncol = lv_ctrl_master.num_cols min_sep = self.min_sep_col sup_info = self.get_supply_column_info(xm_layer) # assume 2 inverter chains + margins have smaller width than a single lvl shifter if 2 * buf_data_lv_master.num_cols + min_sep > din_ncol: raise ValueError("input buffer too large compared to data level shifter's width") if 2 * buf_ctrl_lv_master.num_cols + min_sep > ctrl_ncol: raise ValueError("input buffer too large compared to control level shifter's width") # initialize supply data structures lay_range = range(conn_layer, xm_layer + 1) vdd_io_table = {lay: [] for lay in lay_range} vdd_core_table = {lay: [] for lay in lay_range} vss_table = {lay: [] for lay in lay_range} # instantiate cells and collect pins pin_dict = {'por': [], 'porb': [], 'VDDIO': [], 'VDD': [], 'VSS': []} cur_col = 0 cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, True) # pdrv<0>, itx_en new_col = draw_io_shifters(self, cur_col, buf_ctrl_lv_master, buf_ctrl_lv_master, lv_ctrl_master, lv_itx_en_master, bot_vss, top_vss, in_pins[0], in_pins[1], True, True, False, pin_dict) ctrl_dual_ncol = new_col - cur_col cur_col = new_col cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, False) core_ncol = get_io_shifters_ncol(self.sub_sep_col, lv_ctrl_master, lv_pub_master) self._core_ncol = max(core_ncol, rxhalf_ncol) # puen, puenb draw_io_shifters(self, cur_col, buf_ctrl_lv_master, buf_ctrl_lv_master, lv_ctrl_master, lv_pub_master, bot_vss, top_vss, in_pins[2], in_pins[3], False, False, False, pin_dict) cur_col += self._core_ncol + min_sep + self.sub_sep_col // 2 # ndrv<0>, ndrv<1> cur_col = draw_io_shifters(self, cur_col + self._core_ncol - core_ncol, buf_ctrl_lv_master, buf_ctrl_lv_master, lv_ndrv_master, lv_ndrv_master, bot_vss, top_vss, in_pins[4], in_pins[5], True, True, False, pin_dict) cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, True) # din, pdrv<1> din_ncol_tot = get_io_shifters_ncol(self.sub_sep_col, lv_din_master, lv_ctrl_master) din_ncol_max = max(din_ncol_tot, ctrl_dual_ncol) self._lvshift_right_ncol = din_ncol_max cur_col = draw_io_shifters(self, cur_col + din_ncol_max - din_ncol_tot, buf_data_lv_master, buf_ctrl_lv_master, lv_din_master, lv_ctrl_master, bot_vss, top_vss, in_pins[6], in_pins[7], True, True, True, pin_dict) drv_inst = self.add_tile(drv_master, 1, cur_col) # add tapes on bottom tile max_col = self._draw_bot_supply_column(cur_col, drv_master.tap_columns, sup_info, vdd_core_table, vss_table, ridx_p, ridx_n) self.set_mos_size(num_cols=max_col) # connect and export supply pins vss_list = vdd_io_list = vdd_core_list = [] for lay in range(hm_layer, xm_layer + 1, 2): vss = vss_table[lay] vdd_io = vdd_io_table[lay] vss.extend(drv_inst.port_pins_iter(f'VSS_{lay}')) vdd_io.extend(drv_inst.port_pins_iter(f'VDD_{lay}')) vss_list = self.connect_wires(vss) vdd_io_list = self.connect_wires(vdd_io) vdd_core_list = self.connect_wires(vdd_core_table[lay], upper=vdd_io[0].upper) self.add_pin('VDDIO', vdd_io_list) self.add_pin('VDDCore', vdd_core_list) self.add_pin('VSS', vss_list) self.add_pin('VDDIO_vm', vdd_io_table[vm_layer], hide=True) self.add_pin('VDDCore_vm', vdd_core_table[vm_layer], hide=True) self.add_pin('VSS_vm', vss_table[vm_layer], hide=True) self.add_pin('VDDIO_conn', vdd_io_table[conn_layer], hide=True) self.add_pin('VDDCore_conn', vdd_core_table[conn_layer], hide=True) self.add_pin('VSS_conn', vss_table[conn_layer], hide=True) self.reexport(drv_inst.get_port('VDD_vm'), net_name='VDDIO_vm') self.reexport(drv_inst.get_port('VSS_vm')) self.reexport(drv_inst.get_port('VDD_conn'), net_name='VDDIO_conn') self.reexport(drv_inst.get_port('VSS_conn')) # route input pins to the edge in0_tidx = tr_manager.get_next_track(xm_layer, vdd_core_list[0].track_id.base_index, 'sup', 'sig', up=True) in_tidx_list = tr_manager.place_wires(xm_layer, ['sig'] * len(in_pins), align_track=in0_tidx)[1] tr_xm_w = tr_manager.get_width(xm_layer, 'sig') vm_w = tr_manager.get_width(vm_layer, 'sig') for buf_idx, (tidx, name) in enumerate(zip(in_tidx_list, in_pins)): hm_warr = pin_dict[name][0] vm_tidx_ref = pin_dict[name + '_buf'][1].track_id.base_index vm_tidx = tr_manager.get_next_track(vm_layer, vm_tidx_ref, 'sig', 'sig', up=((buf_idx & 1) == 0)) vm_warr = self.connect_to_tracks(hm_warr, TrackID(vm_layer, vm_tidx, width=vm_w), min_len_mode=MinLenMode.LOWER) self.add_pin(name, self.connect_to_tracks(vm_warr, TrackID(xm_layer, tidx, width=tr_xm_w), track_lower=0), mode=PinMode.LOWER) # connect POR input # connect POR/POR_b por_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=2, tile_idx=2) porb_tid = self.get_track_id(ridx_p, MOSWireType.DS, 'sig', wire_idx=2, tile_idx=1) por = self.connect_to_tracks(pin_dict['por'], por_tid) porb = self.connect_to_tracks(pin_dict['porb'], porb_tid) self.add_pin('por_vccl', por) self.add_pin('porb_vccl', porb) # connect driver inputs # compute track indices # track order: weak_pden, n_enb_drv<0>, pad, p_en_drv<0>, weak_puenb, din # n_enb_drv<1>, tristateb, pad, tristate, p_en_drv<1> vdd_io_tidx = vdd_io_list[0].track_id.base_index pad_tr_w = tr_manager.get_width(xm_layer, 'padout') xm_hs_w = tr_manager.get_width(xm_layer, 'sig_hs') pad_0_tidx = grid.get_middle_track(vss_list[0].track_id[0].base_index, vdd_io_tidx, round_up=False) pad_1_tidx = grid.get_middle_track(vss_list[0].track_id[1].base_index, vdd_io_tidx, round_up=True) pad_2_tidx = grid.get_middle_track(vss_list[0].track_id[1].base_index, vdd_io_list[1].track_id.base_index, round_up=False) bb_tidx_list = tr_manager.place_wires(xm_layer, ['sig', 'sig', 'padout'], align_track=pad_0_tidx, align_idx=-1)[1] bt_tidx_list = tr_manager.place_wires(xm_layer, ['padout', 'sig', 'sig', 'sig_hs'], align_track=pad_0_tidx)[1] tb_tidx_list = tr_manager.place_wires(xm_layer, ['sig', 'sig', 'padout'], align_track=pad_1_tidx, align_idx=-1)[1] tt_tidx_list = tr_manager.place_wires(xm_layer, ['padout', 'sig', 'sig'], align_track=pad_1_tidx)[1] # connect wires self._connect_lv_drv(pin_dict, drv_inst, 'weak_pulldownen_out', 'weak_pden', TrackID(xm_layer, bb_tidx_list[0], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'indrv_buf<0>_out', 'n_enb_drv<0>', TrackID(xm_layer, bb_tidx_list[1], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'ipdrv_buf<0>_out', 'p_en_drv<0>', TrackID(xm_layer, bt_tidx_list[1], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'weak_pullupenb_out', 'weak_puenb', TrackID(xm_layer, bt_tidx_list[2], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'din_out', 'din', TrackID(xm_layer, bt_tidx_list[3], width=xm_hs_w)) self._connect_lv_drv(pin_dict, drv_inst, 'indrv_buf<1>_out', 'n_enb_drv<1>', TrackID(xm_layer, tb_tidx_list[0], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'itx_en_buf_out', 'tristateb', TrackID(xm_layer, tb_tidx_list[1], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'itx_en_buf_outb', 'tristate', TrackID(xm_layer, tt_tidx_list[1], width=tr_xm_w)) self._connect_lv_drv(pin_dict, drv_inst, 'ipdrv_buf<1>_out', 'p_en_drv<1>', TrackID(xm_layer, tt_tidx_list[2], width=tr_xm_w)) # connect driver outputs pad_pitch = pad_2_tidx - pad_0_tidx pad_tid0 = TrackID(xm_layer, pad_0_tidx, width=pad_tr_w, num=2, pitch=pad_pitch) pad_tid1 = TrackID(xm_layer, pad_1_tidx, width=pad_tr_w, num=2, pitch=pad_pitch) pad_pin = drv_inst.get_pin('txpadout') self.add_pin('txpadout', [self.connect_to_tracks(pad_pin, pad_tid0), self.connect_to_tracks(pad_pin, pad_tid1)]) self.add_pin('txpadout_vm', pad_pin, hide=True) # setup schematic parameters rm_keys = ['dual_output', 'invert_out'] data_lv_sch_params = lv_din_master.sch_params.copy(remove=rm_keys) ctrl_lv_sch_params = lv_ctrl_master.sch_params.copy(remove=rm_keys) self.sch_params = dict( drv_params=drv_master.sch_params, data_lv_params=dict( lev_params=data_lv_sch_params, buf_params=buf_data_lv_master.sch_params.copy(remove=['dual_output']), ), ctrl_lv_params=dict( lev_params=ctrl_lv_sch_params, buf_params=buf_ctrl_lv_master.sch_params.copy(remove=['dual_output']), ), )
def draw_layout(self): params = self.params pinfo = MOSBasePlaceInfo.make_place_info(self.grid, params['pinfo']) self.draw_base(pinfo) seg_dict: Mapping[str, int] = params['seg_dict'] buf_seg_list: List[int] = params['buf_seg_list'] buf_segn_list: List[int] = params['buf_segn_list'] buf_segp_list: List[int] = params['buf_segp_list'] w_dict: Mapping[str, int] = params['w_dict'] ridx_p: int = params['ridx_p'] ridx_n: int = params['ridx_n'] vertical_rst: List[str] = params['vertical_rst'] is_guarded: bool = params['is_guarded'] dual_output: bool = params['dual_output'] invert_out: bool = params['invert_out'] vertical_out: bool = params['vertical_out'] in_upper: bool = params['in_upper'] has_rst: bool = params['has_rst'] stack_p: int = params['stack_p'] sig_locs: Mapping[str, Union[float, HalfInt]] = params['sig_locs'] num_col_tot: int = params['num_col_tot'] export_pins: bool = params['export_pins'] hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 # placement and track computations # create level shifter core if not buf_segn_list or not buf_segp_list: if not buf_seg_list: raise RuntimeError('Not segment list provided') buf_segn_list = buf_seg_list buf_segp_list = buf_seg_list buf_nstage = len(buf_segn_list) buf_invert = (buf_nstage % 2 == 1) invert_in = (buf_invert ^ invert_out) if buf_invert ^ invert_in: self._outr_inverted = True outl_name = 'out' outr_name = 'outb' else: self._outr_inverted = False outl_name = 'outb' outr_name = 'out' default_wp = self.get_row_info(ridx_p).width default_wn = self.get_row_info(ridx_n).width core_params = dict( pinfo=pinfo, seg_dict=seg_dict, w_dict=w_dict, ridx_p=ridx_p, ridx_n=ridx_n, vertical_rst=vertical_rst, is_guarded=is_guarded, in_upper=in_upper, has_rst=has_rst, stack_p=stack_p, inp_on_right=invert_in, sig_locs=sig_locs, ) core_master: LevelShifterCore = self.new_template(LevelShifterCore, params=core_params) self._mid_vertical = core_master.out_vertical # get buffer track indices buf_inl_tidx = core_master.get_port( 'poutl').get_pins()[0].track_id.base_index buf_inr_tidx = core_master.get_port( 'poutr').get_pins()[0].track_id.base_index buf_pout0_tidx = self.get_track_index(ridx_p, MOSWireType.DS, 'sig', wire_idx=1) buf_pout1_tidx = self.get_track_index(ridx_p, MOSWireType.DS, 'sig', wire_idx=0) buf_nout0_tidx = self.get_track_index(ridx_n, MOSWireType.DS, 'sig', wire_idx=-2) buf_nout1_tidx = self.get_track_index(ridx_n, MOSWireType.DS, 'sig', wire_idx=-1) # create buffer master sig_locs_l = dict( pout0=buf_pout0_tidx, pout1=buf_pout1_tidx, nout0=buf_nout0_tidx, nout1=buf_nout1_tidx, ) sig_locs_r = sig_locs_l.copy() if is_guarded: # TODO: this code work with InvChainCore's gate index hack sig_locs_l['pin1'] = sig_locs_r['pin0'] = buf_inl_tidx sig_locs_l['pin0'] = sig_locs_r['pin1'] = buf_inr_tidx else: sig_locs_l['nin0'] = sig_locs_r['nin1'] = buf_inl_tidx sig_locs_l['nin1'] = sig_locs_r['nin0'] = buf_inr_tidx w_invp = w_dict.get('invp', default_wp) w_invn = w_dict.get('invn', default_wn) invr_params = dict( pinfo=pinfo, segn_list=buf_segn_list, segp_list=buf_segp_list, is_guarded=is_guarded, ridx_n=ridx_n, ridx_p=ridx_p, w_n=w_invn, w_p=w_invp, sig_locs=sig_locs_r, vertical_out=vertical_out, ) invr_master = self.new_template(InvChainCore, params=invr_params) sch_buf_params = invr_master.sch_params.copy(remove=['dual_output']) # place instances inv_sep = self.min_sep_col inv_sep += (inv_sep & 1) inv_col = invr_master.num_cols inv_gap = (inv_col & 1) inv_col_even = inv_col + inv_gap core_col = core_master.num_cols vdd_list = [] vss_list = [] if dual_output: invl_params = invr_params.copy() invl_params['sig_locs'] = sig_locs_l invl_master = self.new_template(InvChainCore, params=invl_params) cur_tot = core_col + 2 * inv_col_even num_col_tot = max(num_col_tot, cur_tot + 2 * inv_sep) sep_l = (num_col_tot - cur_tot) // 2 sep_l += (sep_l & 1) sep_r = num_col_tot - cur_tot - sep_l inv_l = self.add_tile(invl_master, 0, inv_col_even, flip_lr=True, commit=False) self._update_buf_inst(inv_l, vm_layer, sig_locs_l, sig_locs, 'l') vdd_list.append(inv_l.get_pin('VDD')) vss_list.append(inv_l.get_pin('VSS')) cur_col = inv_col_even + sep_l core = self.add_tile(core_master, 0, cur_col) self._center_col = core_master.center_col + cur_col cur_col += core_col + sep_r self.connect_wires([core.get_pin('poutl'), inv_l.get_pin('pin')]) self._export_buf(inv_l, vertical_out, outl_name, buf_invert) else: cur_tot = core_col + inv_col + inv_gap num_col_tot = max(num_col_tot, cur_tot + inv_sep) sep = num_col_tot - cur_tot sep += (sep & 1) core = self.add_tile(core_master, 0, 0) self._center_col = core_master.center_col cur_col = core_col + sep vdd_list.append(core.get_pin('VDD')) vss_list.append(core.get_pin('VSS')) inv_r = self.add_tile(invr_master, 0, cur_col, commit=False) self._update_buf_inst(inv_r, vm_layer, sig_locs_r, sig_locs, 'r') vdd_list.append(inv_r.get_pin('VDD')) vss_list.append(inv_r.get_pin('VSS')) self._export_buf(inv_r, vertical_out, outr_name, buf_invert) self.set_mos_size(num_cols=cur_col + inv_col_even) # export supplies self.add_pin('VDD', self.connect_wires(vdd_list)) self.add_pin('VSS', self.connect_wires(vss_list)) # connect core output to buffer input self.connect_wires([core.get_pin('poutr'), inv_r.get_pin('pin')]) # reexport core pins if core.has_port('rst_casc'): self.reexport(core.get_port('rst_casc')) self.reexport(core.get_port('rst_outp'), net_name='rst_out') self.reexport(core.get_port('rst_outn'), net_name='rst_outb') self.reexport(core.get_port('inp'), net_name='in') self.reexport(core.get_port('inn'), net_name='inb') midp = core.get_pin('outp') midn = core.get_pin('outn') self.add_pin('midp', midp, hide=not export_pins) self.add_pin('midn', midn, hide=not export_pins) if invert_in: self.add_pin('midr', midn, hide=True) self.add_pin('midl', midp, hide=True) else: self.add_pin('midr', midp, hide=True) self.add_pin('midl', midn, hide=True) # compute schematic parameters self.sch_params = dict( core_params=core_master.sch_params, buf_params=sch_buf_params, dual_output=dual_output, invert_out=invert_out, )
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 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) 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') 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.G, wire_name='sup') nclk_tid = self.get_track_id(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=-1) nrst_tid = prst_tid = None tail_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sig') tail_in_tid = tail_tid 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)) 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) 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)) 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: tail = self.connect_to_tracks([tail_conn, m_in.d], tail_tid) 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) 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, )
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo, mirror=False) unit_params: Param = self.params['unit_params'] inv_params: Param = self.params['inv_params'] nbits: int = self.params['nbits'] flip_b_en: bool = self.params['flip_b_en'] draw_sub: bool = self.params['draw_sub'] export_outb: bool = self.params['export_outb'] abut_tristates: bool = self.params['abut_tristates'] # create masters logic_pinfo = self.get_tile_pinfo(1) unit_params = unit_params.copy(append=dict(pinfo=logic_pinfo, vertical_sup=True, vertical_out=False)) unit_cls = InvTristateCore if abut_tristates else PhaseInterpUnit unit_master: Union[PhaseInterpUnit, InvTristateCore] = self.new_template(unit_cls, params=unit_params) seg = inv_params['seg'] if seg == 0: raise ValueError('Only seg is supported in inv_params.') if seg & 1: raise ValueError('Output inverter must have even number of segments.') if abut_tristates and unit_params['seg'] & 1: raise ValueError('Tristates must have even segments when abutting tristates') seg_half = seg // 2 sig_locs = {'in': unit_master.get_track_index(1, MOSWireType.G, 'sig', wire_idx=0)} inv_params = inv_params.copy(append=dict(pinfo=logic_pinfo, seg=seg_half, vertical_sup=True, sig_locs=sig_locs), remove=['seg_p', 'seg_n']) inv_master = self.new_template(InvCore, params=inv_params) if abut_tristates: self._col_margin = self.get_hm_sp_le_sep_col() else: self._col_margin = unit_master.col_margin a_dict = self._draw_row(unit_master, inv_master, 1, nbits, False, draw_sub, 'a', 0, 2, abut_tristates) b_dict = self._draw_row(unit_master, inv_master, 3, nbits, flip_b_en, draw_sub, 'b', 4, 2, abut_tristates) self.set_mos_size(num_tiles=5) outb = self.connect_wires(a_dict['mid'] + b_dict['mid']) self.add_pin('outb', outb, hide=not export_outb) out = self.connect_wires([a_dict['out'], b_dict['out']]) self.add_pin('out', out) for name in ['VDD', 'VSS', 'VDD_hm']: self.add_pin(name, self.connect_wires(a_dict[name] + b_dict[name])) self.add_pin('VSS0', a_dict['VSS_hm'], hide=True) self.add_pin('VSS1', b_dict['VSS_hm'], hide=True) if draw_sub: vss_hm = [a_dict['VSS_hm'], b_dict['VSS_hm']] if isinstance(a_dict['VDD_hm'], List): vdd_hm = self.connect_wires(a_dict['VDD_hm']+b_dict['VDD_hm']) else: vdd_hm = self.connect_wires([a_dict['VDD_hm'], b_dict['VDD_hm']]) ncol_tot = self.num_cols sub = self.add_substrate_contact(0, 0, tile_idx=0, seg=ncol_tot) self.connect_to_track_wires(sub, vss_hm[0]) sub = self.add_substrate_contact(0, 0, tile_idx=2, seg=ncol_tot) self.connect_to_track_wires(sub, vdd_hm) sub = self.add_substrate_contact(0, 0, tile_idx=4, seg=ncol_tot) self.connect_to_track_wires(sub, vss_hm[1]) self.sch_params = dict( tri_params=unit_master.sch_params, inv_params=inv_master.sch_params.copy(append=dict(seg_p=seg, seg_n=seg)), nbits=nbits, export_outb=export_outb, )
def draw_layout(self): params = self.params pinfo = MOSBasePlaceInfo.make_place_info(self.grid, params['pinfo']) self.draw_base(pinfo, flip_tile=True) lv_params: Param = params['lv_params'] in_buf_params: Param = params['in_buf_params'] export_pins = params['export_pins'] # create masters lv_params = lv_params.copy(dict(pinfo=pinfo, export_pins=export_pins)) lv_master: LevelShifterCoreOutBuffer = self.new_template( LevelShifterCoreOutBuffer, params=lv_params) self._ridx_p = lv_master.params['ridx_p'] self._ridx_n = lv_master.params['ridx_n'] in_buf_params = in_buf_params.copy(dict( pinfo=pinfo, dual_output=True, vertical_output=True, is_guarded=lv_master.is_guarded), remove=['sig_locs']) buf_master: InvChainCore = self.new_template(InvChainCore, params=in_buf_params) if buf_master.num_stages != 2: raise ValueError('Must have exactly two stages in input buffer.') # make sure buffer outb output is next to out, to avoid collision vm_layer = self.conn_layer + 2 out_tidx = buf_master.get_port('out').get_pins()[0].track_id.base_index prev_tidx = self.tr_manager.get_next_track(vm_layer, out_tidx, 'sig', 'sig', up=False) buf_master = cast( InvChainCore, buf_master.new_template_with(sig_locs=dict(outb=prev_tidx))) # placement lv = self.add_tile(lv_master, 1, 0) buf_ncol = buf_master.num_cols if lv_master.mid_vertical: tid = lv.get_pin('midr').track_id tidx = self.tr_manager.get_next_track(vm_layer, tid.base_index, 'sig', 'sig', up=True) col_idx = self.arr_info.track_to_col(vm_layer, tidx, mode=RoundMode.GREATER_EQ) buf_idx = col_idx + buf_ncol else: lv_center = lv_master.center_col buf_idx = lv_center + buf_ncol # make sure supply on even number buf_idx += (buf_idx & 1) buf = self.add_tile(buf_master, 0, buf_idx, flip_lr=True) self.set_mos_size(num_cols=max(buf_idx, lv_master.num_cols)) # connect wires self.add_pin( 'VSS', self.connect_wires([lv.get_pin('VSS'), buf.get_pin('VSS')])) self.connect_differential_wires(buf.get_pin('out'), buf.get_pin('outb'), lv.get_pin('in'), lv.get_pin('inb')) # reexport pins self.reexport(buf.get_port('VDD'), net_name='VDD_in') self.reexport(lv.get_port('VDD')) self.reexport(buf.get_port('in')) if export_pins: self.add_pin('inb_buf', buf.get_pin('outb')) self.add_pin('in_buf', buf.get_pin('out')) self.reexport(lv.get_port('midn')) self.reexport(lv.get_port('midp')) for name in ['out', 'outb', 'rst_out', 'rst_outb', 'rst_casc']: if lv.has_port(name): self.reexport(lv.get_port(name)) buf_sch_params = buf_master.sch_params.copy(remove=['dual_output']) lv_sch_params = lv_master.sch_params.copy( remove=['dual_output', 'invert_out']) self.sch_params = dict( lev_params=lv_sch_params, buf_params=buf_sch_params, dual_output=lv_master.dual_output, invert_out=lv_master.outr_inverted, export_pins=export_pins, )
def draw_layout(self): params = self.params pinfo = MOSBasePlaceInfo.make_place_info(self.grid, params['pinfo']) self.draw_base(pinfo) invp_params_list: Sequence[Param] = params['invp_params_list'] invn_params_list: Sequence[Param] = params['invn_params_list'] pg_params: Param = params['pg_params'] ridx_p: int = params['ridx_p'] ridx_n: int = params['ridx_n'] sig_locs: Mapping[str, Union[float, HalfInt]] = params['sig_locs'] is_guarded: bool = params['is_guarded'] swap_tiles: bool = params['swap_tiles'] vertical_out: bool = params['vertical_out'] vertical_in: bool = params['vertical_in'] export_pins: bool = params['export_pins'] if len(invp_params_list) != 2 or len(invn_params_list) != 3: raise ValueError('Wrong number of parameters for inverters.') hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 # create masters tmp = self._create_masters(pinfo, ridx_p, ridx_n, is_guarded, invp_params_list, invn_params_list, pg_params, sig_locs, vertical_out, vertical_in) invp_masters, invn_masters, pg_master = tmp # place instances # first two columns left-aligned, last column right-aligned ncol0 = max(invp_masters[0].num_cols, invn_masters[0].num_cols) ncol0 += (ncol0 & 1) ncol1 = max(pg_master.num_cols, invn_masters[1].num_cols) ncol1 += (ncol1 & 1) invp_ncol2 = invp_masters[1].num_cols invn_ncol2 = invn_masters[2].num_cols invp_ncol2 += (invp_ncol2 & 1) invn_ncol2 += (invn_ncol2 & 1) ncol2 = max(invp_ncol2, invn_ncol2) sep = self.min_sep_col col1 = ncol0 + sep col_tot = col1 + ncol1 + sep + ncol2 tile_outp = int(swap_tiles) tile_outn = 1 - tile_outp invp0 = self.add_tile(invp_masters[0], tile_outp, 0) invn0 = self.add_tile(invn_masters[0], tile_outn, 0) pg = self.add_tile(pg_master, tile_outp, col1) invn1 = self.add_tile(invn_masters[1], tile_outn, col1) invp1 = self.add_tile(invp_masters[1], tile_outp, col_tot - invp_ncol2) invn2 = self.add_tile(invn_masters[2], tile_outn, col_tot - invn_ncol2) self._buf_col_list = [0, col1, col_tot - invn_ncol2] self.set_mos_size(num_cols=col_tot) if export_pins: self.add_pin('midn_pass0', invp0.get_pin('out')) self.add_pin('midn_pass1', pg.get_pin('s')) self.add_pin('midn_inv', invn0.get_pin('out')) self.add_pin('midp', invn1.get_pin('out')) # vdd/vss vdd_list = [] vss_list = [] for inst in [invp0, pg, invp1, invn0, invn1, invn2]: vdd_list.extend(inst.port_pins_iter('VDD')) vss_list.extend(inst.port_pins_iter('VSS')) vdd = self.connect_wires(vdd_list)[0] vss = self.connect_wires(vss_list)[0] self.add_pin('VDD', vdd, connect=True) self.add_pin('VSS', vss, connect=True) # connect inverters and pass gates tr_manager = self.tr_manager vm_w = tr_manager.get_width(vm_layer, 'sig') invp0_out = invp0.get_pin('out') self.connect_wires([invp0.get_pin('pout'), pg.get_pin('pd')]) self.connect_wires([invp0.get_pin('nout'), pg.get_pin('nd')]) self.connect_wires([invp1.get_pin('nin'), pg.get_pin('ns')]) if is_guarded: self.connect_wires([invp1.get_pin('pin'), pg.get_pin('ps')]) # inputs if vertical_in: self.add_pin( 'in', self.connect_wires( [invp0.get_pin('in'), invn0.get_pin('in')])) else: self.reexport(invp0.get_port('in')) self.reexport(invn0.get_port('in')) # connect first stage en_center = invn1.get_pin('in') self.connect_to_track_wires( [invn0.get_pin('nout'), invn0.get_pin('pout')], en_center) self.connect_to_tracks([ invp0.get_pin('nout'), invp0.get_pin('pout'), pg.get_pin('pd'), pg.get_pin('nd') ], en_center.track_id) # connect second stage self.connect_to_track_wires( [invn1.get_pin('nout'), invn1.get_pin('pout')], invn2.get_pin('in')) self.connect_to_track_wires( [pg.get_pin('ps'), pg.get_pin('ns')], invp1.get_pin('in')) else: # inputs invn0_out = invn0.get_pin('out') if vertical_in: vm_ref = min(invp0_out.track_id.base_index, invn0_out.track_id.base_index) in_tidx = tr_manager.get_next_track(vm_layer, vm_ref, 'sig', 'sig', up=False) self.add_pin( 'in', self.connect_to_tracks( [invp0.get_pin('in'), invn0.get_pin('in')], TrackID(vm_layer, in_tidx, width=vm_w))) else: self.reexport(invp0.get_port('in')) self.reexport(invn0.get_port('in')) # connect first stage self.connect_to_track_wires(invn1.get_pin('in'), invn0_out) self.connect_wires([ invp0.get_pin('nout'), invp0.get_pin('pout'), pg.get_pin('pd'), pg.get_pin('nd') ]) # connect second stage en_center = invn1.get_pin('out') self.connect_to_track_wires(invn2.get_pin('in'), en_center) self.connect_to_tracks( [invp1.get_pin('in'), pg.get_pin('ps'), pg.get_pin('ns')], en_center.track_id) # enables for passgate vdd_tidx = tr_manager.get_next_track(vm_layer, en_center.track_id.base_index, 'sig', 'sig', up=False) vss_tidx = tr_manager.get_next_track(vm_layer, en_center.track_id.base_index, 'sig', 'sig', up=True) self.connect_to_tracks([pg.get_pin('en'), vdd], TrackID(vm_layer, vdd_tidx, width=vm_w)) self.connect_to_tracks([pg.get_pin('enb'), vss[0]], TrackID(vm_layer, vss_tidx, width=vm_w)) # export outputs if vertical_out: self.reexport(invp1.get_port('out'), net_name='outp') self.reexport(invn2.get_port('out'), net_name='outn') else: self.reexport(invn2.get_port('pout'), net_name='outn', label='outn:', hide=False) self.reexport(invn2.get_port('nout'), net_name='outn', label='outn:', hide=False) self.reexport(invp1.get_port('pout'), net_name='outp', label='outp:', hide=False) self.reexport(invp1.get_port('nout'), net_name='outp', label='outp:', hide=False) # set schematic parameters self.sch_params = dict( invp_params_list=[ invp_masters[0].sch_params, invp_masters[1].sch_params ], invn_params_list=[ invn_masters[0].sch_params, invn_masters[1].sch_params, invn_masters[2].sch_params ], pg_params=pg_master.sch_params, export_pins=export_pins, )
def draw_layout(self): pinfo = MOSBasePlaceInfo.make_place_info(self.grid, self.params['pinfo']) self.draw_base(pinfo, flip_tile=True) conn_layer = self.conn_layer hm_layer = conn_layer + 1 vm_layer = hm_layer + 1 xm_layer = vm_layer + 1 ridx_p: int = self.params['ridx_p'] ridx_n: int = self.params['ridx_n'] core_ncol: int = self.params['core_ncol'] tap_info_list: ImmutableList[Tuple[int, bool]] = self.params['tap_info_list'] tmp = self._make_masters(self, pinfo, self.params) (lv_data_master, lv_ctrl_master, lv_por_master, inv_master, data_master, clk_master, buf_ctrl_lv_master, buf_por_lv_master) = tmp inbuf_ncol = max(data_master.num_cols, clk_master.num_cols) # track definitions bot_vss = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=1) top_vss = self.get_track_id(ridx_n, MOSWireType.DS, 'sup', tile_idx=2) # Placement lv_data_ncol = lv_data_master.num_cols lv_ctl_ncol = lv_ctrl_master.num_cols lv_por_ncol = lv_por_master.num_cols min_sep = self.min_sep_col sup_info = self.get_supply_column_info(xm_layer) inv_ncol = inv_master.num_cols inv_ncol += (inv_ncol & 1) half_ncol = max(lv_data_ncol + min_sep + inv_ncol, inbuf_ncol, core_ncol) # assume 2 inverter chains + margins have smaller width than a single lvl shifter if 2 * buf_ctrl_lv_master.num_cols + min_sep > lv_ctl_ncol: raise ValueError("buffer too large compared to data level shifter's width") if 2 * buf_por_lv_master.num_cols + min_sep > lv_por_ncol: raise ValueError("buffer too large compared to data level shifter's width") # initialize supply data structures lay_range = range(conn_layer, xm_layer + 1) vdd_io_table = {lay: [] for lay in lay_range} vdd_core_table = {lay: [] for lay in lay_range} vss_table = {lay: [] for lay in lay_range} # instantiate cells and collect pins pin_dict = {'por': [], 'porb': [], 'VDDIO': [], 'VSS': [], 'por_core': [], 'porb_core': [], 'VDD': []} in_pins = ['din_en', 'por_in', 'ck_en', 'unused'] cur_col = 0 cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, True) new_col = draw_io_shifters(self, cur_col, buf_ctrl_lv_master, buf_por_lv_master, lv_ctrl_master, lv_por_master, bot_vss, top_vss, in_pins[0], in_pins[1], True, True, False, pin_dict) cur_col = new_col cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, False) cur_col += half_ncol self._draw_data_path(cur_col, data_master, lv_data_master, inv_master, bot_vss, True, 'data_', pin_dict) cur_col += min_sep self._draw_data_path(cur_col, clk_master, lv_data_master, inv_master, bot_vss, False, 'clk_', pin_dict) cur_col += half_ncol + self.sub_sep_col // 2 cur_col = draw_io_supply_column(self, cur_col, sup_info, vdd_io_table, vdd_core_table, vss_table, ridx_p, ridx_n, True) cur_col = draw_io_shifters(self, cur_col, buf_ctrl_lv_master, buf_por_lv_master, lv_ctrl_master, lv_por_master, bot_vss, top_vss, in_pins[2], in_pins[3], True, True, False, pin_dict, flip_lr=True) # add tapes on bottom tile max_col = self._draw_bot_supply_column(tap_info_list, sup_info, vdd_core_table, vss_table, ridx_p, ridx_n) max_col = max(cur_col, max_col) self.set_mos_size(num_cols=max_col) xh = self.bound_box.xh # connect and export supply pins vss_table[hm_layer].extend(pin_dict['VSS']) vdd_core_table[hm_layer].extend(pin_dict['VDD']) vdd_io_table[hm_layer].extend(pin_dict['VDDIO']) vss_list = vdd_io_list = vdd_core_list = [] for lay in range(hm_layer, xm_layer + 1, 2): vss_list = self.connect_wires(vss_table[lay], upper=xh) vdd_core_list = self.connect_wires(vdd_core_table[lay], upper=xh) vdd_io_list = self.connect_wires(vdd_io_table[lay], upper=xh) vss_vm = vss_table[vm_layer] self.add_pin('VDDIO', vdd_io_list) self.add_pin('VDDCore', vdd_core_list) self.add_pin('VSS', vss_list) self.add_pin('VDDIO_vm', vdd_io_table[vm_layer], hide=True) self.add_pin('VDDCore_vm', vdd_core_table[vm_layer], hide=True) self.add_pin('VSS_vm', vss_vm, hide=True) self.add_pin('VDDIO_conn', vdd_io_table[conn_layer], hide=True) self.add_pin('VDDCore_conn', vdd_core_table[conn_layer], hide=True) self.add_pin('VSS_conn', vss_table[conn_layer], hide=True) # connect VDDCore signals grid = self.grid tr_manager = self.tr_manager vm_w = tr_manager.get_width(vm_layer, 'sig') xm_w = tr_manager.get_width(xm_layer, 'sig') xm_w_hs = tr_manager.get_width(xm_layer, 'sig_hs') # VDD, por_buf, clkn, clkp, data, data_async, porb_buf, data_en, clk_in, por_in idx_list = tr_manager.place_wires(xm_layer, ['sup', 'sig', 'sig_hs', 'sig_hs', 'sig_hs', 'sig_hs', 'sig', 'sig', 'sig', 'sig'], align_track=vdd_core_list[0].track_id.base_index)[1] # POR por_core, porb_core = pin_dict['por_in_buf'] por_list = pin_dict['por_core'] porb_list = pin_dict['porb_core'] por_list.append(por_core) porb_list.append(porb_core) por_core, porb_core = self.connect_differential_tracks(por_list, porb_list, xm_layer, idx_list[1], idx_list[6], width=xm_w) # extend POR signals so they are symmetric with respect to center self.extend_wires([por_core, porb_core], upper=2 * self.bound_box.xm - por_core.lower) # VDDCore data and control signals match_wires = [pin_dict['data_out'], pin_dict['data_async'], pin_dict['clk_outp'], pin_dict['clk_outn']] results = self.connect_matching_tracks(match_wires, xm_layer, [idx_list[4], idx_list[5], idx_list[3], idx_list[2]], width=xm_w_hs, track_lower=0) self.add_pin('odat', results[0], mode=PinMode.LOWER) self.add_pin('odat_async', results[1], mode=PinMode.LOWER) self.add_pin('oclkp', results[2], mode=PinMode.LOWER) self.add_pin('oclkn', results[3], mode=PinMode.LOWER) in_info_list = [('din_en', 'data_en', idx_list[7]), ('por_in', 'por', idx_list[9]), ('unused', 'unused', -1), ('ck_en', 'clk_en', idx_list[8])] for buf_idx, (key, port_name, tidx) in enumerate(in_info_list): hm_warr = pin_dict[key][0] vm_tidx_ref = pin_dict[key + '_buf'][1].track_id.base_index vm_tidx = tr_manager.get_next_track(vm_layer, vm_tidx_ref, 'sig', 'sig', up=((buf_idx & 1) == 0)) vm_warr = self.connect_to_tracks(hm_warr, TrackID(vm_layer, vm_tidx, width=vm_w), min_len_mode=MinLenMode.LOWER) if key == 'unused': self.connect_to_tracks(vm_warr, bot_vss) else: self.add_pin(port_name, self.connect_to_tracks(vm_warr, TrackID(xm_layer, tidx, width=xm_w), track_lower=0), mode=PinMode.LOWER) # connect VDDIO signals idx_list = tr_manager.place_wires(xm_layer, ['sig', 'sig', 'sup', 'sig', 'sig'], align_track=vdd_io_list[0].track_id.base_index, align_idx=2)[1] for name, ename in [('din_en', 'data'), ('ck_en', 'clk')]: wp, wn = self.connect_differential_tracks(pin_dict[f'{name}_out'], pin_dict[f'{name}_outb'], xm_layer, idx_list[1], idx_list[3], width=xm_w) self.connect_to_track_wires(wp, pin_dict[f'{ename}_en']) self.connect_to_track_wires(wn, pin_dict[f'{ename}_enb']) rp, rn = self.connect_differential_tracks(pin_dict['por_in_out'], pin_dict['por_in_outb'], xm_layer, idx_list[0], idx_list[4], width=xm_w) self.connect_differential_wires(pin_dict['por'], pin_dict['porb'], rp, rn) idx_list = tr_manager.place_wires(vm_layer, ['sig', 'sig', 'sup'], align_track=vdd_io_table[vm_layer][0].track_id.base_index, align_idx=-1)[1] rp, rn = self.connect_differential_tracks(rp, rn, vm_layer, idx_list[0], idx_list[1], width=tr_manager.get_width(vm_layer, 'sig')) self.add_pin('por_vccl', rp) self.add_pin('porb_vccl', rn) # connect io pad signals in_pins = pin_dict['data_in'] in_pins.append(pin_dict['clk_inp'][0]) clkn = pin_dict['clk_inn'][0] in_tidx = grid.coord_to_track(xm_layer, in_pins[0].middle, mode=RoundMode.GREATER_EQ) ck_tidx = grid.coord_to_track(xm_layer, clkn.middle, mode=RoundMode.LESS_EQ) io_w = tr_manager.get_width(xm_layer, 'padout') in_warr = self.connect_to_tracks(in_pins, TrackID(xm_layer, in_tidx, width=io_w), min_len_mode=MinLenMode.MIDDLE) clk_warr = self.connect_to_tracks(clkn, TrackID(xm_layer, ck_tidx, width=io_w), track_lower=in_warr.lower, track_upper=in_warr.upper) self.add_pin('iopad', in_warr) self.add_pin('iclkn', clk_warr) # setup schematic parameters rm_keys = ['dual_output', 'invert_out'] data_lv_sch_params = lv_data_master.sch_params.copy(remove=rm_keys) ctrl_lv_sch_params = lv_ctrl_master.sch_params.copy(remove=rm_keys) por_lv_sch_params = lv_por_master.sch_params.copy(remove=rm_keys) self.sch_params = dict( data_params=data_master.sch_params, clk_params=clk_master.sch_params, data_lv_params=data_lv_sch_params, ctrl_lv_params=ctrl_lv_sch_params, por_lv_params=por_lv_sch_params, buf_ctrl_lv_params=buf_ctrl_lv_master.sch_params.copy(remove=['dual_output']), buf_por_lv_params=buf_por_lv_master.sch_params.copy(remove=['dual_output']), inv_params=inv_master.sch_params, )
def draw_layout(self): params = self.params pinfo = MOSBasePlaceInfo.make_place_info(self.grid, params['pinfo']) self.draw_base(pinfo) tr_manager = self.tr_manager seg_dict: Mapping[str, int] = params['seg_dict'] w_dict: Mapping[str, int] = params['w_dict'] ridx_p: int = params['ridx_p'] ridx_n: int = params['ridx_n'] vertical_rst: List[str] = params['vertical_rst'] is_guarded: bool = params['is_guarded'] in_upper: bool = params['in_upper'] inp_on_right: bool = params['inp_on_right'] has_rst: bool = params['has_rst'] stack_p: int = params['stack_p'] sig_locs: Mapping[str, Union[float, HalfInt]] = params['sig_locs'] if stack_p != 1 and stack_p != 2: raise ValueError('Only support stack_p = 1 or stack_p = 2') if stack_p == 2: if not has_rst: raise ValueError('stack_p = 2 only allowed if has_rst = True') if not in_upper: raise ValueError('stack_p = 2 only allowed if in_upper = True') hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 sig_w_hm = tr_manager.get_width(hm_layer, 'sig') seg_pu = seg_dict['pu'] seg_pd = seg_dict['pd'] seg_rst = seg_dict.get('rst', 0) seg_prst = seg_dict.get('prst', 0) default_wp = self.get_row_info(ridx_p).width default_wn = self.get_row_info(ridx_n).width w_pd = w_dict.get('pd', default_wn) w_pu = w_dict.get('pu', default_wp) sch_w_dict = dict(pd=w_pd, pu=w_pu) vss_tid = self.get_track_id(ridx_n, MOSWireType.DS, wire_name='sup') vdd_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sup') pg_midl_tidx = self.get_track_index(ridx_p, MOSWireType.G, 'sig', wire_idx=-2) pg_midr_tidx = self.get_track_index(ridx_p, MOSWireType.G, 'sig', wire_idx=-1) nd_mid_tidx = self.get_track_index(ridx_n, MOSWireType.DS, 'sig', wire_idx=-1) inn_tidx = self.get_track_index(ridx_n, MOSWireType.G, 'sig', wire_idx=0) inp_tidx = self.get_track_index(ridx_n, MOSWireType.G, 'sig', wire_idx=1) inn_tidx = sig_locs.get('inb', inn_tidx) inp_tidx = sig_locs.get('in', inp_tidx) inn_tid = TrackID(hm_layer, inn_tidx, width=sig_w_hm) inp_tid = TrackID(hm_layer, inp_tidx, width=sig_w_hm) if in_upper: rst_tid = inn_tid else: rst_tid = None nd_mid_tid = TrackID(hm_layer, nd_mid_tidx) # floorplanning number of columns min_sep_odd = self.min_sep_col min_sep_even = min_sep_odd + (min_sep_odd & 1) mid_sep = min_sep_even mid_sep2 = mid_sep // 2 pmos_fg = seg_pu * stack_p pmos_col = pmos_fg + (pmos_fg & 1) if has_rst: rst_sep = min_sep_odd nmos_fg = 2 * seg_pd nmos_col = seg_rst + rst_sep + nmos_fg if nmos_col & 1: rst_sep += 1 nmos_col += 1 if stack_p == 2: if pmos_fg > nmos_fg: # TODO: remove lazy hack raise ValueError( 'pmos reset placement code will break in this case') num_core_col = max(nmos_col, pmos_col) else: rst_sep = 0 nmos_fg = seg_pd num_core_col = max(seg_pd + (seg_pd & 1), pmos_col) self._center_col = col_mid = num_core_col + mid_sep2 seg_tot = 2 * col_mid self.set_mos_size(num_cols=seg_tot) # --- Placement --- # # rst export_mid = sep_g_pmos = (stack_p != 1) load_l = self.add_mos(ridx_p, col_mid - mid_sep2, seg_pu, g_on_s=True, w=w_pu, stack=stack_p, sep_g=sep_g_pmos, export_mid=export_mid, flip_lr=True) load_r = self.add_mos(ridx_p, col_mid + mid_sep2, seg_pu, g_on_s=True, w=w_pu, stack=stack_p, sep_g=sep_g_pmos, export_mid=export_mid) vdd_list = [load_l.s, load_r.s] if has_rst: if seg_rst == 0: raise ValueError('seg_rst cannot be 0') w_rst = w_dict.get('rst', default_wn) sch_w_dict['rst'] = w_rst rst_delta = mid_sep2 + nmos_fg + rst_sep + seg_rst rst_l = self.add_mos(ridx_n, col_mid - rst_delta, seg_rst, w=w_rst) rst_r = self.add_mos(ridx_n, col_mid + rst_delta, seg_rst, w=w_rst, flip_lr=True) in_l = self.add_mos(ridx_n, col_mid - mid_sep2, seg_pd, g_on_s=True, w=w_pd, stack=2, sep_g=True, flip_lr=True) in_r = self.add_mos(ridx_n, col_mid + mid_sep2, seg_pd, g_on_s=True, w=w_pd, stack=2, sep_g=True) vss_list = [in_l.s, in_r.s, rst_r.s, rst_l.s] nd_l = [rst_l.d, in_l.d] nd_r = [rst_r.d, in_r.d] if stack_p == 2: if seg_prst == 0: raise ValueError('seg_prst cannot be 0') prst_sep = min_sep_odd + ((min_sep_odd & 1) ^ ((seg_prst & 1) == 0)) prst_delta = mid_sep2 + pmos_fg + prst_sep + seg_prst prst_l = self.add_mos(ridx_p, col_mid - prst_delta, seg_prst, w=w_pu) prst_r = self.add_mos(ridx_p, col_mid + prst_delta, seg_prst, w=w_pu, flip_lr=True) else: prst_l = prst_r = None else: prst_l = prst_r = rst_l = rst_r = None in_l = self.add_mos(ridx_n, col_mid - mid_sep2, seg_pd, g_on_s=True, w=w_pd, flip_lr=True) in_r = self.add_mos(ridx_n, col_mid + mid_sep2, seg_pd, g_on_s=True, w=w_pd) vss_list = [in_l.s, in_r.s] nd_l = [in_l.d] nd_r = [in_r.d] # --- Routing --- # # vdd/vss vdd = self.connect_to_tracks(vdd_list, vdd_tid) vss = self.connect_to_tracks(vss_list, vss_tid) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) if has_rst or is_guarded: # use vm_layer to connect nmos and pmos drains left_coord = self.grid.track_to_coord( self.conn_layer, in_l.s[0].track_id.base_index) right_coord = self.grid.track_to_coord( self.conn_layer, in_r.s[0].track_id.base_index) dleft_tidx = self.grid.coord_to_track(vm_layer, left_coord, RoundMode.NEAREST) dright_tidx = self.grid.coord_to_track(vm_layer, right_coord, RoundMode.NEAREST) dleft_tidx = tr_manager.get_next_track(vm_layer, dleft_tidx, 'sig', 'sig', up=False) dright_tidx = tr_manager.get_next_track(vm_layer, dright_tidx, 'sig', 'sig', up=True) # connect nmos drains together nd_midl = self.connect_to_tracks(nd_l, nd_mid_tid) nd_midr = self.connect_to_tracks(nd_r, nd_mid_tid) # pmos cross coupling connection if stack_p == 1: pg_midl, pg_midr = self.connect_differential_tracks( load_l.d, load_r.d, hm_layer, pg_midl_tidx, pg_midr_tidx) pg_midl, pg_midr = self.connect_differential_wires( load_r.g, load_l.g, pg_midl, pg_midr) hm_midl_list = [nd_midl, pg_midl] hm_midr_list = [nd_midr, pg_midr] else: pm_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=0) pd_tid = self.get_track_id(ridx_p, MOSWireType.DS, wire_name='sig', wire_idx=1) pd_midl = self.connect_to_tracks([prst_l.d, load_l.d], pd_tid) pd_midr = self.connect_to_tracks([prst_r.d, load_r.d], pd_tid) self.connect_to_tracks([prst_l.s, load_l.m], pm_tid) self.connect_to_tracks([prst_r.s, load_r.m], pm_tid) self.connect_wires([load_l.g[1::2], in_l.g[1::2]]) self.connect_wires([load_r.g[1::2], in_r.g[1::2]]) pg_midl, pg_midr = self.connect_differential_tracks( load_r.g[0::2], load_l.g[0::2], hm_layer, pg_midl_tidx, pg_midr_tidx, track_lower=pd_midl.lower, track_upper=pd_midr.upper) hm_midl_list = [nd_midl, pd_midl, pg_midl] hm_midr_list = [nd_midr, pd_midr, pg_midr] vm_w = tr_manager.get_width(vm_layer, 'sig') midl_vm_tid = TrackID(vm_layer, dleft_tidx, width=vm_w) midr_vm_tid = TrackID(vm_layer, dright_tidx, width=vm_w) midl = self.connect_to_tracks(hm_midl_list, midl_vm_tid) midr = self.connect_to_tracks(hm_midr_list, midr_vm_tid) midl, midr = self.extend_wires([midl, midr], lower=min(midl.lower, midr.lower), upper=max(midl.upper, midr.upper)) if has_rst: # reset connections if in_upper: inl = in_l.g1 inr = in_r.g1 rst_midl_b = in_l.g0 rst_midr_b = in_r.g0 else: inl = in_l.g0 inr = in_r.g0 rst_midl_b = in_l.g1 rst_midr_b = in_r.g1 # connect rst gates rst_b_tidx = self.get_track_index(ridx_n, MOSWireType.G, 'sig', wire_idx=2) rst_b_tidx = sig_locs.get('rst_casc', rst_b_tidx) rst_b_tid = TrackID(hm_layer, rst_b_tidx, width=sig_w_hm) if rst_tid is None: rst_tid = rst_b_tid rst_b_wires = [rst_midl_b, rst_midr_b] if prst_l is not None: # TODO: check for line-end spacing errors, rst_b_tid may not be a good track. rst_b_wires.append(prst_l.g) rst_b_wires.append(prst_r.g) rst_b_warr = self.connect_to_tracks(rst_b_wires, rst_b_tid) # rst_tid has some value now, convert that to rst_outn_tid / rst_outp_tid based # on sig_locs rst_outn_tidx = sig_locs.get('rst_outb', None) if rst_outn_tidx: rst_outn_tid = TrackID(hm_layer, rst_outn_tidx, width=sig_w_hm) else: rst_outn_tid = rst_tid rst_outp_tidx = sig_locs.get('rst_out', None) if rst_outp_tidx: rst_outp_tid = TrackID(hm_layer, rst_outp_tidx, width=sig_w_hm) else: rst_outp_tid = rst_tid rst_outp_gwarrs = rst_l.g if inp_on_right else rst_r.g rst_outn_gwarrs = rst_r.g if inp_on_right else rst_l.g rst_outp_warr = self.connect_to_tracks(rst_outp_gwarrs, rst_outp_tid) rst_outn_warr = self.connect_to_tracks(rst_outn_gwarrs, rst_outn_tid) rst_list = [('rst_outn', rst_outn_warr), ('rst_outp', rst_outp_warr), ('rst_casc', rst_b_warr)] for name, wire in rst_list: if name in vertical_rst: if name == 'rst_casc': vm_tid = self.grid.coord_to_track( vm_layer, wire.middle, RoundMode.NEAREST) elif (name == 'rst_outn') ^ inp_on_right: vm_tid = self.grid.coord_to_track( vm_layer, wire.upper, RoundMode.GREATER_EQ) else: vm_tid = self.grid.coord_to_track( vm_layer, wire.lower, RoundMode.LESS_EQ) wire = self.connect_to_tracks( wire, TrackID(vm_layer, vm_tid), min_len_mode=MinLenMode.MIDDLE) self.add_pin(name, wire) else: inl = in_l.g inr = in_r.g else: # use conn_layer to connect nmos and pmos drains pg_midl, pg_midr = self.connect_differential_tracks( nd_l, nd_r, hm_layer, pg_midl_tidx, pg_midr_tidx) pg_midl, pg_midr = self.connect_differential_wires( load_l.d, load_r.d, pg_midl, pg_midr) pg_midl, pg_midr = self.connect_differential_wires( load_r.g, load_l.g, pg_midl, pg_midr) midl = pg_midl midr = pg_midr inl = in_l.g inr = in_r.g # connect and export input and output pins self.add_pin('poutr', pg_midr, hide=True) self.add_pin('poutl', pg_midl, hide=True) if inp_on_right: inp, inn = self.connect_differential_tracks(inr, inl, inp_tid.layer_id, inp_tid.base_index, inn_tid.base_index, width=inp_tid.width) self.add_pin('outn', midr) self.add_pin('outp', midl) else: inp, inn = self.connect_differential_tracks(inl, inr, inp_tid.layer_id, inp_tid.base_index, inn_tid.base_index, width=inp_tid.width) self.add_pin('outp', midr) self.add_pin('outn', midl) self.add_pin('inp', inp) self.add_pin('inn', inn) # compute schematic parameters default_thp = self.get_row_info(ridx_p).threshold default_thn = self.get_row_info(ridx_n).threshold self.sch_params = dict( lch=self.place_info.lch, seg_dict=seg_dict, w_dict=sch_w_dict, intent_dict=dict( nch=default_thn, pch=default_thp, ), in_upper=in_upper, has_rst=has_rst, stack_p=stack_p, )
def draw_layout(self): grid = self.grid params = self.params pinfo = MOSBasePlaceInfo.make_place_info(grid, params['pinfo']) self.draw_base(pinfo) tr_manager = self.tr_manager seg_p: int = params['seg_p'] seg_n: int = params['seg_n'] seg_nand: int = params['seg_nand'] seg_nor: int = params['seg_nor'] w_p: int = params['w_p'] w_n: int = params['w_n'] w_p_nand: int = params['w_p_nand'] w_n_nand: int = params['w_n_nand'] w_p_nor: int = params['w_p_nor'] w_n_nor: int = params['w_n_nor'] ridx_p: int = params['ridx_p'] ridx_n: int = params['ridx_n'] export_pins: bool = params['export_pins'] vm_layer = self.conn_layer + 2 #if (seg_p - seg_n) % 2 != 0: # raise ValueError('seg_p - seg_n should be even') in0_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=0) nout_tidx = self.get_track_index(ridx_n, MOSWireType.DS_GATE, wire_name='sig', wire_idx=0) pout_tidx = self.get_track_index(ridx_p, MOSWireType.DS_GATE, wire_name='sig', wire_idx=-1) pin_drv_tidx = self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=0) nin_drv_tidx = self.get_track_index(ridx_n, MOSWireType.G, wire_name='sig', wire_idx=2) sig_locs_gate = dict(nin=in0_tidx, nout=nout_tidx, pout=pout_tidx) sig_locs_drv = dict(pin=pin_drv_tidx, nin=nin_drv_tidx) nand_params = dict( pinfo=pinfo, seg=seg_nand, vertical_out=False, vertical_in=False, is_guarded=True, w_p=w_p_nand, w_n=w_n_nand, ridx_n=ridx_n, ridx_p=ridx_p, sig_locs=sig_locs_gate, min_len_mode=dict( in0=MinLenMode.LOWER, in1=MinLenMode.LOWER, ), ) nor_params = nand_params.copy() nor_params['seg'] = seg_nor nor_params['w_p'] = w_p_nor nor_params['w_n'] = w_n_nor drv_params = dict( pinfo=pinfo, seg_p=seg_p, seg_n=seg_n, w_p=w_p, w_n=w_n, stack=1, ridx_p=ridx_p, ridx_n=ridx_n, sig_locs=sig_locs_drv, ) # --- Placement --- # nand_master = self.new_template(NAND2Core, params=nand_params) nor_master = self.new_template(NOR2Core, params=nor_params) drv_master = self.new_template(PullUpDown, params=drv_params) nand_ncols = nand_master.num_cols nor_ncols = nor_master.num_cols drv_ncols = drv_master.num_cols min_sep = self.min_sep_col ncol_tot = nand_ncols + drv_ncols + nor_ncols + 2 * min_sep nand_inst = self.add_tile(nand_master, 0, nand_ncols, flip_lr=True) drv_inst = self.add_tile(drv_master, 0, nand_ncols + min_sep) nor_inst = self.add_tile(nor_master, 0, ncol_tot - nor_ncols) self.set_mos_size(num_cols=ncol_tot) # Routing pin = self.connect_wires( [nor_inst.get_pin('pin<0>'), nand_inst.get_pin('pin<0>')]) nin = self.connect_wires( [nor_inst.get_pin('nin<0>'), nand_inst.get_pin('nin<0>')]) self.add_pin('pin', pin, label='in:') self.add_pin('nin', nin, label='in:') bnd_box = self.bound_box vm_tr_w = tr_manager.get_width(vm_layer, 'sig') en_vm_tidx = grid.coord_to_track(vm_layer, bnd_box.xl, mode=RoundMode.GREATER_EQ) enb_vm_tidx = grid.coord_to_track(vm_layer, bnd_box.xh, mode=RoundMode.LESS_EQ) puenb_vm_tidx = tr_manager.get_next_track(vm_layer, en_vm_tidx, 'sig', 'sig', up=True) pden_vm_tidx = tr_manager.get_next_track(vm_layer, enb_vm_tidx, 'sig', 'sig', up=False) puenb = drv_inst.get_pin('puenb') pden = drv_inst.get_pin('pden') if export_pins: self.add_pin('nand_pu', puenb) self.add_pin('nor_pd', pden) self.connect_to_tracks( [nand_inst.get_pin('pout'), nand_inst.get_pin('nout'), puenb], TrackID(vm_layer, puenb_vm_tidx, width=vm_tr_w)) self.connect_to_tracks( [nor_inst.get_pin('pout'), nor_inst.get_pin('nout'), pden], TrackID(vm_layer, pden_vm_tidx, width=vm_tr_w)) en = self.connect_to_tracks( [nand_inst.get_pin('pin<1>'), nand_inst.get_pin('nin<1>')], TrackID(vm_layer, en_vm_tidx, width=vm_tr_w)) enb = self.connect_to_tracks( [nor_inst.get_pin('pin<1>'), nor_inst.get_pin('nin<1>')], TrackID(vm_layer, enb_vm_tidx, width=vm_tr_w)) self.add_pin('en', en) self.add_pin('enb', enb) self.reexport(drv_inst.get_port('out')) self.reexport(drv_inst.get_port('pout')) self.reexport(drv_inst.get_port('nout')) # connect vss and vdd vdd = self.connect_wires([ nand_inst.get_pin('VDD'), nor_inst.get_pin('VDD'), drv_inst.get_pin('VDD') ]) vss = self.connect_wires([ nand_inst.get_pin('VSS'), nor_inst.get_pin('VSS'), drv_inst.get_pin('VSS') ]) self.add_pin('VDD', vdd) self.add_pin('VSS', vss) # get schematic parameters self.sch_params = dict( nand_params=nand_master.sch_params, nor_params=nor_master.sch_params, pupd_params=drv_master.sch_params, export_pins=export_pins, )
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 = 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=1)) enb_tidx = sig_locs.get( 'penb', self.get_track_index(ridx_p, MOSWireType.G, wire_name='sig', wire_idx=0)) 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, 'out', 'in', 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_inv=sel_inv_master.sch_params, out_inv=out_inv_master.sch_params, tristate=tristate0_master.sch_params)