Пример #1
0
def as_region(cell, layname):
    ''' Mostly a convenience brevity function.
        If a layer isn't in the layer set, return an empty region instead of crashing
    '''
    try:
        return pya.Region(cell.shapes(lys[layname]))
    except KeyError:
        return pya.Region()
Пример #2
0
def metal_pedestal(cell, pedestal_layer='wg_full_photo', offset=0, keepout=None):
    metal_region = pya.Region()
    for layname in ['m5_wiring', 'm5_gnd', 'gp_photo']:
        try:
            metal_region += as_region(cell, layname)
        except: pass
    valid_metal = metal_region - fast_sized(as_region(cell, lys.wg_deep), offset / dbu)
    pedestal_region = fast_sized(valid_metal, offset / dbu)
    if keepout is not None:
        if not isinstance(keepout, (list, tuple)):
            keepout = [keepout]
        for ko_layer in keepout:
            pedestal_region -= as_region(cell, ko_layer)
    cell.shapes(lys[pedestal_layer]).insert(pedestal_region)
Пример #3
0
def fast_sized(input_region, xsize):
    # if something goes wrong, you can fall back to regular here by uncommenting
    if _thread_count is None:
        return input_region.sized(xsize)
    else:
        output_region = pya.Region()
        tp = pya.TilingProcessor()
        tp.input('in1', input_region)
        tp.output('out1', output_region)
        tp.queue("_output(out1, in1.sized({}))".format(xsize))
        tp.tile_size(2000., 2000.)
        tp.tile_border(2 * xsize, 2 * xsize)
        tp.threads = _thread_count
        tp.execute('Sizing job')
        return output_region
Пример #4
0
def turbo(input_region,
          meth_name,
          meth_args,
          tile_border=1,
          job_name='Tiling job'):
    ''' Speeds things up by tiling. Parameters are determined by _thread_count and _tiles
        if _thread_count is 1, it does not invoke tile processor at all
        tile_border is in microns. Recommended that you make it 1.1 * the critical dimension.
        args is a list.
    '''
    if not isinstance(meth_args, (list, tuple)):
        meth_args = [meth_args]
    if _thread_count is None:
        return getattr(input_region, meth_name)(*meth_args)
    else:
        output_region = pya.Region()
        tp = pya.TilingProcessor()
        tp.input('in1', input_region)
        tp.output('out1', output_region)

        clean_args = list()
        for arg in meth_args:
            if arg is True:
                clean_args.append('true')
            elif arg is False:
                clean_args.append('false')
            elif arg is None:
                clean_args.append('nil')
            elif isinstance(arg, (pya.Region, pya.EdgePairs)):
                tp.input('in2', arg)
                clean_args.append('in2')
            else:
                clean_args.append(str(arg))
        job_str = '_output(out1, in1.{}({}))'.format(meth_name,
                                                     ', '.join(clean_args))
        tp.queue(job_str)

        tp.tile_border(tile_border, tile_border)
        tp.tiles(_tiles, _tiles)
        tp.threads = _thread_count
        tp.execute(job_name)
        return output_region
Пример #5
0
def ground_plane(cell, Delta_gp=15.0, points_per_circle=100, air_open=None):
    Delta_gp /= dbu
    cell.clear(lys.gp_photo)
    # Accumulate everything that we don't want to cover in metal
    gp_exclusion_things = pya.Region()
    for layname in ['wg_deep', 'wg_deep_photo', 'wg_shallow', 'm1_nwpad',
                    'm4_ledpad', 'm3_res', 'm5_wiring', 'm2_nw',
                    'GP_KO']:
        try:
            gp_exclusion_things += fast_smoothed(as_region(cell, layname))
        except KeyError: pass
    # Where ground plane is explicitly connected to wires, cut it out of the exclusion region
    gnd_explicit = as_region(cell, 'm5_gnd')
    gp_exclusion_tight = gp_exclusion_things - fast_sized(gnd_explicit, Delta_gp)
    # Inflate the buffer around excluded things and pour
    gp_exclusion_zone = fast_sized(gp_exclusion_tight, Delta_gp)
    gp_region = as_region(cell, 'FLOORPLAN') - gp_exclusion_zone

    # Connect to ground pads
    gp_region.merge()
    gp_region = fast_sized(gp_region, 1 / dbu)  # kill narrow spaces
    gp_region = fast_sized(gp_region, -2 / dbu)  # kill narrow widths
    gp_region = fast_sized(gp_region, 1 / dbu)
    gp_region += as_region(cell, 'm5_gnd')
    gp_region.round_corners(Delta_gp / 5, Delta_gp / 3, points_per_circle)
    gp_region = gp_region.smoothed(.001)  # avoid some bug in pya
    gp_region.merge()
    cell.shapes(lys.gp_photo).insert(gp_region)

    # Open up to the air
    if air_open is not None:
        Delta_air = 5
        fp_safe = as_region(cell, 'FLOORPLAN')
        air_rects = fp_safe - fp_safe.sized(0, -air_open / dbu, 0)
        air_region = air_rects & gp_region
        air_region = fast_sized(air_region, -Delta_air / dbu)
        air_region = fast_sized(air_region, 4 / dbu)  # kill narrow spaces
        air_region = fast_sized(air_region, -8 / dbu)  # kill narrow widths
        air_region = fast_sized(air_region, 4 / dbu)
        air_region.round_corners(Delta_gp / 5, Delta_gp / 3, points_per_circle)
        cell.shapes(lys.gp_v5).insert(air_region)
Пример #6
0
def fast_smoothed(unfiltered_region, deviation=0.1):
    ''' Removes any points that would change the shape by less than this deviation.
        This is used to significantly decrease the number of points prior to sizing

        Note: multicore does not work well with this, but it turns out to be pretty fast
    '''
    # if something goes wrong, you can fall back to regular here by uncommenting
    return _normal_smoothed(unfiltered_region, deviation)

    temp_region = unfiltered_region.dup()
    temp_region.merged_semantics = False

    output_region = pya.Region()
    tp = pya.TilingProcessor()
    tp.input('in1', temp_region)
    tp.output('out1', output_region)
    tp.queue("_output(out1, in1.smoothed({}))".format(deviation / dbu))
    tp.tile_size(2000., 2000.)
    tp.tile_border(5 * deviation, 5 * deviation)
    tp.threads = _thread_count
    tp.execute('Smoothing job')
    return output_region
Пример #7
0
def fast_space(input_region, spacing, angle=90):
    # if something goes wrong, you can fall back to regular here by uncommenting
    if _thread_count is None:
        return input_region.space_check(spacing)
    else:
        output_edge_pairs = pya.Region()
        tp = pya.TilingProcessor()
        tp.input('in1', input_region)
        tp.output('out1', output_edge_pairs)
        # tp.queue("_output(out1, in1.space_check({}))".format(spacing))
        tp.queue(
            "_output(out1, in1.space_check({}, false, nil, {}, nil, nil))".
            format(spacing, angle))

        border = 1.1 * spacing
        tp.tile_border(border, border)
        tp.tiles(_tiles, _tiles)
        # bbox = input_region.bbox()
        # die_wh = [bbox.width(), bbox.height()]
        # tile_wh = [dim / _tiles for dim in die_wh]
        # border_area = 2 * (tile_wh[0] + tile_wh[1]) * border
        tp.threads = _thread_count
        tp.execute('Spacing job')
        return output_edge_pairs
Пример #8
0
def run_xor(file1, file2, tolerance=1, verbose=False):
    ''' Returns nothing. Raises a GeometryDifference if there are differences detected
    '''

    l1 = pya.Layout()
    l1.read(file1)

    l2 = pya.Layout()
    l2.read(file2)

    # Check that same set of layers are present
    layer_pairs = []
    for ll1 in l1.layer_indices():
        li1 = l1.get_info(ll1)
        ll2 = l2.find_layer(l1.get_info(ll1))
        if ll2 is None:
            raise GeometryDifference(
                "Layer {} of layout {} not present in layout {}.".format(
                    li1, file1, file2))
        layer_pairs.append((ll1, ll2))

    for ll2 in l2.layer_indices():
        li2 = l2.get_info(ll2)
        ll1 = l1.find_layer(l2.get_info(ll2))
        if ll1 is None:
            raise GeometryDifference(
                "Layer {} of layout {} not present in layout {}.".format(
                    li2, file2, file1))

    # Check that topcells are the same
    tc1_names = [tc.name for tc in l1.top_cells()]
    tc2_names = [tc.name for tc in l2.top_cells()]
    tc1_names.sort()
    tc2_names.sort()
    if not tc1_names == tc2_names:
        raise GeometryDifference(
            "Missing topcell on one of the layouts, or name differs:\n{}\n{}".
            format(tc1_names, tc2_names))
    topcell_pairs = []
    for tc1_n in tc1_names:
        topcell_pairs.append((l1.cell(tc1_n), l2.cell(tc1_n)))

    # Check that dbu are the same
    if (l1.dbu - l2.dbu) > 1e-6:
        raise GeometryDifference(
            "Database unit of layout {} ({}) differs from that of layout {} ({})."
            .format(file1, l1.dbu, file2, l2.dbu))

    # Run the difftool
    diff = False
    for tc1, tc2 in topcell_pairs:
        for ll1, ll2 in layer_pairs:
            r1 = pya.Region(tc1.begin_shapes_rec(ll1))
            r2 = pya.Region(tc2.begin_shapes_rec(ll2))

            rxor = r1 ^ r2

            if tolerance > 0:
                rxor.size(-tolerance)

            if not rxor.is_empty():
                diff = True
                if verbose:
                    print("{} differences found in {} on layer {}.".format(
                        rxor.size(), tc1.name, l1.get_info(ll1)))
            else:
                if verbose:
                    print("No differences found in {} on layer {}.".format(
                        tc1.name, l1.get_info(ll1)))

    if diff:
        fn_abgd = []
        for fn in [file1, file2]:
            head, tail = os.path.split(fn)
            abgd = os.path.join(os.path.basename(head), tail)
            fn_abgd.append(abgd)
        raise GeometryDifference(
            "Differences found between layouts {} and {}".format(*fn_abgd))