Пример #1
0
    def make_wire_data(cls, wire_data: Any, alignment: Alignment,
                       ptype_default: str) -> WireData:
        """Construct WireData from given data structure.

        wire_data format:

        WireData = Optional[Union[WireGraph, {data=WireGraph, align=Alignment, shared=List[str]}]]
        WireGraph = Union[WireGroup, List[WireGroup]]
        WireGroup = Union[WireList, {wires=WireList, align=Alignment}]
        WireList = List[Wire]
        Wire = Union[name, (name, placement_type), (name, placement_type, wire_type)]

        Parameters
        ----------
        wire_data : Any
            the wire graph specification data structure.
        alignment : Alignment
            default alignment.
        ptype_default : str
            default placement type.

        Returns
        -------
        wire_data : WireData
            the wire graph specification dataclass.
        """
        if isinstance(wire_data, Mapping):
            actual_data = wire_data['data']
            alignment = Alignment[wire_data.get('align', alignment.name)]
            shared_wires = wire_data.get('shared', [])
        else:
            actual_data = wire_data
            shared_wires = []

        wire_grps = []
        for wire_grp, align in _wire_list_iter(actual_data, alignment):
            wire_list = []
            for winfo in wire_grp:
                if isinstance(winfo, str):
                    name = winfo
                    wtype = ''
                    ptype = ptype_default
                else:
                    name: str = winfo[0]
                    ptype: str = winfo[1]
                    wtype: str = '' if len(winfo) < 3 else winfo[2]
                    if not ptype:
                        ptype = ptype_default
                wire_list.append((name, ptype, wtype))
            wire_grps.append((ImmutableList(wire_list), align))

        return WireData(ImmutableList(wire_grps), ImmutableList(shared_wires))
Пример #2
0
 def get_default_param_values(cls) -> Dict[str, Any]:
     return dict(
         ridx_p=-1,
         ridx_n=0,
         core_ncol=0,
         tap_info_list=ImmutableList(),
     )
Пример #3
0
    def get_complement(
        self, tile_idx: int, row_idx: int, start: int, stop: int
    ) -> ImmutableList[Tuple[Tuple[int, int], MOSEdgeInfo, MOSEdgeInfo]]:
        """Returns a list of unused column intervals within the given interval.

        Parameters
        ----------
        tile_idx : int
            th tile index.
        row_idx : int
            the row index.
        start : int
            the starting column, inclusive.
        stop : int
            the ending column, exclusive.

        Returns
        -------
        ans : ImmutableList[Tuple[Tuple[int, int], MOSEdgeInfo, MOSEdgeInfo]]
            a list of unused column intervals and the associated left/right edge info.
        """
        flat_ridx = self._element.get_flat_row_idx_and_flip(tile_idx,
                                                            row_idx)[0]
        compl_intv = self._intvs[flat_ridx].get_complement((start, stop))
        return ImmutableList([(intv,
                               self._end_flags.get((flat_ridx, intv[0]),
                                                   self.default_edge_info),
                               self._end_flags.get((flat_ridx, intv[1]),
                                                   self.default_edge_info))
                              for intv in compl_intv])
Пример #4
0
 def _draw_bot_supply_column(self, col: int, col_list: Sequence[Tuple[int, bool]],
                             sup_info: SupplyColumnInfo,
                             vdd_core_table: Dict[int, List[WireArray]],
                             vss_table: Dict[int, List[WireArray]],
                             ridx_p: int, ridx_n: int) -> int:
     tap_info_list = []
     ncol = sup_info.ncol
     max_col = col
     for idx, (cur_col, flip_lr) in enumerate(col_list):
         col_offset = col + cur_col
         anchor = col_offset + int(flip_lr) * ncol
         tap_info_list.append((col_offset, flip_lr))
         self.add_supply_column(sup_info, anchor, vdd_core_table, vss_table,
                                ridx_p=ridx_p, ridx_n=ridx_n, extend_vdd=False, flip_lr=flip_lr,
                                extend_vss=False, min_len_mode=MinLenMode.MIDDLE)
         max_col = max(max_col, col_offset + ncol)
     self._tap_info = ImmutableList(tap_info_list)
     return max_col
Пример #5
0
    def __init__(self,
                 spec_file: str,
                 spec_dict: Optional[Dict[str, Any]] = None) -> None:
        if spec_dict:
            self._specs = spec_dict
            self._root_dir: Path = Path(self._specs['root_dir']).resolve()
        elif spec_file:
            spec_path = Path(spec_file).resolve()
            if spec_path.is_file():
                self._specs = read_yaml(spec_path)
                self._root_dir: Path = Path(self._specs['root_dir']).resolve()
            elif spec_path.is_dir():
                self._root_dir: Path = spec_path
                self._specs = read_yaml(self._root_dir / 'specs.yaml')
            else:
                raise ValueError(
                    f'{spec_path} is neither data directory or specification file.'
                )
        else:
            raise ValueError('spec_file is empty.')

        self._swp_var_list: ImmutableList[str] = ImmutableList(
            sorted(self._specs['sweep_params'].keys()))
        self._sweep_params = self._specs['sweep_params']
Пример #6
0
    def _add_ext_row(self, grid: RoutingGrid, tech: MOSTech, lch: int, fg: int,
                     re_bot: RowExtInfo, re_top: RowExtInfo,
                     be_bot: List[BlkExtInfo], be_top: List[BlkExtInfo],
                     dx: int, y0: int, ext_h: int, w_edge: int, w_tot: int,
                     prefix: str) -> None:
        arr_options = tech.arr_options

        cut_mode, bot_exty, top_exty = tech.get_extension_regions(
            re_bot, re_top, ext_h)
        if cut_mode.num_cut == 2 and bot_exty == top_exty == 0:
            if be_bot[0].guard_ring and be_bot[0].fg_dev[0][1] is be_top[
                    0].fg_dev[0][1]:
                # this is extension within a guard ring
                if len(be_bot) > 1:
                    fg_edge = be_bot[0].fg
                    fg_gr = fg_edge + be_bot[1].fg_dev[0][0]
                else:
                    fg_edge = be_top[0].fg
                    fg_gr = fg_edge + be_top[1].fg_dev[0][0]

                ext_dx = fg_gr * tech.sd_pitch
                ext_params = dict(lch=lch,
                                  num_cols=fg - 2 * fg_gr,
                                  height=ext_h,
                                  bot_info=re_bot,
                                  top_info=re_top,
                                  gr_info=(fg_edge, fg_gr),
                                  arr_options=arr_options)
                ext_master = self.new_template(MOSExt,
                                               params=ext_params,
                                               grid=grid)

                # add guard ring
                gr_params = dict(lch=lch,
                                 num_cols=fg_gr,
                                 edge_cols=fg_edge,
                                 height=ext_h,
                                 bot_info=re_bot,
                                 top_info=re_top,
                                 sub_type=be_bot[0].fg_dev[0][1],
                                 einfo=ext_master.edge_info,
                                 arr_options=arr_options)
                gr_master = self.new_template(MOSExtGR,
                                              params=gr_params,
                                              grid=grid)
                edge_info = gr_master.edge_info

                self.add_instance(gr_master,
                                  inst_name=f'{prefix}EXGRL',
                                  xform=Transform(dx, y0))
                self.add_instance(gr_master,
                                  inst_name=f'{prefix}EXGRR',
                                  xform=Transform(w_tot - dx, y0,
                                                  Orientation.MY))
            else:
                ext_dx = 0
                ext_params = dict(lch=lch,
                                  num_cols=fg,
                                  height=ext_h,
                                  bot_info=re_bot,
                                  top_info=re_top,
                                  gr_info=(0, 0),
                                  arr_options=arr_options)
                ext_master = self.new_template(MOSExt,
                                               params=ext_params,
                                               grid=grid)
                edge_info = ext_master.edge_info

            self.add_instance(ext_master,
                              inst_name=f'{prefix}EX',
                              xform=Transform(dx + ext_dx, y0))
            edge_master = self.new_template(MOSExtEdge,
                                            params=dict(
                                                lch=lch,
                                                blk_w=w_edge,
                                                einfo=edge_info,
                                                arr_options=arr_options))
            self.add_instance(edge_master,
                              inst_name=f'{prefix}EXEDGEL',
                              xform=Transform(0, y0))
            self.add_instance(edge_master,
                              inst_name=f'{prefix}EXEDGER',
                              xform=Transform(w_tot, y0, Orientation.MY))
        else:
            # draw extension geometries
            info = tech.get_ext_geometries(re_bot, re_top,
                                           ImmutableList(be_bot),
                                           ImmutableList(be_top), cut_mode,
                                           bot_exty, top_exty, dx, y0, w_edge)
            draw_layout_in_template(self, info, set_bbox=False)
Пример #7
0
 def get_info(self, bnd_box: BBox) -> LayoutInfo:
     return LayoutInfo(ImmutableSortedDict(self._lp_dict),
                       ImmutableList(self._warr_list),
                       ImmutableList(self._via_list), bnd_box)
Пример #8
0
 def __init__(self, temp_db: TemplateDB, params: Param, **kwargs: Any) -> None:
     MOSBase.__init__(self, temp_db, params, **kwargs)
     self._core_ncol: int = 0
     self._lvshift_right_ncol: int = 0
     self._tap_info: ImmutableList[Tuple[int, bool]] = ImmutableList()
Пример #9
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,
        )
Пример #10
0
 def __init__(self, temp_db: TemplateDB, params: Param,
              **kwargs: Any) -> None:
     MOSBase.__init__(self, temp_db, params, **kwargs)
     self._tap_columns: ImmutableList[Tuple[int, bool]] = ImmutableList()
Пример #11
0
    def get_mos_space_info(self, row_info: MOSRowInfo, num_cols: int, left_info: MOSEdgeInfo,
                           right_info: MOSEdgeInfo) -> MOSLayInfo:
        lch = self.lch
        sd_pitch = self.sd_pitch
        od_po_extx = self.od_po_extx

        if num_cols < self.min_sep_col and left_info and right_info:
            # cannot draw less than min_sep_col between two devices
            raise ODImplantEnclosureError(f'Cannot draw MOSSpace in {num_cols} columns.')

        imp_od_encx: int = self.mos_config['imp_od_encx']

        mos_lay_table = self.tech_info.config['mos_lay_table']
        po_lp = mos_lay_table['PO_DUMMY']
        pode_lp = mos_lay_table['PO_PODE']

        blk_xr = num_cols * sd_pitch
        blk_yt = row_info.height
        bbox = BBox(0, 0, blk_xr, blk_yt)

        row_type = row_info.row_type
        threshold = row_info.threshold

        builder = LayoutInfoBuilder()

        po_y = (0, blk_yt)
        od_extx = od_po_extx - (sd_pitch - lch) // 2
        if left_info.get('has_od', False):
            self._add_po_array(builder, pode_lp, po_y, 0, 1)
            po_start = 1
            imp_xmin = od_extx + imp_od_encx
        else:
            po_start = 0
            imp_xmin = 0

        if right_info.get('has_od', False):
            self._add_po_array(builder, pode_lp, po_y, num_cols - 1, num_cols)
            po_stop = num_cols - 1
            imp_xmax = blk_xr - od_extx - imp_od_encx
        else:
            po_stop = num_cols
            imp_xmax = blk_xr

        self._add_po_array(builder, po_lp, (0, blk_yt), po_start, po_stop)
        self._add_fb(builder, bbox)

        if left_info:
            typel = left_info['mos_type']
            if right_info:
                typer = right_info['mos_type']
            else:
                typer = typel
        else:
            if right_info:
                typer = right_info['mos_type']
            else:
                typer = row_type
            typel = typer

        guard_ring = ((typel.is_substrate and typel is not row_type.sub_type) or
                      (typer.is_substrate and typer is not row_type.sub_type))
        if typel == typer:
            if guard_ring:
                raise ValueError('Cannot have empty spaces between guard ring edges.')

            blk_rect = BBox(0, 0, blk_xr, blk_yt)
            for lay_purp in self._thres_imp_well_layers_iter(row_type, typel, threshold):
                builder.add_rect_arr(lay_purp, blk_rect)
            be = BlkExtInfo(row_type, threshold, False, ImmutableList([(num_cols, typel)]),
                            ImmutableSortedDict())
            edgel = edger = MOSEdgeInfo(mos_type=typel, has_od=False, is_sub=False)
        else:
            # must be in transistor row, and has one substrate
            if typel.is_substrate and ((not typer.is_substrate) or typel is not row_type.sub_type):
                xmid = od_extx + imp_od_encx
            else:
                xmid = blk_xr - od_extx - imp_od_encx

            if xmid < imp_xmin or xmid > imp_xmax:
                raise ODImplantEnclosureError('Insufficient space to satisfy implant-OD '
                                              'horizontal enclosure.')

            rectl = BBox(0, 0, xmid, blk_yt)
            rectr = BBox(xmid, 0, blk_xr, blk_yt)
            for lay_purp in self._thres_imp_well_layers_iter(row_type, typel, threshold):
                builder.add_rect_arr(lay_purp, rectl)
            for lay_purp in self._thres_imp_well_layers_iter(row_type, typer, threshold):
                builder.add_rect_arr(lay_purp, rectr)

            fgl = xmid // sd_pitch
            be = BlkExtInfo(row_type, threshold, guard_ring,
                            ImmutableList([(fgl, typel), (num_cols - fgl, typer)]),
                            ImmutableSortedDict())
            edgel = MOSEdgeInfo(mos_type=typel, has_od=False, is_sub=False)
            edger = MOSEdgeInfo(mos_type=typer, has_od=False, is_sub=False)

        wire_info = (0, 0, 0)
        return MOSLayInfo(builder.get_info(bbox), edgel, edger, be, be,
                          g_info=wire_info, d_info=wire_info, s_info=wire_info,
                          shorted_ports=ImmutableList())
Пример #12
0
    def get_mos_tap_info(self, row_info: MOSRowInfo, conn_layer: int, seg: int,
                         options: Param) -> MOSLayInfo:
        row_type = row_info.row_type

        guard_ring: bool = options.get('guard_ring', row_info.guard_ring)
        if guard_ring:
            sub_type: MOSType = options.get('sub_type', row_type.sub_type)
        else:
            sub_type: MOSType = row_type.sub_type

        lch = self.lch
        sd_pitch = self.sd_pitch

        mp_h: int = self.mos_config['mp_h']
        mp_po_extx: int = self.mos_config['mp_po_extx']
        md_w: int = self.mos_config['md_w']

        mos_lay_table = self.tech_info.config['mos_lay_table']
        mp_lp = mos_lay_table['MP']

        g_info = self.get_conn_info(1, True)
        d_info = self.get_conn_info(1, False)

        threshold = row_info.threshold
        ds_yt = row_info.ds_conn_y[1]
        mp_yb, mp_yt = row_info['mp_y']
        md_yb, md_yt = row_info['md_y']
        md_yc = (md_yt + md_yb) // 2
        ds_yb = md_yc - (ds_yt - md_yc)

        fg = seg
        num_wire = seg + 1
        num_po = num_wire + 1

        builder = LayoutInfoBuilder()
        bbox = self._get_mos_active_rect_list(builder, row_info, fg, row_info.sub_width, sub_type)

        # Connect gate to MP
        mp_yc = (mp_yb + mp_yt) // 2
        mp_delta = (sd_pitch + lch) // 2 + mp_po_extx
        if num_po & 1:
            num_vg = num_po // 2
            if num_vg & 1:
                # we have 3 PO left over in the middle
                num_vg2 = (num_vg - 1) // 2
                num_vgm = 2
            else:
                # we have 5 PO left over in the middle
                num_vg2 = (num_vg - 2) // 2
                num_vgm = 4
            # draw middle vg
            vgm_x = bbox.xm - ((num_vgm - 1) * sd_pitch) // 2
            mp_xl = vgm_x - mp_delta
            mp_xr = vgm_x + (num_vgm - 1) * sd_pitch + mp_delta
            builder.add_rect_arr(mp_lp, BBox(mp_xl, mp_yb, mp_xr, mp_yt))
            builder.add_via(g_info.get_via_info('M1_LiPo', vgm_x, mp_yc, mp_h,
                                                nx=num_vgm, spx=sd_pitch))
            # draw left/right vg
            if num_vg2 > 0:
                vg_pitch = 2 * sd_pitch

                def _add_vg_half(vg_x: int) -> None:
                    xl = vg_x - mp_delta
                    xr = vg_x + mp_delta
                    builder.add_rect_arr(mp_lp, BBox(xl, mp_yb, xr, mp_yt),
                                         nx=num_vg2, spx=vg_pitch)
                    builder.add_via(g_info.get_via_info('M1_LiPo', vg_x, mp_yc, mp_h,
                                                        nx=num_vg2, spx=vg_pitch))

                _add_vg_half(0)
                _add_vg_half((num_wire - 1) * sd_pitch - (num_vg2 - 1) * vg_pitch)
        else:
            # even number of PO, can connect pair-wise
            num_vg = num_po // 2
            vg_pitch = 2 * sd_pitch
            builder.add_rect_arr(mp_lp, BBox(-mp_delta, mp_yb, mp_delta, mp_yt),
                                 nx=num_vg, spx=vg_pitch)
            builder.add_via(g_info.get_via_info('M1_LiPo', 0, mp_yc, mp_h,
                                                nx=num_vg, spx=vg_pitch))

        # connect drain/source to M1
        m1_yc = (md_yb + md_yt) // 2
        via_pitch = d_info.via_h + d_info.via_sp

        vnum_bot = (md_yt - md_yb - d_info.via_bot_enc * 2 + d_info.via_sp) // via_pitch
        vnum_top = (ds_yt - ds_yb - d_info.via_top_enc * 2 + d_info.via_sp) // via_pitch
        vnum = min(vnum_top, vnum_bot)
        builder.add_via(d_info.get_via_info('M1_LiAct', 0, m1_yc, md_w, ortho=False,
                                            num=vnum, nx=num_wire, spx=sd_pitch))

        edge_info = MOSEdgeInfo(mos_type=sub_type, has_od=True, is_sub=True)
        be = BlkExtInfo(row_type, threshold, guard_ring, ImmutableList([(fg, sub_type)]),
                        ImmutableSortedDict())
        wire_info = (0, num_wire, sd_pitch)
        return MOSLayInfo(builder.get_info(bbox), edge_info, edge_info, be, be,
                          g_info=wire_info, d_info=wire_info, s_info=wire_info,
                          shorted_ports=ImmutableList())
Пример #13
0
    def get_mos_conn_info(self, row_info: MOSRowInfo, conn_layer: int, seg: int, w: int, stack: int,
                          g_on_s: bool, options: Param) -> MOSLayInfo:
        assert conn_layer == 1, 'currently only work for conn_layer = 1'

        lch = self.lch
        sd_pitch = self.sd_pitch

        mp_h: int = self.mos_config['mp_h']
        mp_po_extx: int = self.mos_config['mp_po_extx']
        md_w: int = self.mos_config['md_w']

        mos_lay_table = self.tech_info.config['mos_lay_table']
        mp_lp = mos_lay_table['MP']

        g_info = self.get_conn_info(1, True)
        d_info = self.get_conn_info(1, False)

        export_mid = options.get('export_mid', False)
        export_mid = export_mid and stack == 2

        row_type = row_info.row_type
        ds_yb, ds_yt = row_info.ds_conn_y
        threshold = row_info.threshold
        mp_yb, mp_yt = row_info['mp_y']
        md_yb, md_yt = row_info['md_y']

        fg = seg * stack
        wire_pitch = stack * sd_pitch
        conn_pitch = 2 * wire_pitch
        num_s = seg // 2 + 1
        num_d = (seg + 1) // 2
        s_xc = 0
        d_xc = wire_pitch
        if g_on_s:
            num_g = fg // 2 + 1
            g_xc = 0
        else:
            num_g = (fg + 1) // 2
            g_xc = sd_pitch
        g_pitch = 2 * sd_pitch

        builder = LayoutInfoBuilder()
        bbox = self._get_mos_active_rect_list(builder, row_info, fg, w, row_info.row_type)

        # Connect gate to MP
        mp_po_dx = (sd_pitch + lch) // 2 + mp_po_extx
        builder.add_rect_arr(mp_lp, BBox(g_xc - mp_po_dx, mp_yb, g_xc + mp_po_dx, mp_yt),
                             nx=num_g, spx=g_pitch)

        # connect gate to M1.
        mp_yc = (mp_yb + mp_yt) // 2
        builder.add_via(g_info.get_via_info('M1_LiPo', g_xc, mp_yc, mp_h,
                                            nx=num_g, spx=g_pitch))

        # connect drain/source to M1
        m1_yc = (md_yb + md_yt) // 2
        via_pitch = d_info.via_h + d_info.via_sp
        vnum1 = (md_yt - md_yb - d_info.via_bot_enc * 2 + d_info.via_sp) // via_pitch
        vnum2 = (ds_yt - ds_yb - d_info.via_top_enc * 2 + d_info.via_sp) // via_pitch
        vnum = min(vnum1, vnum2)
        builder.add_via(d_info.get_via_info('M1_LiAct', d_xc, m1_yc, md_w, ortho=False,
                                            num=vnum, nx=num_d, spx=conn_pitch))
        builder.add_via(d_info.get_via_info('M1_LiAct', s_xc, m1_yc, md_w, ortho=False,
                                            num=vnum, nx=num_s, spx=conn_pitch))
        if export_mid:
            m_xc = sd_pitch
            num_m = fg + 1 - num_s - num_d
            m_info = (m_xc, num_m, wire_pitch)
            builder.add_via(d_info.get_via_info('M1_LiAct', m_xc, m1_yc, md_w, ortho=False,
                                                num=vnum, nx=num_m, spx=wire_pitch))
        else:
            m_info = None

        edge_info = MOSEdgeInfo(mos_type=row_type, has_od=True, is_sub=False)
        be = BlkExtInfo(row_type, threshold, False, ImmutableList([(fg, row_type)]),
                        ImmutableSortedDict())
        return MOSLayInfo(builder.get_info(bbox), edge_info, edge_info, be, be,
                          g_info=(g_xc, num_g, g_pitch), d_info=(d_xc, num_d, conn_pitch),
                          s_info=(s_xc, num_s, conn_pitch), m_info=m_info,
                          shorted_ports=ImmutableList([MOSPortType.G]))
Пример #14
0
def _place_rows(tr_manager: TrackManager, tech_cls: MOSTech, pspecs_list: Sequence[RowPlaceSpecs],
                tot_height_min: int, tot_height_pitch: int, bot_mirror: bool, top_mirror: bool,
                hm_shift: Union[int, HalfInt] = 0, max_iter: int = 100
                ) -> Tuple[ImmutableList[RowPlaceInfo], List[Tuple[WireGraph, WireGraph]]]:
    grid = tr_manager.grid
    blk_h_pitch = tech_cls.blk_h_pitch
    conn_layer = tech_cls.conn_layer
    hm_layer = conn_layer + 1
    conn_sp_le = grid.get_line_end_space(conn_layer, 1)

    num_rows = len(pspecs_list)
    prev_ext_info: Optional[RowExtInfo] = None
    prev_ext_h = 0
    ytop_prev = 0
    ytop_conn_prev = COORD_MIN
    pinfo_list = []
    prev_wg: Optional[WireGraph] = None
    row_graph_list: List[Tuple[WireGraph, WireGraph]] = []
    # first pass: determine Y coordinates of each row.
    for idx, pspecs in enumerate(pspecs_list):
        bot_ext_info = pspecs.bot_ext_info
        top_ext_info = pspecs.top_ext_info
        bot_conn_y_table = pspecs.bot_conn_y_table
        top_conn_y_table = pspecs.top_conn_y_table

        row_specs = pspecs.row_specs
        ignore_bot_vm_sp_le = row_specs.ignore_bot_vm_sp_le
        ignore_top_vm_sp_le = row_specs.ignore_top_vm_sp_le

        row_info = pspecs.row_info
        blk_h = row_info.height

        bot_wg = WireGraph.make_wire_graph(hm_layer, tr_manager, row_specs.bot_wires)
        top_wg = WireGraph.make_wire_graph(hm_layer, tr_manager, row_specs.top_wires)
        row_graph_list.append((bot_wg, top_wg))
        if idx == 0:
            # first row, place bottom wires using mirror/shift constraints
            bot_wg.place_compact(hm_layer, tr_manager, bot_mirror=bot_mirror, shift=hm_shift)
        else:
            # subsequent rows, place bottom wires using previous wire graph
            bot_wg.place_compact(hm_layer, tr_manager, prev_wg=prev_wg)

        bnd_table = bot_wg.get_placement_bounds(hm_layer, grid)

        # find Y coordinate of MOS row
        ycur = ytop_prev
        for conn_name, (_, (tr1, w1)) in bnd_table.items():
            # compute ycur so that we can connect to top bottom wire
            vm_vext = grid.get_via_extensions(Direction.LOWER, conn_layer, 1, w1)[0]
            yw = grid.get_wire_bounds(hm_layer, tr1, width=w1)[1]
            ycur = max(ycur, yw + vm_vext - bot_conn_y_table[MOSWireType[conn_name]][1])

        ycur = -(-ycur // blk_h_pitch) * blk_h_pitch
        # make sure extension region is legal
        if idx == 0:
            if bot_mirror:
                ext_w_info = tech_cls.get_ext_width_info(bot_ext_info, bot_ext_info,
                                                         ignore_vm_sp_le=ignore_bot_vm_sp_le)
                ycur, _ = _place_mirror(tech_cls, bot_ext_info, ycur,
                                        ignore_vm_sp_le=ignore_bot_vm_sp_le)
            else:
                ext_w_info = ExtWidthInfo([], 0)
        else:
            ext_w_info = tech_cls.get_ext_width_info(prev_ext_info, bot_ext_info,
                                                     ignore_vm_sp_le=ignore_bot_vm_sp_le)
            cur_bot_ext_h = (ycur - ytop_prev) // blk_h_pitch
            # make sure extension height is valid
            ext_h = ext_w_info.get_next_width(prev_ext_h + cur_bot_ext_h)
            cur_bot_ext_h = ext_h - prev_ext_h
            ycur = ytop_prev + blk_h_pitch * cur_bot_ext_h

        # align bottom wires with placement constraints
        pcons = {wtype.name: ycur + conn_y[1] for wtype, conn_y in bot_conn_y_table.items()}
        bot_wg.align_wires(hm_layer, tr_manager, ytop_prev, ycur + blk_h,
                           top_pcons=PlaceFun(conn_layer, grid, pcons, RoundMode.LESS_EQ))
        ytop_conn_prev_shared = bot_wg.get_shared_conn_y(hm_layer, grid, False)
        ytop_conn_prev = max(ytop_conn_prev, ytop_conn_prev_shared)

        if ytop_conn_prev != COORD_MIN and not ignore_bot_vm_sp_le:
            dy = _calc_vm_dy(grid, row_info, bot_wg, bot_conn_y_table, conn_layer, ycur,
                             ytop_conn_prev, conn_sp_le)
            cnt = 0
            while dy > 0 and cnt < max_iter:
                ycur = -(-(ycur + dy) // blk_h_pitch) * blk_h_pitch
                # need to fix extension height again
                cur_bot_ext_h = (ycur - ytop_prev) // blk_h_pitch
                ext_h = ext_w_info.get_next_width(prev_ext_h + cur_bot_ext_h)
                cur_bot_ext_h = ext_h - prev_ext_h
                ycur = ytop_prev + blk_h_pitch * cur_bot_ext_h
                # re-align bottom wires with placement constraints
                pcons = {wtype.name: ycur + conn_y[1] for wtype, conn_y in bot_conn_y_table.items()}
                bot_wg.align_wires(hm_layer, tr_manager, ytop_prev, ycur + blk_h,
                                   top_pcons=PlaceFun(conn_layer, grid, pcons, RoundMode.LESS_EQ))

                # try again
                dy = _calc_vm_dy(grid, row_info, bot_wg, bot_conn_y_table, conn_layer, ycur,
                                 ytop_conn_prev, conn_sp_le)
                cnt += 1
            if dy > 0:
                raise ValueError('maximum iteration reached, still cannot resolve line-end spacing')

        # get bottom conn_layer Y coordinates
        conn_y_bnds_bot = [COORD_MAX, COORD_MIN]
        for wtype, conn_y in bot_conn_y_table.items():
            if wtype.is_physical:
                conn_y_bnds_bot[0] = min(conn_y_bnds_bot[0], ycur + conn_y[0])
                conn_y_bnds_bot[1] = max(conn_y_bnds_bot[1], ycur + conn_y[1])
        bnd_table = bot_wg.get_placement_bounds(hm_layer, grid)
        conn_y_bnds_bot[0] = _update_y_conn(grid, row_info, conn_layer, ycur, bnd_table,
                                            conn_y_bnds_bot[0], False)
        conn_y_bnds_bot[1] = _update_y_conn(grid, row_info, conn_layer, ycur, bnd_table,
                                            conn_y_bnds_bot[1], True)
        # place top wires
        is_top_row = (idx == num_rows - 1)
        conn_y_bnds_top = [COORD_MAX, COORD_MIN]
        pcons = {}
        for wtype, conn_y in top_conn_y_table.items():
            pcons[wtype.name] = ycur + conn_y[0]
            if wtype.is_physical:
                conn_y_bnds_top[0] = min(conn_y_bnds_top[0], ycur + conn_y[0])
                conn_y_bnds_top[1] = max(conn_y_bnds_top[1], ycur + conn_y[1])

        if bot_wg:
            prev_wg = bot_wg
        if top_wg:
            top_wg.place_compact(hm_layer, tr_manager,
                                 pcons=PlaceFun(conn_layer, grid, pcons, RoundMode.GREATER_EQ),
                                 prev_wg=prev_wg, top_mirror=top_mirror and is_top_row,
                                 ytop_conn=max(conn_y_bnds_bot[1], conn_y_bnds_top[1]))
            if row_info.row_type.is_substrate:
                top_wg.align_wires(hm_layer, tr_manager, ytop_prev, ycur + blk_h, top_pcons=None)

        # get ytop_conn
        bnd_table = top_wg.get_placement_bounds(hm_layer, grid)
        conn_y_bnds_top[0] = _update_y_conn(grid, row_info, conn_layer, ycur, bnd_table,
                                            conn_y_bnds_top[0], False)
        conn_y_bnds_top[1] = _update_y_conn(grid, row_info, conn_layer, ycur, bnd_table,
                                            conn_y_bnds_top[1], True)

        # compute ytop
        ytop_blk = ycur + blk_h
        ytop = max(ytop_blk, top_wg.upper)
        row_h_cur = -(-(ytop - ytop_prev) // blk_h_pitch) * blk_h_pitch
        ytop = ytop_prev + row_h_cur
        if is_top_row:
            # last row
            # update ytop
            ytop = max(ytop, tot_height_min)
            ytop = -(-ytop // tot_height_pitch) * tot_height_pitch
            # fix extension constraint
            if top_mirror:
                pass_mirror = False
                while not pass_mirror:
                    ytop_delta = ytop - ytop_blk
                    ytop_delta2, cur_top_ext_h = _place_mirror(tech_cls, top_ext_info, ytop_delta,
                                                               ignore_vm_sp_le=ignore_top_vm_sp_le)
                    if ytop_delta2 != ytop - ytop_blk:
                        # need more room for extension region on top, make the row taller
                        ytop += ytop_delta2 - ytop_delta
                        ytop = -(-ytop // tot_height_pitch) * tot_height_pitch
                    else:
                        pass_mirror = True
        cur_top_ext_h = (ytop - ytop_blk) // blk_h_pitch

        # update upper coordinate for top wire graph
        top_wg.set_upper(hm_layer, tr_manager, ytop)
        ybot_conn = min(conn_y_bnds_bot[0], conn_y_bnds_top[0])
        ytop_conn = max(conn_y_bnds_bot[1], conn_y_bnds_top[1])
        pinfo_list.append(RowPlaceInfo(row_info, bot_wg.get_wire_lookup(), top_wg.get_wire_lookup(),
                                       ytop_prev, ytop, ycur, ytop_blk, (ybot_conn, ytop_conn)))

        # update previous row information
        ytop_prev = ytop
        ytop_conn_prev = ytop_conn
        prev_ext_info = top_ext_info
        prev_ext_h = cur_top_ext_h
        if top_wg:
            prev_wg = top_wg

    return ImmutableList(pinfo_list), row_graph_list