def get_laygo_space_info(self, row_info, num_blk, left_blk_info, right_blk_info): # type: (Dict[str, Any], int, Any, Any) -> Dict[str, Any] od_y = row_info['od_y'] po_y = row_info['po_y'] # md_y = row_info['md_y'] arr_y = row_info['arr_y'] lch_unit = row_info['lch_unit'] row_type = row_info['row_type'] sub_type = row_info['sub_type'] row_ext_top = row_info['ext_top_info'] row_ext_bot = row_info['ext_bot_info'] lay_info_list = row_info['lay_info_list'] fill_info_list = row_info['fill_info_list'] imp_params = row_info['imp_params'] is_sub = (row_type == sub_type) mos_constants = self.get_mos_tech_constants(lch_unit) sd_pitch = mos_constants['sd_pitch'] od_fill_w_max = None ####TODO#### #This probably needs to come from get_mos_tech_constants, #but its not super sensitive because of the // done in od_spx_fg #this works for now od_spx = lch_unit ########## od_spx_fg = -(-(od_spx - sd_pitch + lch_unit) // sd_pitch) + 2 # get OD fill X interval area = num_blk - 2 * od_spx_fg if area > 0: if od_fill_w_max is None: #od_x_list = [(od_spx_fg, num_blk - od_spx_fg)] od_x = (od_spx_fg, num_blk - od_spx_fg) else: raise Exception('Greg is not rewriteing planar to accomodate od_list') od_fg_max = (od_fill_w_max - lch_unit) // sd_pitch - 1 od_x_list = fill_symmetric_max_density(area, area, 2, od_fg_max, od_spx_fg, offset=od_spx_fg, fill_on_edge=True, cyclic=False)[0] draw_od = True else: #This is just a reasonable dummy value since draw_od is false od_x = (1,2) draw_od = False row_info_list = [RowInfo(od_x=od_x, od_y=od_y, od_type=('dum', sub_type), po_y=po_y), ] # update extension information cur_edge_info = EdgeInfo(od_type=None, draw_layers={}, y_intv=dict(od=od_y)) # figure out poly types per finger po_types = [] od_intv_idx = 0 for cur_idx in range(num_blk): if cur_idx == 0 or cur_idx == num_blk - 1: od_type = left_blk_info[0].od_type if cur_idx == 0 else right_blk_info[0].od_type if od_type == 'mos': po_types.append('PO_edge') elif od_type == 'sub': po_types.append('PO_edge_sub') elif od_type == 'dum': po_types.append('PO_edge_dummy') else: po_types.append('PO_dummy') elif cur_idx < od_spx_fg or cur_idx >= num_blk - od_spx_fg: po_types.append('PO_dummy') #elif od_intv_idx < len(od_x_list): elif od_intv_idx < 1: #cur_od_intv = od_x_list[od_intv_idx] cur_od_intv = od_x if cur_od_intv[1] == cur_idx: po_types.append('PO_edge_dummy') od_intv_idx += 1 elif cur_od_intv[0] <= cur_idx < cur_od_intv[1]: po_types.append('PO_gate_dummy') elif cur_idx == cur_od_intv[0] - 1: po_types.append('PO_edge_dummy') else: if cur_idx > cur_od_intv[1]: od_intv_idx += 1 po_types.append('PO_dummy') else: po_types.append('PO_dummy') # noinspection PyProtectedMember ext_top_info = row_ext_top._replace(po_types=po_types, edgel_info=cur_edge_info, edger_info=cur_edge_info) # noinspection PyProtectedMember ext_bot_info = row_ext_bot._replace(po_types=po_types, edgel_info=cur_edge_info, edger_info=cur_edge_info) lr_edge_info = (cur_edge_info, []) layout_info = dict( is_sub_row=is_sub, blk_type='sub' if is_sub else 'mos', lch_unit=lch_unit, sd_pitch=sd_pitch, fg=num_blk, arr_y=arr_y, draw_od=draw_od, row_info_list=row_info_list, lay_info_list=lay_info_list, sub_type=sub_type, imp_params=imp_params, is_sub_ring=False, dnw_mode='', ) # step 8: return results return dict( layout_info=layout_info, ext_top_info=ext_top_info, ext_bot_info=ext_bot_info, left_edge_info=lr_edge_info, right_edge_info=lr_edge_info, )
def get_tb_edge_info( self, # type: ResTechFinfetBase grid, # type: RoutingGrid core_info, # type: Dict[str, Any] hedge, # type: int l, # type: int w, # type: int res_type, # type: str sub_type, # type: str threshold, # type: str track_widths, # type: List[int] track_spaces, # type: List[Union[float, int]] options, # type: Dict[str, Any] ): # type: (...) -> Optional[Dict[str, Any]] """Returns a dictionary of TB edge layout information. This method checks: 1. spacing rules. 2. PO density rules if all these pass, return TB edge layout information dictionary. """ well_end_mode = options.get('well_end_mode', 3) nfin_min, nfin_max = self.res_config['od_fill_h'] po_od_exty = self.res_config['po_od_exty'] edge_margin = self.res_config['edge_margin'] po_res_spy = self.res_config['po_res_spy'] res_max_density = self.res_config['res_max_density'] od_min_density = self.res_config['od_min_density'] po_spy = self.res_config['po_spy'] finfet_od_exty = self.res_config['finfet_od_exty'] imp_po_ency = self.res_config['imp_po_ency'] rtop_od_ency = self.res_config['rtop_od_ency'] fin_h = self.mos_tech.mos_config['fin_h'] fin_p = self.mos_tech.mos_config['mos_pitch'] fin_p2 = fin_p // 2 fin_h2 = fin_h // 2 wcore = core_info['width'] hcore = core_info['height'] core_lr_dum_w = core_info['lr_dum_w'] core_tb_dum_w = core_info['tb_dum_w'] core_lr_od_loc = core_info['lr_od_loc'][0] core_fill_info = core_info['fill_info'] if well_end_mode & 1 == 0: edge_margin = self.res_config.get('edge_margin_nowell', edge_margin) wres, lres, wres_lr, lres_tb = self.get_res_dimension(l, w) # compute dummy OD Y separation in number of fins od_sp = po_spy + po_od_exty * 2 # when two ODs are N fin pitches apart, the actual OD spacing is N * fin_pitch - fin_h od_sp = -(-(od_sp + fin_h) // fin_p) # check RH_TN density rule max_res_area = int(wcore * hedge * res_max_density) if wres * lres_tb > max_res_area: return None # check spacing rule, which just means we can draw dummy transistors below RH # get space between resistor and core boundary spy = (hcore - lres) // 2 # find bottom edge OD locations # compute OD Y coordinate for bottom edge of TBEdge block bot_dummy_bnd = edge_margin + imp_po_ency + po_od_exty bot_pitch_index = -(-(bot_dummy_bnd - fin_p2 + fin_h2) // fin_p) top_dummy_bnd = hedge - spy - lres_tb - po_res_spy - po_od_exty top_pitch_index = (top_dummy_bnd - fin_p2 - fin_h2) // fin_p tot_space = top_pitch_index - bot_pitch_index + 1 edge_bot_od_loc = fill_symmetric_max_density(tot_space, tot_space, nfin_min, nfin_max, od_sp, fill_on_edge=True, cyclic=False)[0] # compute fin 0 offset and convert fin location to Y coordinates fin_offset = bot_pitch_index * fin_p + fin_p2 edge_bot_od_loc = self._compute_od_y_loc(edge_bot_od_loc, fin_p, fin_h, fin_offset) if not edge_bot_od_loc: # we cannot draw any bottom OD return None edge_tb_dum_yb = edge_bot_od_loc[0][0] # compute OD Y coordinate for left/right edge of TBEdge block. # find the fin pitch index of the lower bound of empty space. bot_pitch_index = top_pitch_index + od_sp # find the fin pitch index of the upper bound of empty space. adj_top_od_yb = hedge + core_lr_od_loc[0][0] top_pitch_index = (adj_top_od_yb - fin_p2 + fin_h2) // fin_p - od_sp # compute total space and fill tot_space = top_pitch_index - bot_pitch_index + 1 edge_lr_od_loc = fill_symmetric_max_density(tot_space, tot_space, nfin_min, nfin_max, od_sp, fill_on_edge=True, cyclic=False)[0] # compute fin 0 offset and convert fin location to Y coordinates fin_offset = bot_pitch_index * fin_p + fin_p2 edge_lr_od_loc = self._compute_od_y_loc(edge_lr_od_loc, fin_p, fin_h, fin_offset) edge_lr_po_bnd = (edge_bot_od_loc[-1][-1] + po_od_exty + po_spy, adj_top_od_yb - po_od_exty - po_spy) # compute total OD area od_area = 0 for od_w, yloc_list in ((core_lr_dum_w + core_tb_dum_w, edge_bot_od_loc), (core_lr_dum_w, edge_lr_od_loc)): for yb, yt in yloc_list: od_area += od_w * (yt - yb) # check OD density rule min_od_area = int(math.ceil(wcore * hedge * od_min_density)) if od_area < min_od_area: return None # if we get here, then all density rules are met # compute fill Y coordinate in edge block fill_edge_y_list = [] for _, _, w, h, core_x, core_y, density, sp_min, sp_max, sp_bnd in core_fill_info: sp_yb = sp_bnd + h sp_yt = hedge + core_y[0][0] area = sp_yt - sp_yb tarea = int(math.ceil(area * density**0.5)) c_info = fill_symmetric_min_density_info(area, tarea, h, h, sp_min, sp_max=sp_max, fill_on_edge=False) edge_y = fill_symmetric_interval(*c_info[0][2], offset=sp_yb, invert=c_info[1])[0] edge_y.insert(0, (sp_bnd, sp_yb)) fill_edge_y_list.append(edge_y) # return layout information return dict( lr_od_loc=(edge_lr_od_loc, edge_lr_po_bnd), bot_od_loc=(edge_bot_od_loc, None), fb_yb=edge_tb_dum_yb - finfet_od_exty, rtop_yb=edge_tb_dum_yb - rtop_od_ency, imp_yb=edge_tb_dum_yb - po_od_exty - imp_po_ency, fill_edge_y_list=fill_edge_y_list, )
def get_core_info( self, grid, # type: RoutingGrid width, # type: int height, # type: int l, # type: int w, # type: int res_type, # type: str sub_type, # type: str threshold, # type: str track_widths, # type: List[int] track_spaces, # type: List[Union[float, int]] options, # type: Dict[str, Any] ): # type: (...) -> Optional[Dict[str, Any]] """Compute core layout information dictionary. This method checks max PO and min OD density rules. """ layer_table = self.config['layer_name'] od_wmin = self.res_config['od_dim_min'][0] od_wmax = self.res_config['od_dim_max'][0] od_sp = self.res_config['od_sp'] od_min_density = self.res_config['od_min_density'] po_od_sp = self.res_config['po_od_sp'] po_max_density = self.res_config['po_max_density'] co_w = self.res_config['co_w'] rpo_co_sp = self.res_config['rpo_co_sp'] m1_sp_max = self.res_config['m1_sp_max'] imp_od_sp = self.res_config['imp_od_sp'] imp_ency = self.res_config['imp_enc'][1] res_info = self.res_config['info'][res_type] od_in_res = res_info['od_in_res'] wres, lres, wres_lr, lres_tb = self.get_res_dimension(l, w) # check PO density max_res_area = int(width * height * po_max_density) if wres * lres > max_res_area: return None # compute OD fill X coordinates on the left/right edge if od_in_res: bnd_spx = (width - wres) // 2 area = 2 * (bnd_spx - po_od_sp) lr_od_xloc = fill_symmetric_max_density(area, area, od_wmin, od_wmax, od_sp, offset=-bnd_spx + po_od_sp, sp_max=None, fill_on_edge=True, cyclic=False)[0] lr_od_h = lres else: lr_od_xloc = [] lr_od_h = 0 # compute OD fill Y coordinates on the top/bottom edge bnd_spy = (height - lres) // 2 if od_in_res: area = 2 * (bnd_spy - po_od_sp) tb_od_offset = -bnd_spy + po_od_sp tb_od_w = wres else: area = 2 * (bnd_spy - (imp_od_sp + imp_ency)) tb_od_offset = -bnd_spy + imp_od_sp + imp_ency tb_od_w = width - od_sp dod_dx = (width - tb_od_w) // 2 tb_od_xloc = [(dod_dx, width - dod_dx)] tb_od_yloc = fill_symmetric_max_density(area, area, od_wmin, od_wmax, od_sp, offset=tb_od_offset, sp_max=None, fill_on_edge=True, cyclic=False)[0] # check OD density min_od_area = int(math.ceil(width * height * od_min_density)) # compute OD area od_area = 0 for od_w, od_intv_list in ((lr_od_h, lr_od_xloc), (tb_od_w, tb_od_yloc)): for lower, upper in od_intv_list: od_area += od_w * (upper - lower) if od_area < min_od_area: return None # if we get here, then all density rules are met # split into top and bottom dummy locations num_dummy_half = -(-len(tb_od_yloc) // 2) bot_od_yloc = tb_od_yloc[-num_dummy_half:] top_od_yloc = [(a + height, b + height) for a, b in tb_od_yloc[:num_dummy_half]] # fill layout info with dummy information layout_info = dict( width=width, height=height, lr_od_xloc=lr_od_xloc, bot_od_yloc=bot_od_yloc, top_od_yloc=top_od_yloc, tb_od_xloc=tb_od_xloc, ) # compute port information # first, compute M2 routing track location xc = width // 2 rpdmy_yb = height // 2 - l // 2 rpdmy_yt = rpdmy_yb + l bot_yc = rpdmy_yb - rpo_co_sp - co_w // 2 top_yc = rpdmy_yt + rpo_co_sp + co_w // 2 bot_layer = self.get_bot_layer() bot_pitch = grid.get_track_pitch(bot_layer, unit_mode=True) bot_num_tr = height // bot_pitch if height % bot_pitch == 0 else height / bot_pitch # first, find M2 tracks such that the top track of this block and the bottom track of # the top adjacent block is track_spaces[0] tracks apart. the actual tracks cannot exceed these. m2_w, m2_sp = track_widths[0], track_spaces[0] if isinstance(m2_sp, int): bot_tr_min = (m2_w + m2_sp + 1) / 2 - 1 else: # for half-integer spacing, we need to round up edge space so that tracks remain centered # in the block bot_tr_min = (m2_w + m2_sp + 1.5) / 2 - 1 top_tr_max = bot_num_tr - 1 - bot_tr_min # find M2 tracks closest to ports top_tr = min( top_tr_max, grid.coord_to_nearest_track(bot_layer, top_yc, half_track=True, mode=1, unit_mode=True)) bot_tr = max( bot_tr_min, grid.coord_to_nearest_track(bot_layer, bot_yc, half_track=True, mode=-1, unit_mode=True)) # get CO/VIA1 parameters, and metal 1 bounding box m1_name = layer_table[1] m2_name = layer_table[2] m2_h = grid.get_track_width(bot_layer, m2_w, unit_mode=True) m2_type = self.tech_info.get_layer_type(m2_name) m2_len_min = self.tech_info.get_min_length_unit(m2_type, m2_h) res = grid.resolution port_info = [] for port_name, yc, m2_tr in (('bot', bot_yc, bot_tr), ('top', top_yc, top_tr)): via0_params, m1_box = self.get_via0_info(xc, yc, wres, res) # get via1 parameters m2_yc = grid.track_to_coord(bot_layer, m2_tr, unit_mode=True) v1_box = BBox(m1_box.left_unit, m2_yc - m2_h // 2, m1_box.right_unit, m2_yc + m2_h // 2, res, unit_mode=True) via1_info = grid.tech_info.get_via_info(v1_box, m1_name, m2_name, 'y') m1_box = m1_box.merge(via1_info['bot_box']) m2_box = via1_info['top_box'] m2_box = m2_box.expand(dx=max( 0, (m2_len_min - m2_box.width_unit) // 2), unit_mode=True) via1_params = via1_info['params'] via1_params['via_type'] = via1_params.pop('id') port_info.append( (port_name, via0_params, via1_params, m1_box, m2_box)) # compute fill information # compute fill Y coordinate in core block # compute fill Y coordinates between ports inside the cell m1_w = port_info[0][3].width_unit m1_h = port_info[0][3].height_unit m1_bot = port_info[0][3] m1_top = port_info[1][3] m1_bot_yb, m1_bot_yt = m1_bot.bottom_unit, m1_bot.top_unit m1_top_yb, m1_top_yt = m1_top.bottom_unit, m1_top.top_unit m1_core_mid_y = fill_symmetric_const_space(m1_top_yb - m1_bot_yt, m1_sp_max, m1_h, m1_h, offset=m1_bot_yt) # compute fill Y coordinates between ports outside the cell m1_core_top_y = fill_symmetric_const_space(m1_bot_yb + height - m1_top_yt, m1_sp_max, m1_h, m1_h, offset=m1_top_yt) # combine fill Y coordinates together in one list fill_len2 = -(-len(m1_core_top_y) // 2) m1_core_y = [(a - height, b - height) for (a, b) in m1_core_top_y[-fill_len2:]] m1_core_y.append((m1_bot_yb, m1_bot_yt)) m1_core_y.extend(m1_core_mid_y) m1_core_y.append((m1_top_yb, m1_top_yt)) m1_core_y.extend(m1_core_top_y[:fill_len2]) # compute fill X coordinate in core block m1_xl, m1_xr = m1_bot.left_unit, m1_bot.right_unit sp_xl = -width + m1_xr sp_xr = m1_xl m1_core_x = fill_symmetric_const_space(sp_xr - sp_xl, m1_sp_max, m1_w, m1_w, offset=sp_xl) m1_core_x.append((m1_xl, m1_xr)) m1_core_x.extend(((a + width, b + width) for (a, b) in m1_core_x[:-1])) layout_info['port_info'] = port_info layout_info['m1_core_x'] = m1_core_x layout_info['m1_core_y'] = m1_core_y layout_info['m1_w'] = m1_w layout_info['m1_h'] = m1_h return layout_info
def get_core_info( self, # type: ResTechFinfetBase grid, # type: RoutingGrid width, # type: int height, # type: int l, # type: int w, # type: int res_type, # type: str sub_type, # type: str threshold, # type: str track_widths, # type: List[int] track_spaces, # type: List[Union[float, int]] options, # type: Dict[str, Any] ): # type: (...) -> Optional[Dict[str, Any]] """Compute core layout information dictionary. This method checks max PO and min OD density rules. """ res = grid.resolution nfin_min, nfin_max = self.res_config['od_fill_h'] po_od_exty = self.res_config['po_od_exty'] po_spx = self.res_config['po_spx'] po_spy = self.res_config['po_spy'] po_res_spx = self.res_config['po_res_spx'] po_res_spy = self.res_config['po_res_spy'] po_lch = self.res_config['po_lch'] po_pitch = self.res_config['po_pitch'] mp_h = self.res_config['mp_h'] res_max_density = self.res_config['res_max_density'] od_min_density = self.res_config['od_min_density'] fin_h = self.mos_tech.mos_config['fin_h'] fin_p = self.mos_tech.mos_config['mos_pitch'] fin_p2 = fin_p // 2 fin_h2 = fin_h // 2 wres, lres, wres_lr, lres_tb = self.get_res_dimension(l, w) # check resistor density max_res_area = int(width * height * res_max_density) if wres * lres > max_res_area: return None # Compute dummy Y coordinates # compute dummy OD Y separation in number of fins od_sp = po_spy + po_od_exty * 2 # when two ODs are N fin pitches apart, the actual OD spacing is N * fin_pitch - fin_h od_sp = -(-(od_sp + fin_h) // fin_p) # compute OD Y coordinates for left/right edge. h_core_nfin = height // fin_p core_lr_od_loc = fill_symmetric_max_density(h_core_nfin, h_core_nfin, nfin_min, nfin_max, od_sp, fill_on_edge=False, cyclic=True)[0] # compute OD Y coordinates for top/bottom edge. # compute fin offset for top edge dummies bnd_spy = (height - lres) // 2 top_dummy_bnd = bnd_spy + lres + po_res_spy + po_od_exty # find the fin pitch index of the lower bound of the empty space. pitch_index = -(-(top_dummy_bnd - fin_p2 + fin_h2) // fin_p) tot_space = 2 * (h_core_nfin - pitch_index) core_tb_od_loc = fill_symmetric_max_density(tot_space, tot_space, nfin_min, nfin_max, od_sp, fill_on_edge=True, cyclic=False)[0] # compute dummy number of fingers for left/right edge bnd_spx = (width - wres) // 2 avail_sp = bnd_spx * 2 - po_res_spx * 2 core_lr_fg = (avail_sp - po_lch) // po_pitch + 1 # compute dummy number of fingers for top/bottom edge core_lr_dum_w = po_lch + (core_lr_fg - 1) * po_pitch avail_sp = width - core_lr_dum_w - 2 * po_spx core_tb_fg = (avail_sp - po_lch) // po_pitch + 1 core_tb_dum_w = po_lch + (core_tb_fg - 1) * po_pitch # check OD density min_od_area = int(math.ceil(width * height * od_min_density)) # compute OD area od_area = 0 for od_w, od_fin_list in ((core_lr_dum_w, core_lr_od_loc), (core_tb_dum_w, core_tb_od_loc)): for fin_start, fin_stop in od_fin_list: od_area += od_w * ((fin_stop - fin_start - 1) * fin_p + fin_h) if od_area < min_od_area: return None # if we get here, then all density rules are met # convert dummy fin location to Y coordinates core_lr_od_loc = self._compute_od_y_loc(core_lr_od_loc, fin_p, fin_h, fin_p2) # split the top/bottom dummies into halves num_dummy_half = -(-len(core_tb_od_loc) // 2) # top dummy locations top_offset = pitch_index * fin_p + fin_p2 core_top_od_loc = self._compute_od_y_loc( core_tb_od_loc[:num_dummy_half], fin_p, fin_h, top_offset) core_top_po_bnd = (height - bnd_spy + po_res_spy, height + bnd_spy - po_res_spy) # bottom dummy locations core_bot_od_loc = self._compute_od_y_loc( core_tb_od_loc[-num_dummy_half:], fin_p, fin_h, top_offset - height) core_bot_po_bnd = (-bnd_spy + po_res_spy, bnd_spy - po_res_spy) # fill layout info with dummy information layout_info = dict( width=width, height=height, lr_dum_w=core_lr_dum_w, tb_dum_w=core_tb_dum_w, lr_od_loc=(core_lr_od_loc, None), top_od_loc=(core_top_od_loc, core_top_po_bnd), bot_od_loc=(core_bot_od_loc, core_bot_po_bnd), lr_fg=core_lr_fg, tb_fg=core_tb_fg, ) # compute port information # first, compute port track location xc = width // 2 rpdmy_yb = height // 2 - l // 2 rpdmy_yt = rpdmy_yb + l bot_yc = rpdmy_yb - mp_h // 2 top_yc = rpdmy_yt + mp_h // 2 bot_layer = self.get_bot_layer() bot_pitch = grid.get_track_pitch(bot_layer, unit_mode=True) bot_num_tr = height // bot_pitch # first, find port tracks such that the top track of this block and the bottom track of # the top adjacent block is track_spaces[0] tracks apart. # the actual tracks cannot exceed these. top_tr_max = bot_num_tr - (track_widths[0] + track_spaces[0] + 1) / 2 bot_tr_min = (track_widths[0] + track_spaces[0] + 1) / 2 - 1 # find port tracks closest to ports top_tr = min( top_tr_max, grid.coord_to_nearest_track(bot_layer, top_yc, half_track=True, mode=1, unit_mode=True)) bot_tr = max( bot_tr_min, grid.coord_to_nearest_track(bot_layer, bot_yc, half_track=True, mode=-1, unit_mode=True)) # get port information port_info = [] for port_name, yc, m2_tr in (('bot', bot_yc, bot_tr), ('top', top_yc, top_tr)): port_yb, port_yt = grid.get_wire_bounds(bot_layer, m2_tr, track_widths[0], unit_mode=True) rect_list, via_list = self.get_port_info(xc, yc, wres, port_yb, port_yt, res) port_info.append((port_name, rect_list, via_list)) # compute fill information # compute fill Y coordinate in core block bot_rect_list = port_info[0][1] top_rect_list = port_info[1][1] fill_info = [] for bot_rect_info, top_rect_info in zip(bot_rect_list, top_rect_list): if bot_rect_info['do_fill']: layer = bot_rect_info['layer'] exc_layer = bot_rect_info['exc_layer'] density = bot_rect_info['density'] sp_min = bot_rect_info['sp_min'] sp_max = bot_rect_info['sp_max'] sp_bnd = bot_rect_info['sp_bnd'] bot_box = bot_rect_info['bbox'] top_box = top_rect_info['bbox'] density_1d = density**0.5 w, h = bot_box.width_unit, bot_box.height_unit bot_yb, bot_yt = bot_box.bottom_unit, bot_box.top_unit top_yb, top_yt = top_box.bottom_unit, top_box.top_unit # compute fill Y coordinates between ports inside the cell area = top_yb - bot_yt tarea = int(math.ceil(area * density_1d)) mid_info = fill_symmetric_min_density_info(area, tarea, h, h, sp_min, sp_max=sp_max, fill_on_edge=False) core_mid_y = fill_symmetric_interval(*mid_info[0][2], offset=bot_yt, invert=mid_info[1])[0] # compute fill Y coordinates between ports outside the cell area = bot_yb + height - top_yt tarea = int(math.ceil(area * density_1d)) top_info = fill_symmetric_min_density_info(area, tarea, h, h, sp_min, sp_max=sp_max, fill_on_edge=False) core_top_y = fill_symmetric_interval(*top_info[0][2], offset=top_yt, invert=top_info[1])[0] # combine fill Y coordinates together in one list fill_len2 = -(-len(core_top_y) // 2) core_y = [(a - height, b - height) for (a, b) in core_top_y[-fill_len2:]] core_y.append((bot_yb, bot_yt)) core_y.extend(core_mid_y) core_y.append((top_yb, top_yt)) core_y.extend(core_top_y[:fill_len2]) # compute fill X coordinate in core block xl, xr = bot_box.left_unit, bot_box.right_unit sp_xl = -width + xr sp_xr = xl area = sp_xr - sp_xl tarea = int(math.ceil(area * density_1d)) x_info = fill_symmetric_min_density_info(area, tarea, w, w, sp_min, sp_max=sp_max, fill_on_edge=False) core_x = fill_symmetric_interval(*x_info[0][2], offset=sp_xl, invert=x_info[1])[0] core_x.append((xl, xr)) core_x.extend( ((a + width, b + width) for (a, b) in core_x[:-1])) fill_info.append((layer, exc_layer, w, h, core_x, core_y, density, sp_min, sp_max, sp_bnd)) layout_info['port_info'] = port_info layout_info['fill_info'] = fill_info return layout_info
def get_laygo_space_info(self, row_info, num_blk, left_blk_info, right_blk_info): # type: (Dict[str, Any], int, Any, Any) -> Dict[str, Any] od_y = row_info['od_y'] po_y = row_info['po_y'] md_y = row_info['md_y'] arr_y = row_info['arr_y'] lch_unit = row_info['lch_unit'] row_type = row_info['row_type'] sub_type = row_info['sub_type'] row_ext_top = row_info['ext_top_info'] row_ext_bot = row_info['ext_bot_info'] lay_info_list = row_info['lay_info_list'] fill_info_list = row_info['fill_info_list'] imp_params = row_info['imp_params'] is_sub = (row_type == sub_type) mos_constants = self.get_mos_tech_constants(lch_unit) sd_pitch = mos_constants['sd_pitch'] od_spx = mos_constants['od_spx'] od_fill_w_max = mos_constants['od_fill_w_max'] od_spx_fg = -(-(od_spx - sd_pitch + lch_unit) // sd_pitch) + 2 # get OD fill X interval area = num_blk - 2 * od_spx_fg if area > 0: if od_fill_w_max is None: od_x_list = [(od_spx_fg, num_blk - od_spx_fg)] else: od_fg_max = (od_fill_w_max - lch_unit) // sd_pitch - 1 od_x_list = fill_symmetric_max_density(area, area, 2, od_fg_max, od_spx_fg, offset=od_spx_fg, fill_on_edge=True, cyclic=False)[0] else: od_x_list = [] # get row OD list row_info_list = [RowInfo(od_x_list=od_x_list, od_y=od_y, od_type=('dum', sub_type), row_y=arr_y, po_y=po_y, md_y=md_y), ] # update extension information cur_edge_info = EdgeInfo(od_type=None, draw_layers={}, y_intv=dict(od=od_y, md=md_y)) # figure out poly types per finger po_types = [] od_intv_idx = 0 for cur_idx in range(num_blk): if cur_idx == 0 or cur_idx == num_blk - 1: od_type = left_blk_info[0].od_type if cur_idx == 0 else right_blk_info[0].od_type if od_type == 'mos': po_types.append('PO_edge') elif od_type == 'sub': po_types.append('PO_edge_sub') elif od_type == 'dum': po_types.append('PO_edge_dummy') else: po_types.append('PO_dummy') elif cur_idx < od_spx_fg - 1 or cur_idx >= num_blk - od_spx_fg + 1: po_types.append('PO_dummy') elif od_intv_idx < len(od_x_list): cur_od_intv = od_x_list[od_intv_idx] if cur_od_intv[1] == cur_idx: po_types.append('PO_edge_dummy') od_intv_idx += 1 elif cur_od_intv[0] <= cur_idx < cur_od_intv[1]: po_types.append('PO_gate_dummy') elif cur_idx == cur_od_intv[0] - 1: po_types.append('PO_edge_dummy') else: if cur_idx > cur_od_intv[1]: od_intv_idx += 1 po_types.append('PO_dummy') else: po_types.append('PO_dummy') # noinspection PyProtectedMember ext_top_info = row_ext_top._replace(po_types=po_types, edgel_info=cur_edge_info, edger_info=cur_edge_info) # noinspection PyProtectedMember ext_bot_info = row_ext_bot._replace(po_types=po_types, edgel_info=cur_edge_info, edger_info=cur_edge_info) lr_edge_info = (cur_edge_info, []) layout_info = dict( is_sub_row=is_sub, blk_type='sub' if is_sub else 'mos', lch_unit=lch_unit, fg=num_blk, arr_y=arr_y, draw_od=True, row_info_list=row_info_list, lay_info_list=lay_info_list, fill_info_list=fill_info_list, # edge parameters sub_type=sub_type, imp_params=imp_params, is_sub_ring=False, dnw_mode='', # adjacent block information list adj_row_list=[], left_blk_info=left_blk_info[0], right_blk_info=right_blk_info[0], ) # step 8: return results return dict( layout_info=layout_info, ext_top_info=ext_top_info, ext_bot_info=ext_bot_info, left_edge_info=lr_edge_info, right_edge_info=lr_edge_info, )