Esempio n. 1
0
def draw_layout_in_template(template: TemplateBase,
                            lay_info: LayoutInfo,
                            set_bbox: bool = True) -> None:
    for lay_purp, box_col in lay_info.rect_dict.items():
        template.add_bbox_collection(lay_purp, box_col)

    for winfo in lay_info.warr_list:
        template.add_wires(winfo.layer,
                           winfo.track,
                           winfo.lower,
                           winfo.upper,
                           width=winfo.width,
                           num=winfo.num,
                           pitch=winfo.pitch)

    for vinfo in lay_info.via_list:
        template.add_via_primitive(vinfo.via_type,
                                   Transform(vinfo.xc, vinfo.yc),
                                   vinfo.w,
                                   vinfo.h,
                                   num_rows=vinfo.vny,
                                   num_cols=vinfo.vnx,
                                   sp_rows=vinfo.vspy,
                                   sp_cols=vinfo.vspx,
                                   enc1=vinfo.enc1,
                                   enc2=vinfo.enc2,
                                   nx=vinfo.nx,
                                   ny=vinfo.ny,
                                   spx=vinfo.spx,
                                   spy=vinfo.spy)

    if set_bbox:
        template.prim_bound_box = lay_info.bound_box
Esempio n. 2
0
    def _add_mos_space(self, pinfo: MOSBasePlaceInfo, used_arr: MOSUsedArray,
                       sd_pitch: int, dx: int, dy: int, tile_idx: int,
                       row_idx: int, start: int, seg: int, left: MOSEdgeInfo,
                       right: MOSEdgeInfo) -> None:
        params = dict(
            row_info=pinfo.get_row_place_info(row_idx).row_info,
            num_cols=seg,
            left_info=left,
            right_info=right,
            arr_options=pinfo.arr_info.arr_options,
        )
        try:
            master = self.new_template(MOSSpace,
                                       params=params,
                                       grid=pinfo.grid)
        except ODImplantEnclosureError as err:
            raise ValueError('Not enough space for OD-implant enclosure.\n'
                             f'Error on tile {tile_idx}, row {row_idx}, '
                             f'column [{start}, {start + seg})') from err

        y0, orient = MOSBase.register_device(
            used_arr, tile_idx, row_idx, start, seg, False, master.left_info,
            master.right_info, master.top_info, master.bottom_info, None)

        x0 = sd_pitch * start
        self.add_instance(master,
                          inst_name=f'XT{tile_idx}R{row_idx}C{start}',
                          xform=Transform(x0 + dx, y0 + dy, orient))
Esempio n. 3
0
    def add_tile(self, master: MOSBase, tile_idx: int, col_idx: int, *,
                 flip_lr: bool = False, commit: bool = True) -> PyLayInstance:
        tile_idx = self._tile_check(tile_idx)

        abut_list = []
        try:
            self._used_arr.add_tiles(tile_idx, col_idx, master.used_array, flip_lr, abut_list)
        except ValueError as err:
            msg = (f'Error adding {master.get_layout_basename()} to tile '
                   f'{tile_idx}, column {col_idx}, flip_lr = {flip_lr}')
            raise ValueError(msg) from err

        pinfo, y0, flip_tile = self.get_tile_info(tile_idx)

        self._handle_abutment(abut_list)
        if flip_tile ^ master.flipped:
            orient = Orientation.MX
            y0 += pinfo.height
        else:
            orient = Orientation.R0

        if flip_lr:
            orient = orient.flip_lr()

        x0 = col_idx * self.sd_pitch
        return self.add_instance(master, inst_name=f'XT{tile_idx}C{col_idx}',
                                 xform=Transform(x0, y0, orient), commit=commit)
Esempio n. 4
0
 def _add_row_edges(self, w_edge: int, w_tot: int, y: int,
                    orient: Orientation, row_info: MOSRowInfo,
                    left_info: MOSEdgeInfo, right_info: MOSEdgeInfo,
                    prefix: str, arr_options: Mapping[str, Any]) -> None:
     left_master = self.new_template(MOSRowEdge,
                                     params=dict(blk_w=w_edge,
                                                 rinfo=row_info,
                                                 einfo=left_info,
                                                 arr_options=arr_options))
     right_master = self.new_template(MOSRowEdge,
                                      params=dict(blk_w=w_edge,
                                                  rinfo=row_info,
                                                  einfo=right_info,
                                                  arr_options=arr_options))
     self.add_instance(left_master,
                       inst_name=f'{prefix}EGL',
                       xform=Transform(0, y, orient))
     self.add_instance(right_master,
                       inst_name=f'{prefix}EGR',
                       xform=Transform(w_tot, y, orient.flip_lr()))
Esempio n. 5
0
def test_transform(barr1_info, dx, dy, orient, barr2_info):
    barr1 = make_bbox_array(barr1_info)
    barr2 = make_bbox_array(barr2_info)
    xform = Transform(dx, dy, Orientation[orient])
    a = barr1.get_transform(xform)
    assert a == barr2
    assert a is not barr1

    b = barr1.transform(xform)
    assert barr1 == barr2
    assert b is barr1
Esempio n. 6
0
def test_transform(box0, dx, dy, orient, box1):
    xform = Transform(dx, dy, Orientation[orient])
    a = BBox(box0[0], box0[1], box0[2], box0[3])
    ans = BBox(box1[0], box1[1], box1[2], box1[3])

    b = a.get_transform(xform)
    assert b == ans
    assert b is not a
    c = a.transform(xform)
    assert a == ans
    assert c is a
Esempio n. 7
0
    def add_mos(self, row_idx: int, col_idx: int, seg: int, *, tile_idx: int = 0, w: int = 0,
                g_on_s: bool = False, stack: int = 1, flip_lr: bool = False,
                **kwargs: Any) -> MOSPorts:
        if seg <= 0:
            raise ValueError('Cannot draw non-positive segments.')

        pinfo = self.get_tile_pinfo(tile_idx)
        row_idx = _row_check(pinfo, row_idx)
        rpinfo = pinfo.get_row_place_info(row_idx)
        row_info = rpinfo.row_info
        row_type = row_info.row_type
        w_max = row_info.width

        if row_type.is_substrate:
            raise ValueError('Cannot draw transistors in substrate row.')
        if w == 0:
            w = w_max
        elif w > w_max:
            raise ValueError(f'Cannot create transistor with w > {w_max}')

        conn_layer = pinfo.conn_layer

        fg = seg * stack

        # create connection master
        params = dict(
            row_info=row_info,
            conn_layer=conn_layer,
            seg=seg,
            w=w,
            stack=stack,
            g_on_s=g_on_s,
            options=kwargs,
            arr_options=self.arr_info.arr_options,
        )
        master = self.new_template(MOSConn, params=params)

        abut_list = []
        y0, orient = self.register_device(self._used_arr, tile_idx, row_idx,
                                          col_idx, fg, flip_lr,
                                          master.left_info, master.right_info,
                                          master.top_info, master.bottom_info, abut_list)
        self._handle_abutment(abut_list)

        # compute instance transform
        x0 = col_idx * self.sd_pitch
        inst = self.add_instance(master, inst_name=f'XT{tile_idx}R{row_idx}C{col_idx}',
                                 xform=Transform(x0, y0, orient))

        # construct port object
        m_pin = inst.get_pin('m') if inst.has_port('m') else None
        return MOSPorts(inst.get_pin('g'), inst.get_pin('d'), inst.get_pin('s'),
                        master.shorted_ports, m=m_pin)
Esempio n. 8
0
 def _add_mos_end(self, mos_grid: RoutingGrid, lch: int, dx: int, y: int,
                  orient: Orientation, blk_h: int, einfo: RowExtInfo,
                  fg: int, prefix: str,
                  arr_options: Mapping[str, Any]) -> MOSEdgeInfo:
     master = self.new_template(MOSEnd,
                                params=dict(
                                    lch=lch,
                                    blk_h=blk_h,
                                    num_cols=fg,
                                    einfo=einfo,
                                    arr_options=arr_options,
                                ),
                                grid=mos_grid)
     self.add_instance(master,
                       inst_name=prefix,
                       xform=Transform(dx, y, orient))
     return master.edge_info
Esempio n. 9
0
    def draw_boundaries(self,
                        master: MOSBase,
                        top_layer: int,
                        *,
                        half_blk_x: bool = True,
                        half_blk_y: bool = True) -> PyLayInstance:
        self._core = master

        tech_cls = master.tech_cls
        bbox = master.bound_box
        used_arr = master.used_array

        w_blk, h_blk = self.grid.get_block_size(top_layer,
                                                half_blk_x=half_blk_x,
                                                half_blk_y=half_blk_y)

        w_master = bbox.w
        h_master = bbox.h
        w_edge = tech_cls.get_edge_width(w_master, w_blk)
        base_end_info = tech_cls.get_mos_base_end_info(master.place_info,
                                                       h_blk)

        # get top/bottom boundary delta/height
        num_tiles = used_arr.num_tiles
        idx_bot = int(used_arr.get_flip_tile(0))
        idx_top = int(not used_arr.get_flip_tile(num_tiles - 1))
        dy_bot = base_end_info.h_blk[idx_bot]
        dy_top = base_end_info.h_blk[idx_top]
        h_end_bot = base_end_info.h_mos_end[idx_bot]
        h_end_top = base_end_info.h_mos_end[idx_top]

        self._xform = Transform(w_edge, dy_bot)
        inst = self.add_instance(master, inst_name='X0', xform=self._xform)

        my_used_arr = used_arr.get_copy()
        sd_pitch = tech_cls.sd_pitch
        w_tot = w_edge * 2 + w_master
        h_tot = dy_bot + dy_top + h_master
        self._fill_space(master.grid, tech_cls, w_edge, my_used_arr, sd_pitch,
                         w_tot, h_tot, dy_bot, h_end_bot, h_end_top)

        self.set_size_from_bound_box(top_layer, BBox(0, 0, w_tot, h_tot))
        return inst
Esempio n. 10
0
    def add_substrate_contact(self, row_idx: int, col_idx: int, *, tile_idx: int = 0, seg: int = 0,
                              flip_lr: bool = False, port_mode: SubPortMode = SubPortMode.EVEN,
                              **kwargs: Any) -> WireArray:
        if seg <= 0:
            seg = self.min_sub_col

        tile_idx = self._tile_check(tile_idx)
        pinfo = self.get_tile_pinfo(tile_idx)
        row_idx = _row_check(pinfo, row_idx)
        rpinfo = pinfo.get_row_place_info(row_idx)
        row_info = rpinfo.row_info
        conn_layer = self.conn_layer

        # create connection master
        params = dict(
            row_info=row_info,
            conn_layer=conn_layer,
            seg=seg,
            options=kwargs,
            arr_options=self.arr_info.arr_options,
        )
        master = self.new_template(MOSTap, params=params)

        abut_list = []
        y0, orient = self.register_device(self._used_arr, tile_idx, row_idx,
                                          col_idx, seg, flip_lr,
                                          master.left_info, master.right_info,
                                          master.top_info, master.bottom_info, abut_list)
        self._handle_abutment(abut_list)

        # compute instance transform
        x0 = col_idx * self.sd_pitch

        inst = self.add_instance(master, inst_name=f'XT{tile_idx}R{row_idx}C{col_idx}',
                                 xform=Transform(x0, y0, orient))
        warr = inst.get_pin('sup')
        if port_mode is SubPortMode.BOTH:
            return warr
        elif port_mode is SubPortMode.EVEN:
            return warr[0::2]
        else:
            return warr[1::2]
Esempio n. 11
0
    def _handle_abutment(self, abut_list: List[MOSAbutInfo]) -> None:
        abut_mode = self.tech_cls.abut_mode
        if abut_mode is MOSAbutMode.NONE:
            return

        if abut_mode is MOSAbutMode.OVERLAY:
            used_arr = self._used_arr
            arr_options = self.arr_info.arr_options
            for info in abut_list:
                tile_idx, row_idx = used_arr.flat_row_to_tile_row(info.row_flat)
                pinfo, tile_yb, flip_tile = self.get_tile_info(tile_idx)
                row_info, y0, orient = self.get_mos_row_info(pinfo, tile_yb, flip_tile, row_idx)
                x0 = info.col * self.sd_pitch
                params = dict(row_info=row_info, edgel=info.edgel, edger=info.edger,
                              arr_options=arr_options)
                master = self.new_template(MOSAbut, params=params)
                self.add_instance(master, inst_name=f'XA{tile_idx}R{row_idx}C{info.col}',
                                  xform=Transform(x0, y0, orient))
        else:
            raise RuntimeError(f'MOSAbutMode {abut_mode} not implemented yet.')
Esempio n. 12
0
    def get_copy_with(
        self,
        top_ignore_lay: Optional[int] = None,
        top_private_lay: Optional[int] = None,
        tr_colors: Optional[TrackColoring] = None,
        tr_specs: Optional[List[Tuple[int, int, int, int, int]]] = None
    ) -> RoutingGrid:
        if top_ignore_lay is None:
            top_ignore_lay = self.top_ignore_layer
        if top_private_lay is None:
            top_private_lay = self.top_private_layer
        if tr_colors is None:
            tr_colors = self.get_track_coloring_at(self.bot_layer,
                                                   self.top_layer, Transform())
        if tr_specs is None:
            tr_specs = []

        new_grid = super(RoutingGrid,
                         self).get_copy_with(top_ignore_lay, top_private_lay,
                                             tr_colors, tr_specs)
        return self.__class__(self._tech_info, '', copy=new_grid)
Esempio n. 13
0
    def __init__(self, temp_db: TemplateDB, params: Param,
                 **kwargs: Any) -> None:
        TemplateBase.__init__(self, temp_db, params, **kwargs)

        self._core: Optional[MOSBase] = None
        self._xform: Transform = Transform()
Esempio n. 14
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)
Esempio n. 15
0
def test_default2():
    xform = Transform(3)
    assert xform.x == 3
    assert xform.y == 0
    assert xform.orient is Orientation.R0
Esempio n. 16
0
    def _fill_space(self, grid: RoutingGrid, tech_cls: MOSTech, w_edge: int,
                    used_arr: MOSUsedArray, sd_pitch: int, w_tot: int,
                    h_tot: int, dy: int, h_end_bot: int,
                    h_end_top: int) -> None:
        lch = tech_cls.lch
        arr_options = tech_cls.arr_options

        dx = w_edge
        num_tiles = used_arr.num_tiles
        num_cols = used_arr.num_cols
        last_flat_row_idx = used_arr.num_flat_rows - 1

        # add space blocks
        for tile_idx in range(num_tiles):
            pinfo = used_arr.get_tile_pinfo(tile_idx)
            for row_idx in range(pinfo.num_rows):
                for (start,
                     end), cur_left, cur_right in used_arr.get_complement(
                         tile_idx, row_idx, 0, num_cols):
                    seg = end - start
                    self._add_mos_space(pinfo, used_arr, sd_pitch, dx, dy,
                                        tile_idx, row_idx, start, seg,
                                        cur_left, cur_right)

        # draw edges and extensions
        for tile_idx in range(num_tiles):
            pinfo, tile_yb, flip_tile = used_arr.get_tile_info(tile_idx)
            if flip_tile:
                row_range = range(pinfo.num_rows - 1, -1, -1)
            else:
                row_range = range(pinfo.num_rows)

            tile_h = pinfo.height
            tile_last_row = row_range.stop - row_range.step
            for row_idx in row_range:
                flat_row_idx = used_arr.get_flat_row_idx_and_flip(
                    tile_idx, row_idx)[0]
                row_info, y_edge, orient_edge = MOSBase.get_mos_row_info(
                    pinfo, tile_yb, flip_tile, row_idx)
                # draw row edges
                rpinfo = pinfo.get_row_place_info(row_idx)
                self._add_row_edges(
                    w_edge, w_tot, y_edge + dy, orient_edge, row_info,
                    used_arr.get_edge_info(flat_row_idx, 0),
                    used_arr.get_edge_info(flat_row_idx, num_cols),
                    f'XR{flat_row_idx}', arr_options)

                if flat_row_idx != last_flat_row_idx:
                    # draw extensions between rows
                    be_bot = used_arr.get_top_info(flat_row_idx)
                    be_top = used_arr.get_bottom_info(flat_row_idx + 1)

                    # get bottom Y coordinate
                    yb_ext = self._get_row_yblk(rpinfo, flip_tile, tile_yb,
                                                tile_h)[1]

                    if row_idx == tile_last_row:
                        tmp = used_arr.get_tile_info(tile_idx + 1)
                        next_pinfo, next_tile_yb, next_flip_tile = tmp
                        next_row_idx = next_pinfo.num_rows - 1 if next_flip_tile else 0
                    else:
                        next_pinfo = pinfo
                        next_tile_yb = tile_yb
                        next_flip_tile = flip_tile
                        next_row_idx = row_idx + row_range.step

                    next_rpinfo = next_pinfo.get_row_place_info(next_row_idx)
                    next_ri = next_rpinfo.row_info
                    yt_ext = self._get_row_yblk(next_rpinfo, next_flip_tile,
                                                next_tile_yb,
                                                next_pinfo.height)[0]

                    ext_h = yt_ext - yb_ext
                    if row_info.flip == flip_tile:
                        re_bot = row_info.top_ext_info
                    else:
                        re_bot = row_info.bot_ext_info
                    if next_ri.flip == next_flip_tile:
                        re_top = next_ri.bot_ext_info
                    else:
                        re_top = next_ri.top_ext_info
                    self._add_ext_row(grid, tech_cls, lch, num_cols, re_bot,
                                      re_top, be_bot, be_top, dx, yb_ext + dy,
                                      ext_h, w_edge, w_tot,
                                      f'XR{flat_row_idx}')

        # draw ends
        pinfo, _, flip_tile = used_arr.get_tile_info(0)
        flat_row_idx = pinfo.num_rows - 1 if flip_tile else 0
        ri = pinfo.get_row_place_info(flat_row_idx).row_info
        re = ri.bot_ext_info if ri.flip == flip_tile else ri.top_ext_info
        be = self._add_mos_end(grid, lch, dx, 0, Orientation.R0, h_end_bot, re,
                               num_cols, 'BE', arr_options)
        pinfo, _, flip_tile = used_arr.get_tile_info(num_tiles - 1)
        flat_row_idx = 0 if flip_tile else pinfo.num_rows - 1
        ri = pinfo.get_row_place_info(flat_row_idx).row_info
        re = ri.top_ext_info if ri.flip == flip_tile else ri.bot_ext_info
        te = self._add_mos_end(grid, lch, dx, h_tot, Orientation.MX, h_end_top,
                               re, num_cols, 'TE', arr_options)
        # draw corners
        cb = self.new_template(MOSCorner,
                               params=dict(lch=lch,
                                           einfo=be,
                                           blk_w=w_edge,
                                           blk_h=h_end_bot,
                                           arr_options=arr_options))
        ct = self.new_template(MOSCorner,
                               params=dict(lch=lch,
                                           einfo=te,
                                           blk_w=w_edge,
                                           blk_h=h_end_top,
                                           arr_options=arr_options))
        self.add_instance(cb, inst_name='XCLL')
        self.add_instance(cb,
                          inst_name='XCLR',
                          xform=Transform(w_tot, 0, Orientation.MY))
        self.add_instance(ct,
                          inst_name='XCUL',
                          xform=Transform(0, h_tot, Orientation.MX))
        self.add_instance(ct,
                          inst_name='XCUR',
                          xform=Transform(w_tot, h_tot, Orientation.R180))

        # override cell boundary
        pr_xl, pr_yb = cb.corner
        pr_xr, pr_yt = ct.corner
        pr_xr = w_tot - pr_xr
        pr_yt = h_tot - pr_yt
        self.add_cell_boundary(BBox(pr_xl, pr_yb, pr_xr, pr_yt))

        # set edge parameters
        self.edge_info = TemplateEdgeInfo(cb.left_edge, cb.bottom_edge,
                                          ct.left_edge, ct.bottom_edge)
Esempio n. 17
0
def test_default3():
    xform = Transform(3, -4)
    assert xform.x == 3
    assert xform.y == -4
    assert xform.orient is Orientation.R0
Esempio n. 18
0
def test_constructor(dx, dy, mode):
    xform = Transform(dx, dy, mode)
    assert xform.x == dx
    assert xform.y == dy
    assert xform.orient is mode
Esempio n. 19
0
    def draw_boundaries(self,
                        master: ArrayBase,
                        top_layer: int,
                        *,
                        half_blk_x: bool = True,
                        half_blk_y: bool = True) -> PyLayInstance:
        tech_cls: ArrayTech = master.tech_cls
        core_box: BBox = master.bound_box
        info: ArrayPlaceInfo = master.place_info

        grid = self.grid

        blk_w, blk_h = grid.get_block_size(top_layer,
                                           half_blk_x=half_blk_x,
                                           half_blk_y=half_blk_y)

        arr_w = core_box.w
        arr_h = core_box.h
        binfo = info.blk_info
        edge_info = binfo.edge_info
        end_info = binfo.end_info
        corner_w = tech_cls.get_edge_width(edge_info, arr_w, blk_w)
        corner_h = tech_cls.get_end_height(end_info, arr_h, blk_h)
        nx = master.nx
        ny = master.ny
        spx = info.width
        spy = info.height
        bnd_params = dict(tech_kwargs=tech_cls.tech_kwargs,
                          w=spx,
                          h=corner_h,
                          info=end_info,
                          options=info.blk_options)
        b_master = self.new_template(ArrayEnd,
                                     params=bnd_params,
                                     grid=master.grid)
        bnd_params['w'] = corner_w
        bnd_params['info'] = b_master.edge_info
        c_master = self.new_template(ArrayCorner, params=bnd_params)
        bnd_params['h'] = spy
        bnd_params['info'] = edge_info
        l_master = self.new_template(ArrayEdge, params=bnd_params)

        tot_w = arr_w + 2 * corner_w
        tot_h = arr_h + 2 * corner_h
        bbox = BBox(0, 0, tot_w, tot_h)
        self.set_size_from_bound_box(top_layer, bbox)

        self.add_instance(c_master, inst_name='CLL')
        self.add_instance(c_master,
                          inst_name='CLR',
                          xform=Transform(tot_w, 0, Orientation.MY))
        self.add_instance(c_master,
                          inst_name='CUL',
                          xform=Transform(0, tot_h, Orientation.MX))
        self.add_instance(c_master,
                          inst_name='CUR',
                          xform=Transform(tot_w, tot_h, Orientation.R180))

        self.add_instance(b_master,
                          inst_name='EB',
                          xform=Transform(corner_w, 0),
                          nx=nx,
                          spx=spx)
        self.add_instance(b_master,
                          inst_name='ET',
                          xform=Transform(corner_w, tot_h, Orientation.MX),
                          nx=nx,
                          spx=spx)
        self.add_instance(l_master,
                          inst_name='EL',
                          xform=Transform(0, corner_h),
                          ny=ny,
                          spy=spy)
        self.add_instance(l_master,
                          inst_name='ET',
                          xform=Transform(tot_w, corner_h, Orientation.MY),
                          ny=ny,
                          spy=spy)

        inst = self.add_instance(master,
                                 inst_name='RES',
                                 xform=Transform(corner_w, corner_h))

        # set edge parameters
        self.edge_info = TemplateEdgeInfo(c_master.left_edge,
                                          c_master.bottm_edge,
                                          c_master.left_edge,
                                          c_master.bottom_edge)

        return inst
Esempio n. 20
0
    def get_via_info(self,
                     bbox: BBox,
                     layer_dir: Direction,
                     layer: str,
                     adj_layer: str,
                     ex_dir: Orient2D,
                     *,
                     purpose: str = '',
                     adj_purpose: str = '',
                     wlen: int = -1,
                     adj_wlen: int = -1,
                     extend: bool = True,
                     adj_ex_dir: Optional[Orient2D] = None,
                     **kwargs: Any) -> Optional[Dict[str, Any]]:
        """Create a via on the routing grid given the bounding box.

        Parameters
        ----------
        bbox : BBox
            the bounding box of the via.
        layer_dir : Direction
            the direction of the first specified layer.  LOWER if the first layer is the
            bottom layer, UPPER if the first layer is the top layer.
        layer : str
            the first layer name.
        adj_layer : str
            the second layer name.
        ex_dir : Orient2D
            the first layer extension direction.
        purpose : str
            first layer purpose name.
        adj_purpose : str
            second layer purpose name.
        wlen : int
            length of first layer wire connected to this Via, in resolution units.
            Used for length enhancement EM calculation.
        adj_wlen : int
            length of second layer wire connected to this Via, in resolution units.
            Used for length enhancement EM calculation.
        extend : bool
            True if via extension can be drawn outside of bounding box.
        adj_ex_dir : Optional[Orient2D]
            second layer extension direction.  Can force to extend in same direction as bottom.
        **kwargs : Any
            optional parameters for EM rule calculations, such as nominal temperature,
            AC rms delta-T, etc.

        Returns
        -------
        info : Optional[Dict[str, Any]]
            A dictionary of via information, or None if no solution.  Should have the following:

            resistance : float
                The total via array resistance, in Ohms.
            idc : float
                The total via array maximum allowable DC current, in Amperes.
            iac_rms : float
                The total via array maximum allowable AC RMS current, in Amperes.
            iac_peak : float
                The total via array maximum allowable AC peak current, in Amperes.
            params : Dict[str, Any]
                A dictionary of via parameters.
        """
        if adj_ex_dir is None:
            adj_ex_dir = ex_dir.perpendicular()

        via_id = self.get_via_id(layer_dir, layer, purpose, adj_layer,
                                 adj_purpose)
        via_param = self.get_via_param(bbox.w, bbox.h, via_id, layer_dir,
                                       ex_dir, adj_ex_dir, extend)

        if via_param.empty:
            # no solution found
            return None

        xform = Transform(bbox.xm, bbox.ym, Orientation.R0)
        m_box = via_param.get_box(xform, layer_dir)
        adj_m_box = via_param.get_box(xform, layer_dir.flip())
        w = m_box.get_dim(ex_dir.perpendicular())
        adj_w = adj_m_box.get_dim(adj_ex_dir.perpendicular())
        cut_dim = via_param.cut_dim
        nx = via_param.nx
        ny = via_param.ny
        idc, irms, ipeak = self.get_via_em_specs(layer_dir,
                                                 layer,
                                                 purpose,
                                                 adj_layer,
                                                 adj_purpose,
                                                 cut_dim[0],
                                                 cut_dim[1],
                                                 m_w=w,
                                                 m_l=wlen,
                                                 adj_m_w=adj_w,
                                                 adj_m_l=adj_wlen,
                                                 array=nx > 1 or ny > 1,
                                                 **kwargs)

        params = {
            'id': via_id,
            'xform': Transform(bbox.xm, bbox.ym, Orientation.R0),
            'via_param': via_param,
        }

        ntot = nx * ny
        box_list = [None, None]
        box_list[layer_dir] = m_box
        box_list[layer_dir.flip()] = adj_m_box
        return dict(
            resistance=0.0,
            idc=idc * ntot,
            iac_rms=irms * ntot,
            iac_peak=ipeak * ntot,
            params=params,
            metal_box=box_list,
        )
Esempio n. 21
0
    def draw_layout(self) -> None:
        ndio_cls_name: str = self.params['ndio_cls']
        pdio_cls_name: str = self.params['pdio_cls']
        ndio_params: Param = self.params['ndio_params']
        pdio_params: Param = self.params['pdio_params']
        fe_params: Param = self.params['fe_params']
        npadout: int = self.params['npadout']
        w_dio_min: int = self.params['w_dio_min']
        dio_margin: int = self.params['dio_margin']
        tb_margin: int = self.params['tb_margin']

        fe_ip_params = dict(
            cls_name='xbase.layout.mos.top.GenericWrapper',
            params=dict(
                cls_name=Frontend.get_qualified_name(),
                params=fe_params,
                export_hidden=True,
                half_blk_x=False,
            ),
        )

        # make masters
        fe_master: TemplateBase = self.new_template(IPMarginTemplate, params=fe_ip_params)
        ndio_cls = cast(Type[TemplateBase], import_class(ndio_cls_name))
        pdio_cls = cast(Type[TemplateBase], import_class(pdio_cls_name))
        nd_master: TemplateBase = self.new_template(ndio_cls, params=ndio_params)
        pd_master: TemplateBase = self.new_template(pdio_cls, params=pdio_params)

        # floorplan
        grid = self.grid
        top_layer = nd_master.top_layer
        w_blk, h_blk = grid.get_block_size(top_layer)
        tb_margin = -(-tb_margin // h_blk) * h_blk
        fe_box = fe_master.bound_box
        nd_box = nd_master.bound_box
        pd_box = pd_master.bound_box
        h_fe = fe_box.h
        h_nd = nd_box.h
        h_pd = pd_box.h
        w_fe = fe_box.w
        w_nd = nd_box.w
        w_pd = pd_box.w
        w_dio = max(w_dio_min + dio_margin, w_nd + w_pd)
        w_tot = w_fe + w_dio
        w_tot = -(-w_tot // w_blk) * w_blk
        h_tot = max(h_fe, h_nd, h_pd) + 2 * tb_margin
        y_fe = (h_tot - h_fe) // (2 * h_blk) * h_blk
        y_nd = (h_tot - h_nd) // (2 * h_blk) * h_blk
        y_pd = (h_tot - h_pd) // (2 * h_blk) * h_blk
        x_nd = w_fe
        x_pd = w_tot - w_pd

        # instantiate blocks
        fe = self.add_instance(fe_master, inst_name='XFE', xform=Transform(0, y_fe))
        nd = self.add_instance(nd_master, inst_name='XND', xform=Transform(x_nd, y_nd))
        pd = self.add_instance(pd_master, inst_name='XPD', xform=Transform(x_pd, y_pd))

        self.set_size_from_bound_box(top_layer, BBox(0, 0, w_tot, h_tot))

        # diode guard ring connections
        vlay = (fe.get_port('VSS').get_single_layer(), 'drawing')
        vdir = Direction.LOWER
        vss_list = []
        vdd_list = []
        vddio_list = []
        vss_bbox: List[BBox] = fe.get_all_port_pins('VSS', layer=vlay[0])
        vdd_bbox: List[BBox] = fe.get_all_port_pins('VDDCore', layer=vlay[0])
        vddio_bbox: List[BBox] = fe.get_all_port_pins('VDDIO', layer=vlay[0])

        vss_pd = pd.get_all_port_pins('gr')
        for bbox in vss_bbox:
            vss_list.extend(self.connect_bbox_to_track_wires(vdir, vlay, bbox, vss_pd))
        vss_list = self.connect_wires(vss_list)

        vddio_nd = nd.get_all_port_pins('gr')
        for bbox in vddio_bbox:
            vddio_list.extend(self.connect_bbox_to_track_wires(vdir, vlay, bbox, vddio_nd))
        vddio_list = self.connect_wires(vddio_list)

        # diode ports
        vddio_pd = pd.get_all_port_pins('sub')
        vss_nd = nd.get_all_port_pins('sub')
        dio_sp_le = grid.get_line_end_space(vss_nd[0].layer_id, vss_nd[0].track_id.width, even=True)
        dio_sp_le2 = dio_sp_le // 2
        if dio_margin < 0:
            dio_margin = dio_sp_le2
        dio_port_xh = w_tot - dio_margin
        x_dio_mid = (nd.bound_box.xh + pd.bound_box.xl) // 2
        vddio_pd = self.extend_wires(vddio_pd, lower=x_dio_mid + dio_sp_le2, upper=dio_port_xh)
        vss_nd = self.extend_wires(vss_nd, lower=x_nd, upper=x_dio_mid - dio_sp_le2)
        self.add_pin('VDDIO', vddio_pd)
        self.add_pin('VSS', vss_nd)
        pad = pd.get_all_port_pins('pad')
        pad.extend(nd.port_pins_iter('pad'))
        pad = self.connect_wires(pad, lower=x_nd, upper=dio_port_xh)[0]
        self.add_pin('iopad', pad)

        # short diode wires so we are LVS clean
        ym_layer = vddio_nd[0].layer_id - 1
        vss_tidx = grid.coord_to_track(ym_layer, x_nd + w_nd, mode=RoundMode.GREATER_EQ)
        vddio_tidx = grid.coord_to_track(ym_layer, x_pd, mode=RoundMode.LESS_EQ)
        iopad_tidx = grid.get_middle_track(vss_tidx, vddio_tidx, round_up=False)
        ym_w = grid.get_min_track_width(ym_layer, top_ntr=vddio_nd[0].track_id.width)
        vddio_pd.extend(vddio_list)
        vss_nd.extend(vss_list)
        self.connect_to_tracks(vddio_pd, TrackID(ym_layer, vddio_tidx, width=ym_w))
        self.connect_to_tracks(vss_nd, TrackID(ym_layer, vss_tidx, width=ym_w))
        self.connect_to_tracks(pad, TrackID(ym_layer, iopad_tidx, width=ym_w))

        # connect iopad and iclkn
        npad = pad.track_id.num
        pad_conn = pad[:npadout]
        iclkn_bnds = [COORD_MAX, COORD_MIN]
        iopad_out_list = []
        for bbox in fe.port_pins_iter('txpadout', layer=vlay[0]):
            warr = self.connect_bbox_to_track_wires(vdir, vlay, bbox, pad_conn)
            iopad_out_list.append(warr)

        for bbox in fe.port_pins_iter('rxpadin', layer=vlay[0]):
            cur_bnds = [0, 0]
            warr = self.connect_bbox_to_track_wires(vdir, vlay, bbox, pad_conn, ret_bnds=cur_bnds)
            iopad_out_list.append(warr)
            iclkn_bnds[0] = min(iclkn_bnds[0], cur_bnds[0])
            iclkn_bnds[1] = max(iclkn_bnds[1], cur_bnds[1])

        # export iopad_out
        pad_conn_tid = pad_conn.track_id
        pad_layer = pad_conn_tid.layer_id
        iopad_out_list = self.connect_wires(iopad_out_list)
        lower = iopad_out_list[0].lower
        pad_bnds = grid.get_wire_bounds(pad_layer, pad_conn_tid.base_index,
                                        width=pad_conn_tid.width)
        pin_len = grid.get_next_length(pad_layer, pad_conn_tid.width, pad_bnds[1] - pad_bnds[0],
                                       even=True)
        ms_params = self.add_res_metal_warr(pad_layer, pad_conn_tid.base_index,
                                            lower + pin_len, lower + 2 * pin_len,
                                            width=pad_conn_tid.width, num=pad_conn_tid.num,
                                            pitch=pad_conn_tid.pitch)

        self.add_pin('iopad_out', WireArray(pad_conn_tid, lower, lower + pin_len))

        bbox_iclkn = fe.get_pin('iclkn', layer=vlay[0])
        bbox_iclkn = bbox_iclkn.set_interval(Orient2D.y, iclkn_bnds[0], iclkn_bnds[1])
        # NOTE: some process require metal to be in same hierarchy as pin
        self.add_rect(vlay, bbox_iclkn)
        self.add_pin_primitive('iclkn', vlay[0], bbox_iclkn)

        # supply connections
        for idx in range(npad):
            tid = vss_nd[idx].track_id
            if idx == 0 or idx == npad - 1:
                cur_list = vdd_list
                cur_bbox = vdd_bbox
            elif idx & 1:
                cur_list = vss_list
                cur_bbox = vss_bbox
            else:
                cur_list = vddio_list
                cur_bbox = vddio_bbox

            for bbox in cur_bbox:
                cur_list.append(self.connect_bbox_to_tracks(vdir, vlay, bbox, tid))

        self.add_pin('VDDCore', self.connect_wires(vdd_list))
        self.add_pin('VDDIO', self.connect_wires(vddio_list))
        self.add_pin('VSS', self.connect_wires(vss_list))
        for name in ['VDDCore', 'VDDIO', 'VSS']:
            self.reexport(fe.get_port(f'{name}_xm'), net_name=name, hide=False)

        # reexport pins
        for name in ['din', 'ipdrv_buf<1>', 'ipdrv_buf<0>',
                     'indrv_buf<1>', 'indrv_buf<0>', 'itx_en_buf', 'weak_pulldownen',
                     'weak_pullupenb']:
            self.reexport(fe.get_port(name))

        for name in ['clk_en', 'data_en', 'por', 'odat', 'odat_async',
                     'oclkp', 'oclkn']:
            self.reexport(fe.get_port(name))

        self.sch_params = dict(
            fe_params=fe_master.sch_params,
            nd_params=nd_master.sch_params,
            pd_params=pd_master.sch_params,
            ms_params=ms_params,
        )