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))
def get_default_param_values(cls) -> Dict[str, Any]: return dict( ridx_p=-1, ridx_n=0, core_ncol=0, tap_info_list=ImmutableList(), )
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])
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
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']
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 get_info(self, bnd_box: BBox) -> LayoutInfo: return LayoutInfo(ImmutableSortedDict(self._lp_dict), ImmutableList(self._warr_list), ImmutableList(self._via_list), bnd_box)
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()
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, )
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()
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]))
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