Exemple #1
0
    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
Exemple #2
0
    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])
Exemple #3
0
    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)
Exemple #4
0
    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)
Exemple #5
0
    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,
        )
Exemple #6
0
    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,
        )
Exemple #7
0
    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,
                )),
        )
Exemple #8
0
    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,
        )
Exemple #11
0
    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))
Exemple #12
0
    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,
        )
Exemple #13
0
    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,
        )
Exemple #14
0
    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,
        )
Exemple #15
0
    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,
        )
Exemple #16
0
    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)
Exemple #18
0
    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,
        )
Exemple #19
0
    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'],
        )
Exemple #20
0
    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,
        )
Exemple #21
0
    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,
        )
Exemple #23
0
    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,
        )
Exemple #24
0
    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,
        )
Exemple #27
0
    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,
        )
Exemple #29
0
    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,
        )
Exemple #30
0
    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)