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
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))
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)
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()))
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
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
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)
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
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
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]
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.')
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)
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()
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)
def test_default2(): xform = Transform(3) assert xform.x == 3 assert xform.y == 0 assert xform.orient is Orientation.R0
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)
def test_default3(): xform = Transform(3, -4) assert xform.x == 3 assert xform.y == -4 assert xform.orient is Orientation.R0
def test_constructor(dx, dy, mode): xform = Transform(dx, dy, mode) assert xform.x == dx assert xform.y == dy assert xform.orient is mode
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
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, )
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, )