def _get_od_y_list(self, blk_h: int, yl: int, yh: int, fin_p: int, fin_h: int, od_y_density: float) -> List[Tuple[int, int]]: po_od_exty: int = self._fill_config['po_od_exty'] po_spy: int = self._fill_config['po_spy'] nfin_min: int = self._fill_config['nfin_min'] nfin_max: int = self._fill_config['nfin_max'] od_spy_max: int = self._fill_config['od_spy_max'] fin_h2 = fin_h // 2 fin_sep_min = -(-(fin_h + po_spy + 2 * po_od_exty) // fin_p) fin_sep_max = (od_spy_max + fin_h) // fin_p fin_start = _get_fin_num(yl + po_od_exty + fin_h2, fin_p, round_up=True) fin_stop = _get_fin_num(yh - po_od_exty - fin_h2, fin_p, round_up=False) fin_area = fin_stop - fin_start area_specs = [(int(ceil(blk_h * od_y_density)), fin_p, fin_h)] info = fill_symmetric_min_density_info(fin_area, nfin_min - 1, nfin_max - 1, fin_sep_min, area_specs, sp_max=fin_sep_max, fill_on_edge=True, cyclic=False) d0 = fin_p * fin_start + (fin_p - fin_h) // 2 d1 = fin_p * fin_start + (fin_p + fin_h) // 2 return fill_symmetric_interval(info, d0=d0, d1=d1, scale=fin_p)
def _get_od_x_list(self, blk_w: int, fill_xl: int, fill_xh: int, sd_pitch: int, lch: int, od_spx: int) -> Tuple[List[Tuple[int, int]], float]: num_sd_min: int = self._fill_config['num_sd_min'] num_sd_max: int = self._fill_config['num_sd_max'] fill_w = fill_xh - fill_xl dum_spx = max(od_spx, sd_pitch - lch) num_sd_sep = -(-(dum_spx + lch) // sd_pitch) od_w_min = _get_od_w(num_sd_min, sd_pitch, lch) if fill_w < od_w_min: # too narrow; cannot draw anything return [], 0.0 num_sd_tot = _get_num_sd(fill_w, sd_pitch, lch, round_up=False) row_w = _get_od_w(num_sd_tot, sd_pitch, lch) row_xl = (fill_xl + fill_xh - row_w) // 2 if num_sd_tot <= num_sd_max: # we can just draw one dummy per row od_x_list = [(row_xl, row_xl + row_w)] od_x_density = row_w / blk_w else: # we need multiple dummies per row info = fill_symmetric_max_density_info(num_sd_tot, num_sd_min, num_sd_max, num_sd_sep, [(num_sd_tot, 1, 0)], fill_on_edge=True, cyclic=False) od_x_list = fill_symmetric_interval(info, d0=row_xl, d1=row_xl + lch, scale=sd_pitch) od_x_density = info.get_fill_area(sd_pitch, lch) / blk_w return od_x_list, od_x_density
def get_lr_edge_info( self, # type: ResTechFinfetBase grid, # type: RoutingGrid core_info, # type: Dict[str, Any] wedge, # 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 LR edge layout information. This method checks: 1. spacing rules. 2. PO density rules if all these pass, return LR edge layout information dictionary. """ well_end_mode = options.get('well_end_mode', 3) edge_margin = self.res_config['edge_margin'] po_lch = self.res_config['po_lch'] po_pitch = self.res_config['po_pitch'] od_fg_min = self.res_config['od_fg_min'] po_res_spx = self.res_config['po_res_spx'] res_max_density = self.res_config['res_max_density'] od_min_density = self.res_config['od_min_density'] po_spx = self.res_config['po_spx'] finfet_od_extx = self.res_config['finfet_od_extx'] imp_od_encx = self.res_config['imp_od_encx'] rtop_od_encx = self.res_config['rtop_od_encx'] wcore = core_info['width'] hcore = core_info['height'] core_lr_dum_w = core_info['lr_dum_w'] core_lr_od_loc = core_info['lr_od_loc'][0] core_top_od_loc = core_info['top_od_loc'][0] core_bot_od_loc = core_info['bot_od_loc'][0] core_fill_info = core_info['fill_info'] if well_end_mode & 2 == 0: edge_margin = self.res_config.get('edge_margin_nowell', edge_margin) wres, lres, wres_lr, lres_tb = self.get_res_dimension(l, w) # check spacing rule # get space between resistor and core boundary spx = (wcore - wres) // 2 # width of dummy transistor in left/right edge dum_w = po_lch + po_pitch * (od_fg_min - 1) # width is given by NW space/dummy/dummy-to-res-space/res/res-to-boundary-space wedge_min = edge_margin + imp_od_encx + dum_w + po_res_spx + wres_lr + spx if wedge < wedge_min: return None # check resistor density rule max_res_area = int(wedge * hcore * res_max_density) if wres_lr * lres > max_res_area: return None # number of dummy fingers for left edge of LREdge, also the center X coordinate avail_sp_xl = edge_margin + imp_od_encx avail_sp_xr = wedge - spx - wres_lr - po_res_spx avail_sp = avail_sp_xr - avail_sp_xl edge_lr_fg = (avail_sp - po_lch) // po_pitch + 1 edge_lr_dum_w = po_lch + (edge_lr_fg - 1) * po_pitch edge_lr_xc = avail_sp_xl + edge_lr_dum_w // 2 edge_lr_dum_xl = avail_sp_xl # number of dummy fingers for top/bottom edge of LREdge, also the center X coordinate avail_sp_xl = avail_sp_xl + edge_lr_dum_w + po_spx avail_sp_xr = wedge - core_lr_dum_w // 2 - po_spx edge_tb_xc = (avail_sp_xl + avail_sp_xr) // 2 avail_sp = avail_sp_xr - avail_sp_xl edge_tb_fg = (avail_sp - po_lch) // po_pitch + 1 edge_tb_dum_w = po_lch + (edge_tb_fg - 1) * po_pitch # compute total OD area od_area = 0 for od_w, yloc_list in ((edge_lr_dum_w, core_lr_od_loc), (edge_tb_dum_w, core_top_od_loc), (edge_tb_dum_w, core_bot_od_loc)): for yb, yt in yloc_list: # we do not add OD with bottom Y coordinates less than 0, # because of arraying over-counting issues if yb >= 0: od_area += od_w * (yt - yb) # check OD density rule min_od_area = int(math.ceil(hcore * wedge * od_min_density)) if od_area < min_od_area: return None # if we get here, then all density rules are met # compute fill X coordinate in edge block fill_edge_x_list = [] for _, _, w, h, core_x, core_y, density, sp_min, sp_max, sp_bnd in core_fill_info: sp_xl = sp_bnd + w sp_xr = wedge + core_x[0][0] area = sp_xr - sp_xl tarea = int(math.ceil(area * density**0.5)) c_info = fill_symmetric_min_density_info(area, tarea, w, w, sp_min, sp_max=sp_max, fill_on_edge=False) edge_x = fill_symmetric_interval(*c_info[0][2], offset=sp_xl, invert=c_info[1])[0] edge_x.insert(0, (sp_bnd, sp_xl)) fill_edge_x_list.append(edge_x) # return layout information well_xl = edge_lr_dum_xl - imp_od_encx return dict( lr_fg=edge_lr_fg, tb_fg=edge_tb_fg, lr_xc=edge_lr_xc, tb_xc=edge_tb_xc, fb_xl=edge_lr_dum_xl - finfet_od_extx, imp_xl=well_xl, well_xl=well_xl, rtop_xl=edge_lr_dum_xl - rtop_od_encx, fill_edge_x_list=fill_edge_x_list, )
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, # 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