def get_mos_ext_edge_info(self, blk_w: int, einfo: MOSEdgeInfo) -> LayoutInfo: sd_pitch = self.sd_pitch cpo_h = self.mos_config['cpo_h'] mos_lay_table = self.tech_info.config['mos_lay_table'] cpo_lp = mos_lay_table['CPO'] po_lp = mos_lay_table['PO_DUMMY'] blk_h = einfo['blk_h'] row_type = einfo['row_type'] mos_type = einfo['mos_type'] threshold = einfo['threshold'] blk_rect = BBox(0, 0, blk_w, blk_h) imp_rect = BBox(blk_w - sd_pitch, 0, blk_w, blk_h) cpo_h2 = cpo_h // 2 builder = LayoutInfoBuilder() builder.add_rect_arr(cpo_lp, BBox(blk_w - sd_pitch, -cpo_h2, blk_w, cpo_h2)) builder.add_rect_arr(cpo_lp, BBox(blk_w - sd_pitch, blk_h - cpo_h2, blk_w, blk_h + cpo_h2)) num_sd_pitch = blk_w // sd_pitch self._add_po_array(builder, po_lp, (0, blk_h), num_sd_pitch - 1, num_sd_pitch) self._add_fb(builder, imp_rect) for lay_purp in self._thres_imp_well_layers_iter(row_type, mos_type, threshold): builder.add_rect_arr(lay_purp, imp_rect) return builder.get_info(blk_rect)
def _get_mos_ext_info_helper(self, num_cols: int, blk_h: int, row_type: MOSType, threshold: str ) -> ExtEndLayInfo: sd_pitch = self.sd_pitch cpo_h = self.mos_config['cpo_h'] mos_lay_table = self.tech_info.config['mos_lay_table'] cpo_lp = mos_lay_table['CPO'] po_lp = mos_lay_table['PO_DUMMY'] blk_w = num_cols * sd_pitch cpo_h2 = cpo_h // 2 blk_rect = BBox(0, 0, blk_w, blk_h) builder = LayoutInfoBuilder() builder.add_rect_arr(cpo_lp, BBox(0, -cpo_h2, blk_w, cpo_h2)) builder.add_rect_arr(cpo_lp, BBox(0, blk_h - cpo_h2, blk_w, blk_h + cpo_h2)) self._add_fb(builder, blk_rect) self._add_po_array(builder, po_lp, (0, blk_h), 0, num_cols) for lay_purp in self._thres_imp_well_layers_iter(row_type, row_type, threshold): builder.add_rect_arr(lay_purp, blk_rect) edge_info = MOSEdgeInfo(blk_h=blk_h, row_type=row_type, mos_type=row_type, threshold=threshold) return ExtEndLayInfo(builder.get_info(blk_rect), edge_info)
def get_mos_corner_info(self, blk_w: int, blk_h: int, einfo: MOSEdgeInfo) -> CornerLayInfo: sd_pitch = self.sd_pitch cpo_h = self.mos_config['cpo_h'] mos_lay_table = self.tech_info.config['mos_lay_table'] cpo_lp = mos_lay_table['CPO'] cpo_h2 = cpo_h // 2 blk_rect = BBox(0, 0, blk_w, blk_h) builder = LayoutInfoBuilder() builder.add_rect_arr(cpo_lp, BBox(blk_w - sd_pitch, blk_h - cpo_h2, blk_w, blk_h + cpo_h2)) edgel = edgeb = ImmutableSortedDict(dict(dev_type=DeviceType.MOS, well_margin=sd_pitch)) return CornerLayInfo(builder.get_info(blk_rect), (0, 0), edgel, edgeb)
def get_mos_end_info(self, blk_h: int, num_cols: int, einfo: RowExtInfo) -> ExtEndLayInfo: sd_pitch = self.sd_pitch cpo_h = self.mos_config['cpo_h'] mos_lay_table = self.tech_info.config['mos_lay_table'] cpo_lp = mos_lay_table['CPO'] cpo_h2 = cpo_h // 2 blk_w = num_cols * sd_pitch blk_rect = BBox(0, 0, blk_w, blk_h) builder = LayoutInfoBuilder() builder.add_rect_arr(cpo_lp, BBox(0, blk_h - cpo_h2, blk_w, blk_h + cpo_h2)) edge_info = MOSEdgeInfo() return ExtEndLayInfo(builder.get_info(blk_rect), edge_info)
def get_blk_info(self, conn_layer: int, w: int, h: int, nx: int, ny: int, **kwargs: Any) -> Optional[ArrayLayInfo]: res_type: str = kwargs.get('res_type', 'standard') if res_type != 'metal': raise ValueError(f'unsupported resistor type: {res_type}') x_pitch: int = self.res_config['x_pitch'] conn_w: int = self.res_config['conn_w'] sp_le = self.tech_info.get_min_line_end_space('M1CA', conn_w, purpose='drawing', even=True) builder = LayoutInfoBuilder() x0 = x_pitch // 2 yl = sp_le yh = h - sp_le x1 = w - x_pitch // 2 boxl = BBox(x0 - conn_w // 2, yl, x0 + conn_w // 2, yh) builder.add_rect_arr(('M1CA', 'drawing'), boxl, nx=2, spx=x1 - x0) xm = w // 2 boxm = BBox(xm - conn_w // 2, yl, xm + conn_w // 2, yh) builder.add_rect_arr(('M1CA', 'drawing'), boxm) boxr = BBox(boxm.xl, yl + conn_w, boxm.xh, yh - conn_w) builder.add_rect_arr(('m1res', 'drawing'), boxr) builder.add_rect_arr(('M1CA', 'drawing'), BBox(boxl.xl, yl, boxm.xh, yl + conn_w)) boxr = boxl.get_move_by(dx=x1 - x0) builder.add_rect_arr(('M1CA', 'drawing'), BBox(boxm.xl, yh - conn_w, boxr.xh, yh)) tidu = HalfInt(2 * ((w // x_pitch) - 1)) return ArrayLayInfo( builder.get_info(BBox(0, 0, w, h)), ImmutableSortedDict({ 'u': WireArrayInfo(1, tidu, yl, yh, 1, 1, 0), 'l': WireArrayInfo(1, HalfInt(0), yl, yh, 1, 1, 0) }), ImmutableSortedDict(), ImmutableSortedDict())
def get_ext_geometries(self, re_bot: RowExtInfo, re_top: RowExtInfo, be_bot: ImmutableList[BlkExtInfo], be_top: ImmutableList[BlkExtInfo], cut_mode: MOSCutMode, bot_exty: int, top_exty: int, dx: int, dy: int, w_edge: int) -> LayoutInfo: builder = LayoutInfoBuilder() if cut_mode is MOSCutMode.MID: sd_pitch = self.sd_pitch cpo_h = self.mos_config['cpo_h'] cpo_h2 = cpo_h // 2 cpo_lp = self.tech_info.config['mos_lay_table']['CPO'] wr = dx for be in be_bot: wr += be.fg * sd_pitch builder.add_rect_arr(cpo_lp, BBox(dx - sd_pitch, dy - cpo_h2, wr + sd_pitch, dy + cpo_h2)) return builder.get_info(BBox(0, 0, 0, 0))
def get_mos_row_edge_info(self, blk_w: int, rinfo: MOSRowInfo, einfo: MOSEdgeInfo) -> LayoutInfo: lch = self.lch sd_pitch = self.sd_pitch od_po_extx = self.od_po_extx od_extx = od_po_extx - (sd_pitch - lch) // 2 mos_config = self.mos_config imp_od_encx: int = mos_config['imp_od_encx'] mos_lay_table = self.tech_info.config['mos_lay_table'] row_type = rinfo.row_type blk_h = rinfo.height mos_type = einfo['mos_type'] has_od = einfo.get('has_od', False) blk_rect = BBox(0, 0, blk_w, blk_h) imp_rect = BBox(blk_w - imp_od_encx - od_extx, 0, blk_w, blk_h) po_xl = blk_w - sd_pitch // 2 - lch // 2 builder = LayoutInfoBuilder() if has_od: po_lp = mos_lay_table['PO_PODE'] else: po_lp = mos_lay_table['PO_DUMMY'] builder.add_rect_arr(po_lp, BBox(po_xl, 0, po_xl + lch, rinfo.height)) self._add_fb(builder, imp_rect) if mos_type.is_substrate and mos_type is not row_type.sub_type: row_type = mos_type for lay_purp in self._thres_imp_well_layers_iter( row_type, mos_type, rinfo.threshold): builder.add_rect_arr(lay_purp, imp_rect) return builder.get_info(blk_rect)
def get_fill_info(self, mos_type: str, threshold: str, w: int, h: int, el: Param, eb: Param, er: Param, et: Param) -> LayoutInfo: fin_p: int = self._fill_config['mos_pitch'] fin_h: int = self._fill_config['fin_h'] lch: int = self._fill_config['lch'] po_od_exty: int = self._fill_config['po_od_exty'] po_spy: int = self._fill_config['po_spy'] sd_pitch: int = self._fill_config['sd_pitch'] od_spx: int = self._fill_config['od_spx'] od_density_min: float = self._fill_config['od_density_min'] imp_od_encx: int = self._fill_config['imp_od_encx'] imp_po_ency: int = self._fill_config['imp_po_ency'] fin_p2 = fin_p // 2 fin_h2 = fin_h // 2 od_edge_margin = max(imp_od_encx, od_spx // 2) po_edge_margin = max(imp_po_ency, po_spy // 2) mos_layer_table = self.tech_info.config['mos_lay_table'] po_lp = mos_layer_table['PO_DUMMY'] od_lp = mos_layer_table['OD_DUMMY'] fb_lp = mos_layer_table['FB'] builder = LayoutInfoBuilder() # compute fill X intervals bnd_xl = el.get('delta', 0) bnd_xr = w - er.get('delta', 0) bnd_yb = eb.get('delta', 0) bnd_yt = h - et.get('delta', 0) bbox = BBox(bnd_xl, bnd_yb, bnd_xr, bnd_yt) fill_xl = bnd_xl + od_edge_margin fill_xh = bnd_xr - od_edge_margin fill_yl = bnd_yb + po_edge_margin fill_yh = bnd_yt - po_edge_margin # compute fill X/Y intervals od_x_list, od_x_density = self._get_od_x_list(w, fill_xl, fill_xh, sd_pitch, lch, od_spx) if not od_x_list: return builder.get_info(bbox) od_y_density = od_density_min / od_x_density od_y_list = self._get_od_y_list(h, fill_yl, fill_yh, fin_p, fin_h, od_y_density) if not od_y_list: return builder.get_info(bbox) # draw fills ny = len(od_y_list) for idx, (od_yb, od_yt) in enumerate(od_y_list): po_yb = fill_yl if idx == 0 else od_yb - po_od_exty po_yt = fill_yh if idx == ny - 1 else od_yt + po_od_exty for od_xl, od_xr in od_x_list: builder.add_rect_arr(od_lp, BBox(od_xl, od_yb, od_xr, od_yt)) nx = 1 + (od_xr - od_xl - lch) // sd_pitch builder.add_rect_arr(po_lp, BBox(od_xl, po_yb, od_xl + lch, po_yt), nx=nx, spx=sd_pitch) # draw other layers fin_yb = ( (bnd_yb - fin_p2 + fin_h2) // fin_p) * fin_p + fin_p2 - fin_h2 fin_yt = -(-(bnd_yt - fin_p2 - fin_h2) // fin_p) * fin_p + fin_p2 + fin_h2 for imp_lp in self._thres_imp_well_layers_iter(mos_type, threshold): builder.add_rect_arr(imp_lp, BBox(bnd_xl, bnd_yb, bnd_xr, bnd_yt)) builder.add_rect_arr(fb_lp, BBox(bnd_xl, fin_yb, bnd_xr, fin_yt)) return builder.get_info(bbox)
def get_corner_info(self, w: int, h: int, info: ImmutableSortedDict[str, Any], **kwargs: Any) -> LayoutInfo: builder = LayoutInfoBuilder() return builder.get_info(BBox(0, 0, w, h))
def get_end_info(self, w: int, h: int, info: ImmutableSortedDict[str, Any], **kwargs: Any) -> ArrayEndInfo: builder = LayoutInfoBuilder() return ArrayEndInfo(builder.get_info(BBox(0, 0, w, h)), ImmutableSortedDict())
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())
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())
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]))