Example #1
0
    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)
Example #2
0
    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
Example #3
0
    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,
        )
Example #4
0
    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,
        )
Example #5
0
    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