Exemple #1
0
def generate_tisaradc_space(laygen,
                            objectname_pfix,
                            tisar_libname,
                            space_libname,
                            tisar_name,
                            space_name,
                            placement_grid,
                            routing_grid_m3m4_thick,
                            routing_grid_m4m5_thick,
                            routing_grid_m5m6_thick,
                            origin=np.array([0, 0])):
    """generate tisar space """
    pg = placement_grid
    ttisar = laygen.templates.get_template(tisar_name, libname=tisar_libname)
    tspace = laygen.templates.get_template(space_name, libname=space_libname)
    tbnd_bottom = laygen.templates.get_template('boundary_bottom')
    tbnd_bleft = laygen.templates.get_template('boundary_bottomleft')
    space_xy = np.array([tspace.size[0], ttisar.size[1]])
    laygen.add_rect(
        None,
        np.array([
            origin,
            origin + space_xy + 2 * tbnd_bleft.size[0] * np.array([1, 0])
        ]), laygen.layers['prbnd'])
    num_space = int(
        (ttisar.size[1] - 2 * tbnd_bottom.size[1]) / tspace.size[1])
    #space_xy=np.array([tspace.size[0], 56.88]) #change it after finishing the clock part
    #num_space=int((56.88-2*tbnd_bottom.size[1])/tspace.size[1]) #should be changed after finishing the clock part
    space_origin = origin + laygen.get_template_size('boundary_bottomleft', pg)
    ispace = [
        laygen.place(name="I" + objectname_pfix + 'SP0',
                     templatename=space_name,
                     gridname=pg,
                     xy=space_origin,
                     template_libname=space_libname)
    ]
    #devname_bnd_left = ['nmos4_fast_left', 'pmos4_fast_left']
    #devname_bnd_right = ['nmos4_fast_right', 'pmos4_fast_right']
    devname_bnd_left = ['ptap_fast_left', 'ntap_fast_left']
    devname_bnd_right = ['ptap_fast_right', 'ntap_fast_right']
    transform_bnd_left = ['R0', 'MX']
    transform_bnd_right = ['R0', 'MX']
    for i in range(1, num_space):
        if i % 2 == 0:
            ispace.append(
                laygen.relplace(name="I" + objectname_pfix + 'SP' + str(i),
                                templatename=space_name,
                                gridname=pg,
                                refinstname=ispace[-1].name,
                                direction='top',
                                transform='R0',
                                template_libname=space_libname))
            #devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            #devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            devname_bnd_left += ['ptap_fast_left', 'ntap_fast_left']
            devname_bnd_right += ['ptap_fast_right', 'ntap_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            ispace.append(
                laygen.relplace(name="I" + objectname_pfix + 'SP' + str(i),
                                templatename=space_name,
                                gridname=pg,
                                refinstname=ispace[-1].name,
                                direction='top',
                                transform='MX',
                                template_libname=space_libname))
            #devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            #devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            devname_bnd_left += ['ntap_fast_left', 'ptap_fast_left']
            devname_bnd_right += ['ntap_fast_right', 'ptap_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
            #transform_bnd_left +=  ['MX', 'R0']
            #transform_bnd_right += ['MX', 'R0']

    m_bnd = int(space_xy[0] / tbnd_bottom.size[0])
    [bnd_bottom, bnd_top, bnd_left, bnd_right] \
        = laygenhelper.generate_boundary(laygen, objectname_pfix='BND0', placement_grid=pg,
                            devname_bottom=['boundary_bottomleft', 'boundary_bottom', 'boundary_bottomright'],
                            shape_bottom=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_top=['boundary_topleft', 'boundary_top', 'boundary_topright'],
                            shape_top=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_left=devname_bnd_left,
                            transform_left=transform_bnd_left,
                            devname_right=devname_bnd_right,
                            transform_right=transform_bnd_right,
                            origin=origin)
    #vdd/vss
    #m3
    rvdd_xy_m3 = []
    rvss_xy_m3 = []
    space_template = laygen.templates.get_template(space_name, workinglib)
    space_pins = space_template.pins
    space_origin_phy = laygen.get_inst_bbox_phygrid(ispace[0].name)[0]
    vddcnt = 0
    vsscnt = 0
    for pn, p in space_pins.items():
        if pn.startswith('VDD'):
            pxy = space_origin_phy + np.array(
                [p['xy'][0], p['xy'][1] * np.array([1, num_space])])
            laygen.add_rect(None, pxy, p['layer'])
            rvdd_xy_m3.append(
                laygen.grids.get_absgrid_coord_region(gridname=rg_m3m4_thick,
                                                      xy0=pxy[0],
                                                      xy1=pxy[1]))
            vddcnt += 1
        if pn.startswith('VSS'):
            pxy = space_origin_phy + np.array(
                [p['xy'][0], p['xy'][1] * np.array([1, num_space])])
            laygen.add_rect(None, pxy, p['layer'])
            rvss_xy_m3.append(
                laygen.grids.get_absgrid_coord_region(gridname=rg_m3m4_thick,
                                                      xy0=pxy[0],
                                                      xy1=pxy[1]))
            vsscnt += 1
    #m4
    input_rails_xy = [rvdd_xy_m3, rvss_xy_m3]
    rvdd_m4, rvss_m4 = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        overwrite_num_routes=80,
        offset_start_index=0,
        offset_end_index=0)
    #exclude_phycoord_list=[[23.4,34.7]])
    #m5
    input_rails_rect = [rvdd_m4, rvss_m4]
    rvdd_m5, rvss_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M5_',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    #m6 (extract VDD/VSS grid from tisar and make power pins)
    rg_m5m6_thick_temp_tisar = 'route_M5_M6_thick_temp_tisar_VDD'
    laygenhelper.generate_grids_from_template(
        laygen,
        gridname_input=rg_m5m6_thick,
        gridname_output=rg_m5m6_thick_temp_tisar,
        template_name=tisar_name,
        template_libname=tisar_libname,
        template_pin_prefix=['VDD'],
        xy_grid_type='ygrid')
    input_rails_rect = [rvdd_m5]
    rvdd_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick_temp_tisar,
        netnames=['VDD'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=-1)
    rg_m5m6_thick_temp_tisar = 'route_M5_M6_thick_temp_tisar_VSS'
    laygenhelper.generate_grids_from_template(
        laygen,
        gridname_input=rg_m5m6_thick,
        gridname_output=rg_m5m6_thick_temp_tisar,
        template_name=tisar_name,
        template_libname=tisar_libname,
        template_pin_prefix=['VSS'],
        xy_grid_type='ygrid')
    input_rails_rect = [rvss_m5]
    rvss_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick_temp_tisar,
        netnames=['VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=-2)
def generate_r2r_dac_bcap_array(laygen,
                                objectname_pfix,
                                templib_logic,
                                placement_grid,
                                routing_grid_m4m5,
                                routing_grid_m5m6,
                                rg_m3m4_basic_thick,
                                rg_m5m6_thick,
                                m,
                                num_bits,
                                num_hori,
                                num_vert,
                                origin=np.array([0, 0])):
    """generate r2rdac """
    r2r_name = 'r2r_dac_bcap'
    sar_name = 'sar_wsamp_bb_doubleSA'
    ret_name = 'adc_retimer'
    tgate_name = 'tgate_' + str(m) + 'x'
    pg = placement_grid

    rg_m4m5 = routing_grid_m4m5
    rg_m5m6 = routing_grid_m5m6
    # rg_m4m5 = routing_grid_m4m5
    # rg_m4m5_basic_thick = routing_grid_m4m5_basic_thick
    # rg_m4m5_thick = routing_grid_m4m5_thick
    # rg_m5m6 = routing_grid_m5m6
    # rg_m5m6_thick = routing_grid_m5m6_thick
    # rg_m5m6_thick_basic = routing_grid_m5m6_thick_basic
    # rg_m6m7_thick = routing_grid_m6m7_thick

    # Calculate reference coordinate
    x1_phy = laygen.get_template_xy(
        name=r2r_name, gridname=None, libname=workinglib)[0] * num_hori
    pin_origin_x = laygen.grids.get_absgrid_x(rg_m5m6_thick_basic, x1_phy)
    y1_phy = origin[1] + laygen.get_template_xy(name=sar_name, gridname=None, libname=workinglib)[1] \
             + laygen.get_template_xy(name=ret_name, gridname=None, libname=workinglib)[1]
    pin_origin_y = laygen.grids.get_absgrid_y(rg_m5m6, y1_phy)
    pin_origin_y1_thick = origin[1] + \
                          laygen.get_template_pin_xy(sar_name, 'SF_BIAS', rg_m5m6_thick, libname=workinglib)[0][1] \
                          + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    pin_origin_y0_thick = origin[1] + \
                          laygen.get_template_pin_xy(sar_name, 'VREF_SF_BIAS', rg_m5m6_thick, libname=workinglib)[0][1] \
                          + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    # pin_origin_y0_thick = laygen.grids.get_absgrid_y(rg_m5m6_thick, y0_phy)

    # placement
    irdac = []
    for i in range(num_vert):
        if i == 0:
            irdac.append(
                laygen.relplace(name="I" + objectname_pfix + 'IBCAP' + str(i),
                                templatename=r2r_name,
                                gridname=pg,
                                refinstname=None,
                                xy=origin,
                                shape=[num_hori, 1],
                                template_libname=workinglib))
        else:
            irdac.append(
                laygen.relplace(name="I" + objectname_pfix + 'IBCAP' + str(i),
                                templatename=r2r_name,
                                gridname=pg,
                                refinstname=irdac[-1].name,
                                shape=[num_hori, 1],
                                template_libname=workinglib,
                                direction='top'))

    # output routing
    for i in range(num_hori):
        for j in range(num_vert):
            if i == num_hori - 1 and j == num_vert - 1:  # VREF_SF_BIAS routing with thick wire
                rv1, rh1 = laygen.route_vh(
                    laygen.layers['metal'][5],
                    laygen.layers['metal'][6],
                    xy0=laygen.get_inst_pin_xy(irdac[j].name,
                                               'I',
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[0] +
                    np.array([j, 0]),
                    xy1=np.array([pin_origin_x, pin_origin_y0_thick]),
                    gridname0=rg_m5m6_thick)
                laygen.via(
                    None,
                    xy=laygen.get_inst_pin_xy(
                        irdac[j].name, 'I', rg_m4m5, index=np.array(
                            [i, 0]))[0] + np.array([j, 0]),
                    gridname=rg_m4m5)
                laygen.boundary_pin_from_rect(rh1,
                                              rg_m5m6_thick,
                                              'out<' + str(num_hori * j + i) +
                                              '>',
                                              laygen.layers['pin'][6],
                                              size=4,
                                              direction='right')
            elif num_hori * j + i == num_slices * 3:  # SF_BIAS routing with thick wire
                rv1, rh1 = laygen.route_vh(
                    laygen.layers['metal'][5],
                    laygen.layers['metal'][6],
                    xy0=laygen.get_inst_pin_xy(irdac[j].name,
                                               'I',
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[0] +
                    np.array([j, 0]),
                    xy1=np.array([pin_origin_x, pin_origin_y1_thick]),
                    gridname0=rg_m5m6_thick)
                laygen.via(
                    None,
                    xy=laygen.get_inst_pin_xy(
                        irdac[j].name, 'I', rg_m4m5, index=np.array(
                            [i, 0]))[0] + np.array([j, 0]),
                    gridname=rg_m4m5)
                laygen.boundary_pin_from_rect(rh1,
                                              rg_m5m6_thick,
                                              'out<' + str(num_hori * j + i) +
                                              '>',
                                              laygen.layers['pin'][6],
                                              size=4,
                                              direction='right')
            else:
                rv0, rh0 = laygen.route_vh(
                    laygen.layers['metal'][5],
                    laygen.layers['metal'][6],
                    xy0=laygen.get_inst_pin_xy(irdac[j].name,
                                               'I',
                                               rg_m5m6_thick_basic,
                                               index=np.array([i, 0]))[0] +
                    np.array([j, 0]),
                    xy1=np.array(
                        [pin_origin_x, pin_origin_y + 4 + num_hori * j + i]),
                    gridname0=rg_m5m6_thick_basic)
                laygen.via(
                    None,
                    xy=laygen.get_inst_pin_xy(irdac[j].name,
                                              'I',
                                              rg_m4m5_thick,
                                              index=np.array([i, 0]))[0] +
                    np.array([j, 0]),
                    gridname=rg_m4m5_thick)
                laygen.boundary_pin_from_rect(rh0,
                                              rg_m5m6_thick_basic,
                                              'out<' + str(num_hori * j + i) +
                                              '>',
                                              laygen.layers['pin'][6],
                                              size=4,
                                              direction='right')
    pin_origin_y2_thick = laygen.grids.get_absgrid_y(
        rg_m5m6_thick,
        laygen.grids.get_phygrid_y(rg_m5m6,
                                   pin_origin_y + 4 + num_hori * num_vert))
    # m5 supply
    pdict_m5m6_thick = laygen.get_inst_pin_xy(None, None, rg_m5m6_thick)
    rvdd_m5 = []
    rvss_m5 = []
    for i in range(num_hori):
        for p in pdict_m5m6_thick[irdac[0].name]:
            if p.startswith('VDD'):
                r0 = laygen.route(
                    None,
                    laygen.layers['metal'][5],
                    xy0=laygen.get_inst_pin_xy(irdac[0].name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[0],
                    xy1=laygen.get_inst_pin_xy(irdac[num_vert - 1].name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[1],
                    gridname0=rg_m5m6_thick)
                rvdd_m5.append(r0)
        for p in pdict_m5m6_thick[irdac[0].name]:
            if p.startswith('VSS'):
                r0 = laygen.route(
                    None,
                    laygen.layers['metal'][5],
                    xy0=laygen.get_inst_pin_xy(irdac[0].name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[0],
                    xy1=laygen.get_inst_pin_xy(irdac[num_vert - 1].name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[1],
                    gridname0=rg_m5m6_thick)
                rvss_m5.append(r0)

    # m6
    print(pin_origin_y0_thick, pin_origin_y1_thick, pin_origin_y2_thick)
    input_rails_rect = [rvdd_m5, rvss_m5]
    rvdd_m6_0, rvss_m6_0 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_0_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        overwrite_end_index=pin_origin_y0_thick - 2)
    rvdd_m6_1, rvss_m6_1 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_1_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        overwrite_start_index=pin_origin_y0_thick + 2,
        overwrite_end_index=pin_origin_y1_thick - 2)
    rvdd_m6_2, rvss_m6_2 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_2_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        overwrite_start_index=pin_origin_y2_thick + 2,
        offset_end_index=0)
Exemple #3
0
def generate_samp(laygen,
                  objectname_pfix,
                  workinglib,
                  placement_grid='placement_basic',
                  routing_grid_m4m5='route_M4_M5_basic_thick',
                  power_grid_m3m4='route_M3_M4_basic_thick',
                  power_grid_m4m5='route_M4_M5_thick',
                  power_grid_m5m6='route_M5_M6_thick',
                  origin=np.array([0, 0])):
    """generate a sampler with clock buffers. used when AnalogMOS is not available
    """
    #variable/cell namings
    pg = placement_grid
    rg45bt = routing_grid_m4m5
    pg34bt = power_grid_m3m4
    pg45t = power_grid_m4m5
    pg56t = power_grid_m5m6

    # placement
    core_origin = origin + laygen.get_xy(
        obj=laygen.get_template(name='boundary_bottomleft'), gridname=pg)
    # sampler body
    isamp = laygen.relplace(name=None,
                            templatename='sarsamp_body',
                            gridname=pg,
                            template_libname=workinglib,
                            transform='R0',
                            xy=core_origin)
    # clock buffer
    if tgate == True:
        ibuf = laygen.relplace(name=None,
                               templatename='sarsamp_buf',
                               gridname=pg,
                               refobj=isamp,
                               direction='top',
                               template_libname=workinglib,
                               transform='MX')
    else:
        ibuf = laygen.relplace(name=None,
                               templatename='sarsamp_buf',
                               gridname=pg,
                               refobj=isamp,
                               direction='top',
                               template_libname=workinglib,
                               transform='R0')
    # boundaries
    a = laygen.get_xy(obj=laygen.get_template(name='sarsamp_body',
                                              libname=workinglib),
                      gridname=pg)[0]
    b = laygen.get_xy(obj=laygen.get_template(name='boundary_bottom'),
                      gridname=pg)[0]
    m_bnd = int(a / b)
    if tgate == True:
        devname_bnd_left = ['nmos4_fast_left', 'pmos4_fast_left'
                            ] + ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right = ['nmos4_fast_right', 'pmos4_fast_right'
                             ] + ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left = ['R0', 'MX'] + ['MX', 'R0']
        transform_bnd_right = ['R0', 'MX'] + ['MX', 'R0']
    else:
        devname_bnd_left = ['nmos4_fast_left', 'nmos4_fast_left'
                            ] + ['nmos4_fast_left', 'pmos4_fast_left']
        devname_bnd_right = ['nmos4_fast_right', 'nmos4_fast_right'
                             ] + ['nmos4_fast_right', 'pmos4_fast_right']
        transform_bnd_left = ['R0', 'MX'] + ['R0', 'MX']
        transform_bnd_right = ['R0', 'MX'] + ['R0', 'MX']
    [bnd_bottom, bnd_top, bnd_left, bnd_right] \
        = laygenhelper.generate_boundary(laygen, objectname_pfix='BND0', placement_grid=pg,
                            devname_bottom=['boundary_bottomleft', 'boundary_bottom', 'boundary_bottomright'],
                            shape_bottom=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_top=['boundary_topleft', 'boundary_top', 'boundary_topright'],
                            shape_top=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_left=devname_bnd_left,
                            transform_left=transform_bnd_left,
                            devname_right=devname_bnd_right,
                            transform_right=transform_bnd_right,
                            origin=origin)

    # route
    x_center=int((laygen.get_inst_bbox(name=ibuf.name, gridname=rg45bt, sort=True)[1][0] \
                  -laygen.get_inst_bbox(name=ibuf.name, gridname=rg45bt, sort=True)[0][0])/2\
                  +laygen.get_inst_bbox(name=ibuf.name, gridname=rg45bt, sort=True)[0][0])
    y_top = int(
        laygen.get_inst_bbox(name=ibuf.name, gridname=rg45bt, sort=True)[1][1])
    #in
    xy0 = laygen.get_inst_pin_xy(name=isamp.name,
                                 pinname='IP',
                                 gridname=rg45bt,
                                 sort=True)[0]
    xy1 = [x_center - 3, y_top]
    rh0, rinp0 = laygen.route_hv(xy0=xy0, xy1=xy1, gridname0=rg45bt)
    xy0 = laygen.get_inst_pin_xy(name=isamp.name,
                                 pinname='IM',
                                 gridname=rg45bt,
                                 sort=True)[0]
    xy1 = [x_center + 3, y_top]
    rh0, rinn0 = laygen.route_hv(xy0=xy0, xy1=xy1, gridname0=rg45bt)
    #out
    xy0 = laygen.get_inst_pin_xy(name=isamp.name,
                                 pinname='OP',
                                 gridname=rg45bt,
                                 sort=True)[0]
    xy1 = [x_center - 3, 0]
    rh0, routp0 = laygen.route_hv(xy0=xy0, xy1=xy1, gridname0=rg45bt)
    xy0 = laygen.get_inst_pin_xy(name=isamp.name,
                                 pinname='OM',
                                 gridname=rg45bt,
                                 sort=True)[0]
    xy1 = [x_center + 3, 0]
    rh0, routn0 = laygen.route_hv(xy0=xy0, xy1=xy1, gridname0=rg45bt)
    #en
    xy0 = laygen.get_inst_pin_xy(name=ibuf.name,
                                 pinname='OUT_SW',
                                 gridname=rg45bt,
                                 sort=True)[0]
    xy1 = laygen.get_inst_pin_xy(name=isamp.name,
                                 pinname='EN',
                                 gridname=rg45bt,
                                 sort=True)[0]
    rh0, rckpg0, rh1 = laygen.route_hvh(xy0=xy0,
                                        xy1=xy1,
                                        track_x=x_center - 1,
                                        gridname0=rg45bt)
    rh0, rv0, rh1 = laygen.route_hvh(xy0=xy0,
                                     xy1=xy1,
                                     track_x=x_center + 1,
                                     gridname0=rg45bt)
    #enb
    if tgate == True:
        xy0 = laygen.get_inst_pin_xy(name=ibuf.name,
                                     pinname='out_int<0>',
                                     gridname=rg45bt,
                                     sort=True)[0]
        xy1 = laygen.get_inst_pin_xy(name=isamp.name,
                                     pinname='ENB',
                                     gridname=rg45bt,
                                     sort=True)[0]
        rh0, rckint0, rh1 = laygen.route_hvh(xy0=xy0,
                                             xy1=xy1,
                                             track_x=x_center - 2,
                                             gridname0=rg45bt)
        rh0, rv0, rh1 = laygen.route_hvh(xy0=xy0,
                                         xy1=xy1,
                                         track_x=x_center + 2,
                                         gridname0=rg45bt)
    #ckin
    xy0 = laygen.get_inst_pin_xy(name=ibuf.name,
                                 pinname='IN',
                                 gridname=rg45bt,
                                 sort=True)[0]
    xy1 = [xy0[0], y_top]
    rh0, rckin0 = laygen.route_hv(xy0=xy0, xy1=xy1, gridname0=rg45bt)
    #ckout
    xy0 = laygen.get_inst_pin_xy(name=ibuf.name,
                                 pinname='OUT_BUF',
                                 gridname=rg45bt,
                                 sort=True)[0]
    xy1 = [x_center, 0]
    rh0, rckout0 = laygen.route_hv(xy0=xy0, xy1=xy1, gridname0=rg45bt)

    # signal pins
    for p, r in zip(['inp', 'inn', 'outp', 'outn', 'ckin', 'ckout', 'ckpg'],
                    [rinp0, rinn0, routp0, routn0, rckin0, rckout0, rckpg0]):
        laygen.pin(name=p,
                   layer=laygen.layers['pin'][5],
                   refobj=r,
                   gridname=rg45bt)

    #vdd/vss - route
    #samp_m3_xy
    rvss_samp_m3 = [[], []]
    for pn, p in laygen.get_inst(isamp.name).pins.items():
        if pn.startswith('VSSL'):
            xy = laygen.get_inst_pin_xy(name=isamp.name,
                                        pinname=pn,
                                        gridname=pg34bt,
                                        sort=True)
            rvss_samp_m3[0].append(xy)
        if pn.startswith('VSSR'):
            xy = laygen.get_inst_pin_xy(name=isamp.name,
                                        pinname=pn,
                                        gridname=pg34bt,
                                        sort=True)
            rvss_samp_m3[1].append(xy)
    #samp_m4
    rvss_samp_m4 = [None, None]
    input_rails_xy = [rvss_samp_m3[0]]
    rvss_samp_m4[0] = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_SAMPL_M4_',
        layer=laygen.layers['metal'][4],
        gridname=pg34bt,
        netnames=['VSS'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=-1)[0]
    input_rails_xy = [rvss_samp_m3[1]]
    rvss_samp_m4[1] = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_SAMPR_M4_',
        layer=laygen.layers['metal'][4],
        gridname=pg34bt,
        netnames=['VSS'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=-1)[0]
    #buf_m3_xy
    rvdd_buf_m3 = [[], []]
    rvss_buf_m3 = [[], []]
    for pn, p in laygen.get_inst(ibuf.name).pins.items():
        if pn.startswith('VSSL'):
            xy = laygen.get_inst_pin_xy(name=ibuf.name,
                                        pinname=pn,
                                        gridname=pg34bt,
                                        sort=True)
            rvss_buf_m3[0].append(xy)
        if pn.startswith('VSSR'):
            xy = laygen.get_inst_pin_xy(name=ibuf.name,
                                        pinname=pn,
                                        gridname=pg34bt,
                                        sort=True)
            rvss_buf_m3[1].append(xy)
        if pn.startswith('VDDL'):
            xy = laygen.get_inst_pin_xy(name=ibuf.name,
                                        pinname=pn,
                                        gridname=pg34bt,
                                        sort=True)
            rvdd_buf_m3[0].append(xy)
        if pn.startswith('VDDR'):
            xy = laygen.get_inst_pin_xy(name=ibuf.name,
                                        pinname=pn,
                                        gridname=pg34bt,
                                        sort=True)
            rvdd_buf_m3[1].append(xy)
    #buf_m4
    rvss_buf_m4 = [None, None]
    rvdd_buf_m4 = [None, None]
    input_rails_xy = [rvdd_buf_m3[0], rvss_buf_m3[0]]
    rvdd_buf_m4[0], rvss_buf_m4[
        0] = laygenhelper.generate_power_rails_from_rails_xy(
            laygen,
            routename_tag='_BUFL_M4_',
            layer=laygen.layers['metal'][4],
            gridname=pg34bt,
            netnames=['VDD', 'VSS'],
            direction='x',
            input_rails_xy=input_rails_xy,
            generate_pin=False,
            overwrite_start_coord=None,
            overwrite_end_coord=None,
            offset_start_index=1,
            offset_end_index=0)
    input_rails_xy = [rvdd_buf_m3[1], rvss_buf_m3[1]]
    rvdd_buf_m4[1], rvss_buf_m4[
        1] = laygenhelper.generate_power_rails_from_rails_xy(
            laygen,
            routename_tag='_BUFR_M4_',
            layer=laygen.layers['metal'][4],
            gridname=pg34bt,
            netnames=['VDD', 'VSS'],
            direction='x',
            input_rails_xy=input_rails_xy,
            generate_pin=False,
            overwrite_start_coord=None,
            overwrite_end_coord=None,
            offset_start_index=1,
            offset_end_index=0)
    #m4
    rvss_m4 = [
        rvss_samp_m4[0] + rvss_buf_m4[0], rvss_samp_m4[1] + rvss_buf_m4[1]
    ]
    rvdd_m4 = rvdd_buf_m4
    #m5
    rvss_m5 = [None, None]
    rvdd_m5 = [None, None]
    input_rails_rect = [rvdd_m4[0], rvss_m4[0]]
    rvdd_m5[0], rvss_m5[0] = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='L_M5_',
        layer=laygen.layers['metal'][5],
        gridname=pg45t,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    input_rails_rect = [rvdd_m4[1], rvss_m4[1]]
    rvdd_m5[1], rvss_m5[1] = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='R_M5_',
        layer=laygen.layers['metal'][5],
        gridname=pg45t,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    #m6
    input_rails_rect = [rvdd_m5[0] + rvdd_m5[1], rvss_m5[0] + rvss_m5[1]]
    x1 = laygen.get_inst_bbox(
        name=ibuf.name, gridname=pg56t)[1][0] + laygen.get_xy(
            obj=laygen.get_template(name='nmos4_fast_left'), gridname=pg56t)[0]
    rvdd_m6, rvss_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_',
        layer=laygen.layers['metal'][6],
        gridname=pg56t,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=x1,
        offset_start_index=0,
        offset_end_index=0)
    #trimming and pinning
    x1_phy = laygen.get_inst_bbox(name=ibuf.name)[1][0] + laygen.get_xy(
        obj=laygen.get_template(name='nmos4_fast_left'))[0]
    for r in rvdd_m6:
        r.xy1[0] = x1_phy
        p = laygen.pin(name='VDD_M6_' + r.name,
                       layer=laygen.layers['pin'][6],
                       refobj=r,
                       gridname=pg56t,
                       netname='VDD')
        p.xy1[0] = x1_phy
    for r in rvss_m6:
        r.xy1[0] = x1_phy
        p = laygen.pin(name='VSS_M6_' + r.name,
                       layer=laygen.layers['pin'][6],
                       refobj=r,
                       gridname=pg56t,
                       netname='VSS')
        p.xy1[0] = x1_phy
Exemple #4
0
def generate_tisaradc_space2(laygen,
                             objectname_pfix,
                             tisar_libname,
                             space_libname,
                             tisar_name,
                             space_name,
                             placement_grid,
                             routing_grid_m3m4_thick,
                             routing_grid_m4m5_thick,
                             routing_grid_m5m6_thick,
                             origin=np.array([0, 0])):
    """generate tisar space """
    pg = placement_grid
    ttisar = laygen.templates.get_template(tisar_name, libname=tisar_libname)
    tspace = laygen.templates.get_template(space_name, libname=space_libname)

    sar_pins = ttisar.pins

    tbnd_bottom = laygen.templates.get_template('boundary_bottom')
    tbnd_bleft = laygen.templates.get_template('boundary_bottomleft')
    space_xy = np.array([tspace.size[0], ttisar.size[1]])
    laygen.add_rect(
        None,
        np.array([
            origin,
            origin + space_xy + 2 * tbnd_bleft.size[0] * np.array([1, 0])
        ]), laygen.layers['prbnd'])
    num_space_tot = int(
        (ttisar.size[1] - 2 * tbnd_bottom.size[1]) / tspace.size[1])
    tbnd_bleft_size = tbnd_bleft.size

    #VDD/VSS/VREF integration
    rvddclkd = []
    rvddsamp = []
    rvddsar = []
    rvddsar_upper = []
    rvref0 = []
    rvref1 = []
    rvref2 = []
    rvssclkd = []
    rvsssamp = []
    rvsssar = []
    rvsssar_upper = []
    vddclkd_xy = []
    vddsamp_xy = []
    vddsar_xy = []
    vddsar_upper_xy = []
    vssclkd_xy = []
    vsssamp_xy = []
    vsssar_xy = []
    vsssar_upper_xy = []
    y_vddsar_max = 0
    y_vddsar_lower_max = 0
    y_vddsamp_min = 500000
    y_vddsamp_max = 0
    y_vddclkd_min = 500000
    y_vref0 = sar_pins['VREF0<0>']['xy'][0][1]

    #categorize vdd pins and figure out thresholds
    for pn, p in sar_pins.items():
        '''
        if pn.startswith('VDDCLKD'):
            pxy=np.array([[0, sar_pins[pn]['xy'][0][1]], 
                          [2*tbnd_bleft_size[0]+space_xy[0], sar_pins[pn]['xy'][1][1]]])
            rvddclkd.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pxy[1][1]<y_vddclkd_min:
                y_vddclkd_min=pxy[1][1]
            vddclkd_xy.append(pxy)
        '''
        if pn.startswith('VDDSAMP'):
            pxy = np.array([[0, sar_pins[pn]['xy'][0][1]],
                            [
                                2 * tbnd_bleft_size[0] + space_xy[0],
                                sar_pins[pn]['xy'][1][1]
                            ]])
            rvddsamp.append(
                laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pxy[1][1] < y_vddsamp_min:
                y_vddsamp_min = pxy[1][1]
            if pxy[1][1] > y_vddsamp_max:
                y_vddsamp_max = pxy[1][1]
            vddsamp_xy.append(pxy)
        if pn.startswith('VDDSAR'):
            pxy = np.array([[0, sar_pins[pn]['xy'][0][1]],
                            [
                                2 * tbnd_bleft_size[0] + space_xy[0],
                                sar_pins[pn]['xy'][1][1]
                            ]])
            if pxy[0][1] > y_vref0:
                rvddsar_upper.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
                vddsar_upper_xy.append(pxy)
            else:
                rvddsar.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
                vddsar_xy.append(pxy)
                if pxy[1][1] > y_vddsar_lower_max:
                    y_vddsar_lower_max = pxy[1][1]
            if pxy[1][1] > y_vddsar_max:
                y_vddsar_max = pxy[1][1]
        if pn.startswith('VREF'):
            pxy = np.array([[0, sar_pins[pn]['xy'][0][1]],
                            [
                                2 * tbnd_bleft_size[0] + space_xy[0],
                                sar_pins[pn]['xy'][1][1]
                            ]])
            if pn.endswith('<0>'):
                rvref0.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pn.endswith('<1>'):
                rvref1.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pn.endswith('<2>'):
                rvref2.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
    y_vss_th = 0.5 * (y_vddsar_max + y_vddsamp_min
                      )  #find out threshold (sar/samp)
    #y_vss_th2=0.5*(y_vddsamp_max+y_vddclkd_min) #(samp/clkd)
    y_vss_th2 = 100000
    for pn, p in sar_pins.items():
        if pn.startswith('VSS'):
            pxy = np.array([[0, sar_pins[pn]['xy'][0][1]],
                            [
                                2 * tbnd_bleft_size[0] + space_xy[0],
                                sar_pins[pn]['xy'][1][1]
                            ]])
            if pxy[0][1] > y_vss_th2:
                rvssclkd.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
                vssclkd_xy.append(pxy)
            elif pxy[0][1] > y_vss_th:
                rvsssamp.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
                vsssamp_xy.append(pxy)
            elif pxy[0][1] > y_vref0:
                rvsssar_upper.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
                vsssar_upper_xy.append(pxy)
            else:
                rvsssar.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
                vsssar_xy.append(pxy)

    #vddsar cap
    num_space_sar = int(
        (y_vss_th - 2 * tbnd_bottom.size[1]) / tspace.size[1]) + 4
    space_origin = origin + laygen.get_template_size('boundary_bottomleft', pg)
    ispace_sar = [
        laygen.place(name="I" + objectname_pfix + 'SPSAR0',
                     templatename=space_name,
                     gridname=pg,
                     xy=space_origin,
                     template_libname=space_libname)
    ]
    devname_bnd_left = ['nmos4_fast_left', 'pmos4_fast_left']
    devname_bnd_right = ['nmos4_fast_right', 'pmos4_fast_right']
    transform_bnd_left = ['R0', 'MX']
    transform_bnd_right = ['R0', 'MX']

    for i in range(1, num_space_sar):
        if i % 2 == 0:
            ispace_sar.append(
                laygen.relplace(name="I" + objectname_pfix + 'SPSAR' + str(i),
                                templatename=space_name,
                                gridname=pg,
                                refinstname=ispace_sar[-1].name,
                                direction='top',
                                transform='R0',
                                template_libname=space_libname))
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            ispace_sar.append(
                laygen.relplace(name="I" + objectname_pfix + 'SPSAR' + str(i),
                                templatename=space_name,
                                gridname=pg,
                                refinstname=ispace_sar[-1].name,
                                direction='top',
                                transform='MX',
                                template_libname=space_libname))
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']

    m_bnd = int(space_xy[0] / tbnd_bottom.size[0])
    [bnd_bottom, bnd_top, bnd_left, bnd_right] \
        = laygenhelper.generate_boundary(laygen, objectname_pfix='BNDSAR0', placement_grid=pg,
                            devname_bottom=['boundary_bottomleft', 'boundary_bottom', 'boundary_bottomright'],
                            shape_bottom=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_top=['boundary_topleft', 'boundary_top', 'boundary_topright'],
                            shape_top=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_left=devname_bnd_left,
                            transform_left=transform_bnd_left,
                            devname_right=devname_bnd_right,
                            transform_right=transform_bnd_right,
                            origin=origin)

    #vddsamp cap
    num_space_samp = num_space_tot - num_space_sar - 1
    space_origin = origin + laygen.get_template_size(
        'boundary_bottomleft', pg) * np.array([1, (3 + 2 * num_space_sar)])
    ispace_samp = [
        laygen.place(name="I" + objectname_pfix + 'SPSAMP0',
                     templatename=space_name,
                     gridname=pg,
                     xy=space_origin,
                     template_libname=space_libname)
    ]
    devname_bnd_left = ['nmos4_fast_left', 'pmos4_fast_left']
    devname_bnd_right = ['nmos4_fast_right', 'pmos4_fast_right']
    transform_bnd_left = ['R0', 'MX']
    transform_bnd_right = ['R0', 'MX']

    for i in range(1, num_space_samp):
        if i % 2 == 0:
            ispace_samp.append(
                laygen.relplace(name="I" + objectname_pfix + 'SPSAMP' + str(i),
                                templatename=space_name,
                                gridname=pg,
                                refinstname=ispace_samp[-1].name,
                                direction='top',
                                transform='R0',
                                template_libname=space_libname))
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            ispace_samp.append(
                laygen.relplace(name="I" + objectname_pfix + 'SPSAMP' + str(i),
                                templatename=space_name,
                                gridname=pg,
                                refinstname=ispace_samp[-1].name,
                                direction='top',
                                transform='MX',
                                template_libname=space_libname))
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']

    m_bnd = int(space_xy[0] / tbnd_bottom.size[0])
    [bnd_bottom, bnd_top, bnd_left, bnd_right] \
        = laygenhelper.generate_boundary(laygen, objectname_pfix='BNDSAMP0', placement_grid=pg,
                            devname_bottom=['boundary_bottomleft', 'boundary_bottom', 'boundary_bottomright'],
                            shape_bottom=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_top=['boundary_topleft', 'boundary_top', 'boundary_topright'],
                            shape_top=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_left=devname_bnd_left,
                            transform_left=transform_bnd_left,
                            devname_right=devname_bnd_right,
                            transform_right=transform_bnd_right,
                            origin=space_origin-laygen.get_template_size('boundary_bottomleft', pg))
    #vdd/vss
    #m3
    rvdd_sar_xy_m3 = []
    rvss_sar_xy_m3 = []
    space_template = laygen.templates.get_template(space_name, workinglib)
    space_pins = space_template.pins
    space_origin_phy = laygen.get_inst_bbox_phygrid(ispace_sar[0].name)[0]
    vddcnt = 0
    vsscnt = 0
    for pn, p in space_pins.items():
        if pn.startswith('VDD'):
            pxy = space_origin_phy + np.array(
                [p['xy'][0], p['xy'][1] * np.array([1, num_space_sar])])
            pxy[1][1] = y_vddsar_lower_max
            laygen.add_rect(None, pxy, laygen.layers['metal'][3])
            rvdd_sar_xy_m3.append(
                laygen.grids.get_absgrid_coord_region(gridname=rg_m3m4_thick,
                                                      xy0=pxy[0],
                                                      xy1=pxy[1]))
            vddcnt += 1
        if pn.startswith('VSS'):
            pxy = space_origin_phy + np.array(
                [p['xy'][0], p['xy'][1] * np.array([1, num_space_sar])])
            pxy[1][1] = y_vddsar_lower_max
            laygen.add_rect(None, pxy, laygen.layers['metal'][3])
            rvss_sar_xy_m3.append(
                laygen.grids.get_absgrid_coord_region(gridname=rg_m3m4_thick,
                                                      xy0=pxy[0],
                                                      xy1=pxy[1]))
            vsscnt += 1
    rvdd_samp_xy_m3 = []
    rvss_samp_xy_m3 = []
    space_template = laygen.templates.get_template(space_name, workinglib)
    space_pins = space_template.pins
    space_origin_phy = laygen.get_inst_bbox_phygrid(ispace_samp[0].name)[0]
    vddcnt = 0
    vsscnt = 0
    for pn, p in space_pins.items():
        if pn.startswith('VDD'):
            pxy = space_origin_phy + np.array(
                [p['xy'][0], p['xy'][1] * np.array([1, num_space_samp])])
            laygen.add_rect(None, pxy, laygen.layers['metal'][3])
            rvdd_samp_xy_m3.append(
                laygen.grids.get_absgrid_coord_region(gridname=rg_m3m4_thick,
                                                      xy0=pxy[0],
                                                      xy1=pxy[1]))
            vddcnt += 1
        if pn.startswith('VSS'):
            pxy = space_origin_phy + np.array(
                [p['xy'][0], p['xy'][1] * np.array([1, num_space_samp])])
            laygen.add_rect(None, pxy, laygen.layers['metal'][3])
            rvss_samp_xy_m3.append(
                laygen.grids.get_absgrid_coord_region(gridname=rg_m3m4_thick,
                                                      xy0=pxy[0],
                                                      xy1=pxy[1]))
            vsscnt += 1
    #m4
    input_rails_xy = [rvdd_samp_xy_m3, rvss_samp_xy_m3]
    rvdd_samp_m4, rvss_samp_m4 = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_M4_SAMP_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDDSAMP', 'VSSSAMP'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    input_rails_xy = [rvdd_sar_xy_m3, rvss_sar_xy_m3]
    rvdd_sar_m4, rvss_sar_m4 = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_M4_SAR_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDDSAR', 'VSSSAR'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    #m5
    input_rails_rect = [rvdd_samp_m4, rvss_samp_m4]
    rvdd_samp_m5, rvss_samp_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M5_SAMP',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    input_rails_rect = [rvdd_sar_m4, rvss_sar_m4]
    rvdd_sar_m5, rvss_sar_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M5_SAR',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)

    #m6 (extract VDD/VSS grid from tisar and make power pins)
    rg_m5m6_sar_vdd = 'route_M5_M6_thick_temp_tisar_sar_vdd'
    laygenhelper.generate_grids_from_xy(laygen,
                                        gridname_input=rg_m5m6_thick,
                                        gridname_output=rg_m5m6_sar_vdd,
                                        xy=vddsar_xy,
                                        xy_grid_type='ygrid')
    input_rails_rect = [rvdd_sar_m5]
    [rvdd_sar_m6] = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_SAR_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_sar_vdd,
        netnames=['VDDSAR'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)

    rg_m5m6_sar_vss = 'route_M5_M6_thick_temp_tisar_sar_vss'
    laygenhelper.generate_grids_from_xy(laygen,
                                        gridname_input=rg_m5m6_thick,
                                        gridname_output=rg_m5m6_sar_vss,
                                        xy=vsssar_xy,
                                        xy_grid_type='ygrid')
    input_rails_rect = [rvss_sar_m5]
    [rvss_sar_m6] = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_SAR_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_sar_vss,
        netnames=['VSS:'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)

    rg_m5m6_samp = 'route_M5_M6_thick_temp_tisar_samp_vdd'
    laygenhelper.generate_grids_from_xy(laygen,
                                        gridname_input=rg_m5m6_thick,
                                        gridname_output=rg_m5m6_samp,
                                        xy=vddsamp_xy,
                                        xy_grid_type='ygrid')
    input_rails_rect = [rvdd_samp_m5]
    [rvdd_samp_m6] = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_SAMP_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_samp,
        netnames=['VDDSAMP'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    rg_m5m6_samp = 'route_M5_M6_thick_temp_tisar_samp_vss'
    laygenhelper.generate_grids_from_xy(laygen,
                                        gridname_input=rg_m5m6_thick,
                                        gridname_output=rg_m5m6_samp,
                                        xy=vsssamp_xy,
                                        xy_grid_type='ygrid')
    input_rails_rect = [rvss_samp_m5]
    [rvss_samp_m6] = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_SAMP_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_samp,
        netnames=['VSS:'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    '''
    rg_m5m6_clkd='route_M5_M6_thick_temp_tisar_clkd'
    laygenhelper.generate_grids_from_xy(laygen, gridname_input=rg_m5m6_thick, gridname_output=rg_m5m6_clkd, xy=vddclkd_xy + vssclkd_xy, xy_grid_type='ygrid')
    input_rails_rect = [rvdd_samp_m5, rvss_samp_m5]
    [rvdd_clkd_m6, rvss_clkd_m6] = laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_M6_CLKD_', 
                            layer=laygen.layers['pin'][6], gridname=rg_m5m6_clkd, netnames=['VDDSAMP', 'VSS:'], direction='x', 
                            input_rails_rect=input_rails_rect, generate_pin=True, overwrite_start_coord=None, overwrite_end_coord=None,
                            offset_start_index=0, offset_end_index=0)
    '''
    print(num_space_sar, num_space_samp)

    yamlfile_output = "adc_sar_size.yaml"
    #write to file
    with open(yamlfile_output, 'r') as stream:
        outdict = yaml.load(stream)
    outdict['num_space_sar'] = num_space_sar
    outdict['num_space_samp'] = num_space_samp
    with open(yamlfile_output, 'w') as stream:
        yaml.dump(outdict, stream)
def generate_sfft_clkdis_viadel(laygen,
                                objectname_pfix,
                                logictemp_lib,
                                working_lib,
                                grid,
                                origin=np.array([0, 0]),
                                way_order=[0, 8, 1, 7, 2, 6, 3, 5, 4],
                                m_clki=2,
                                m_clko=2,
                                num_bits=5,
                                num_vss_h=4,
                                num_vdd_h=4):
    """generate cap driver """

    pg = grid['pg']
    rg_m1m2 = grid['rg_m1m2']
    rg_m1m2_thick = grid['rg_m1m2_thick']
    rg_m2m3 = grid['rg_m2m3']
    rg_m2m3_basic = grid['rg_m2m3_basic']
    rg_m2m3_thick = grid['rg_m2m3_thick']
    rg_m2m3_thick2 = grid['rg_m2m3_thick2']
    rg_m3m4 = grid['rg_m3m4']
    rg_m3m4_dense = grid['rg_m3m4_dense']
    rg_m3m4_thick2 = grid['rg_m3m4_thick2']
    rg_m4m5 = grid['rg_m4m5']
    rg_m4m5_thick = grid['rg_m4m5_thick']
    rg_m5m6 = grid['rg_m5m6']
    rg_m5m6_thick = grid['rg_m5m6_thick']
    rg_m6m7 = grid['rg_m6m7']

    num_ways = len(way_order)
    ##Get the index for each way
    way_index = []
    for i in range(num_ways):
        way_index.append(way_order.index(i))
    print(way_index)
    #Get even and odd index
    #way_index_even = []
    #way_index_odd = []
    #for i in range(int(num_ways/2)):
    #way_index_even.append(way_order.index(2*i))
    #way_index_odd.append(way_order.index(2*i+1))
    x0 = laygen.templates.get_template('clk_dis_viadel_cell',
                                       libname=workinglib).xy[1][0]
    ##Placing delay cells
    viacell = []
    viacell0 = laygen.place(name='I' + objectname_pfix + 'CELL0',
                            templatename='clk_dis_viadel_cell',
                            gridname=pg,
                            xy=origin,
                            template_libname='clk_dis_generated')
    viacell.append(viacell0)

    for i in range(num_ways - 1):
        viacellx = laygen.relplace(name='I' + objectname_pfix + 'CELL' +
                                   str(i + 1),
                                   templatename='clk_dis_viadel_cell',
                                   gridname=pg,
                                   refinstname=viacell[-1].name,
                                   template_libname='clk_dis_generated')
        viacell.append(viacellx)

    ## Route, connection between different DFFs
    #wire_num = 0
    for i in range(num_ways):
        wire_num = i % 2
        #else:
        #wire_num = 2
        #if (i<int(num_ways-1)):
        #if ((i!=0) and (flag !=np.sign(way_index[i+1]-way_index[i]))):
        #wire_num = wire_num + 1
        #flag = np.sign(way_index[i+1]-way_index[i])
        #print(flag)
        #else:
        #wire_num = wire_num + 1

        #print(wire_num)
        #print(wire_num_odd)

        if (i < int(num_ways) - 1):
            dff_O_xy = laygen.get_inst_pin_xy(viacell[way_index[i]].name,
                                              'DATAO', rg_m3m4)
            dff_I_xy = laygen.get_inst_pin_xy(viacell[way_index[i + 1]].name,
                                              'DATAI', rg_m3m4)
        else:
            dff_O_xy = laygen.get_inst_pin_xy(viacell[way_index[i]].name,
                                              'DATAO', rg_m3m4)
            dff_I_xy = laygen.get_inst_pin_xy(viacell[way_index[0]].name,
                                              'DATAI', rg_m3m4)
        y_cood = dff_I_xy[0][1]
        laygen.pin(name='DATAO<' + str(way_order[way_index[i]]) + '>',
                   layer=laygen.layers['pin'][3],
                   xy=dff_O_xy,
                   gridname=rg_m3m4)
        if (i < int(num_ways / 2)):
            laygen.route_vhv(laygen.layers['metal'][3],
                             laygen.layers['metal'][4], dff_O_xy[0],
                             dff_I_xy[0], y_cood - 7 - wire_num, rg_m3m4)
        else:
            laygen.route_vhv(laygen.layers['metal'][3],
                             laygen.layers['metal'][4], dff_O_xy[0],
                             dff_I_xy[0], y_cood - 9 - wire_num, rg_m3m4)
        '''
        if (i<int(num_ways/2)-1):
            dff_O_xy_even = laygen.get_inst_pin_xy(viacell[way_index_even[i]].name, 'DATAO', rg_m3m4)
            dff_I_xy_even = laygen.get_inst_pin_xy(viacell[way_index_even[i+1]].name, 'DATAI', rg_m3m4)
            dff_O_xy_odd = laygen.get_inst_pin_xy(viacell[way_index_odd[i]].name, 'DATAO', rg_m3m4)
            dff_I_xy_odd = laygen.get_inst_pin_xy(viacell[way_index_odd[i+1]].name, 'DATAI', rg_m3m4)
        else:
            dff_O_xy_even = laygen.get_inst_pin_xy(viacell[way_index_even[i]].name, 'DATAO', rg_m3m4)
            dff_I_xy_even = laygen.get_inst_pin_xy(viacell[way_index_even[0]].name, 'DATAI', rg_m3m4)
            dff_O_xy_odd = laygen.get_inst_pin_xy(viacell[way_index_odd[i]].name, 'DATAO', rg_m3m4)
            dff_I_xy_odd = laygen.get_inst_pin_xy(viacell[way_index_odd[0]].name, 'DATAI', rg_m3m4)
        y_cood = dff_I_xy_even[0][1]
        laygen.pin(name='DATAO<'+str(way_order[way_index_even[i]])+'>', layer=laygen.layers['pin'][3], xy=dff_I_xy_even, gridname=rg_m3m4) 
        laygen.pin(name='DATAO<'+str(way_order[way_index_odd[i]])+'>', layer=laygen.layers['pin'][3], xy=dff_I_xy_odd, gridname=rg_m3m4) 
        laygen.route_vhv(laygen.layers['metal'][3], laygen.layers['metal'][4], dff_O_xy_even[0], dff_I_xy_even[0], y_cood-7-wire_num_even, rg_m3m4)
        laygen.route_vhv(laygen.layers['metal'][3], laygen.layers['metal'][4], dff_O_xy_odd[0], dff_I_xy_odd[0], y_cood-7-wire_num_odd, rg_m3m4)
        '''

    ##Route, clock connection from TGATE to input
    for i in range(num_ways):
        for j in range(m_clki):
            viadel_I_xy = laygen.get_inst_pin_xy(viacell[i].name,
                                                 'CLKI_' + str(j), rg_m5m6)
            laygen.pin(name='CLKI' + str(way_order[i]) + '_' + str(j),
                       layer=laygen.layers['pin'][5],
                       xy=viadel_I_xy,
                       gridname=rg_m5m6,
                       netname='CLKI<' + str(way_order[i]) + '>')

    ##Route, clock connection from TGATE to output
    for i in range(num_ways):
        for j in range(m_clko):
            viadel_I_xy = laygen.get_inst_pin_xy(viacell[i].name,
                                                 'CLKO_' + str(j), rg_m5m6)
            laygen.pin(name='CLKO' + str(way_order[i]) + '_' + str(j),
                       layer=laygen.layers['pin'][5],
                       xy=viadel_I_xy,
                       gridname=rg_m5m6,
                       netname='CLKO<' + str(way_order[i]) + '>')

    ##Route, for calibration signals
    #Get the right coodinate on grid m3m4
    m2m3_x = laygen.grids.get_absgrid_coord_x(gridname=rg_m2m3_basic, x=x0)
    m3m4_x = laygen.grids.get_absgrid_coord_x(gridname=rg_m3m4, x=x0)
    for i in range(num_ways):
        if i < 0:
            for j in range(num_bits):
                viadel_m2m3_xy = laygen.get_inst_pin_xy(
                    viacell[i].name, 'CAL<' + str(j) + '>', rg_m2m3_basic)[1]
                laygen.route(None,
                             laygen.layers['metal'][3],
                             xy0=viadel_m2m3_xy,
                             xy1=np.array([
                                 viadel_m2m3_xy[0],
                                 viadel_m2m3_xy[1] + 5 + j + i * num_bits
                             ]),
                             gridname0=rg_m2m3_basic)
                laygen.via(None,
                           xy=np.array([
                               viadel_m2m3_xy[0],
                               viadel_m2m3_xy[1] + 5 + j + i * num_bits
                           ]),
                           gridname=rg_m2m3_basic)
                calpx = laygen.route(
                    None,
                    laygen.layers['metal'][2],
                    xy0=np.array([0,
                                  viadel_m2m3_xy[1] + 5 + j + i * num_bits]),
                    xy1=np.array([
                        m2m3_x * num_ways,
                        viadel_m2m3_xy[1] + 5 + j + i * num_bits
                    ]),
                    gridname0=rg_m2m3_basic)
                laygen.boundary_pin_from_rect(
                    calpx,
                    gridname=rg_m2m3_basic,
                    name='CLKCAL' + str(way_order[i]) + '<' + str(j) + '>',
                    layer=laygen.layers['pin'][2],
                    size=2,
                    direction='right')
        else:
            for j in range(num_bits):
                viadel_m2m3_xy = laygen.get_inst_pin_xy(
                    viacell[i].name, 'CAL<' + str(j) + '>', rg_m2m3)[1]
                laygen.route(None,
                             laygen.layers['metal'][3],
                             xy0=viadel_m2m3_xy,
                             xy1=np.array(
                                 [viadel_m2m3_xy[0], viadel_m2m3_xy[1] - 4]),
                             gridname0=rg_m2m3)
                laygen.via(None,
                           xy=np.array(
                               [viadel_m2m3_xy[0], viadel_m2m3_xy[1] - 4]),
                           gridname=rg_m2m3)

    ##Route, for set/reset signals
    #STP and RSTP -- even
    m2m3_x = laygen.grids.get_absgrid_coord_x(gridname=rg_m2m3_basic, x=x0)
    viadel_ST_xy = laygen.get_inst_pin_xy(viacell[way_index[0]].name, 'ST',
                                          rg_m2m3_basic)[1]
    laygen.route_vh(laygen.layers['metal'][3],
                    laygen.layers['metal'][2], viadel_ST_xy,
                    np.array([0, viadel_ST_xy[1] + 12]), rg_m2m3_basic)

    viadel_RST_xy = laygen.get_inst_pin_xy(viacell[way_index[0]].name, 'RST',
                                           rg_m2m3)[1]
    laygen.route(None,
                 laygen.layers['metal'][3],
                 xy0=viadel_RST_xy,
                 xy1=np.array([viadel_RST_xy[0], viadel_RST_xy[1] + 4]),
                 gridname0=rg_m2m3)
    laygen.via(None,
               xy=np.array([viadel_RST_xy[0], viadel_RST_xy[1] + 4]),
               gridname=rg_m2m3)

    for i in range(int(num_ways) - 1):  #way_index
        viadel_RST_xy = laygen.get_inst_pin_xy(viacell[way_index[i + 1]].name,
                                               'RST', rg_m2m3_basic)[1]
        laygen.route_vh(laygen.layers['metal'][3],
                        laygen.layers['metal'][2], viadel_RST_xy,
                        np.array([0, viadel_RST_xy[1] + 12]), rg_m2m3_basic)

        viadel_ST_xy = laygen.get_inst_pin_xy(viacell[way_index[i + 1]].name,
                                              'ST', rg_m2m3)[1]
        laygen.route(None,
                     laygen.layers['metal'][3],
                     xy0=viadel_ST_xy,
                     xy1=np.array([viadel_ST_xy[0], viadel_ST_xy[1] + 4]),
                     gridname0=rg_m2m3)
        laygen.via(None,
                   xy=np.array([viadel_ST_xy[0], viadel_ST_xy[1] + 4]),
                   gridname=rg_m2m3)

    stp = laygen.route(None,
                       laygen.layers['metal'][2],
                       xy0=np.array([0, viadel_RST_xy[1] + 12]),
                       xy1=np.array(
                           [m2m3_x * num_ways - 4, viadel_RST_xy[1] + 12]),
                       gridname0=rg_m2m3_basic)
    laygen.boundary_pin_from_rect(stp,
                                  gridname=rg_m2m3_basic,
                                  name='RSTP',
                                  layer=laygen.layers['pin'][2],
                                  size=2,
                                  direction='left')
    '''
    #STN, RSTN -- odd
    viadel_ST_xy = laygen.get_inst_pin_xy(viacell[way_index_odd[0]].name, 'ST', rg_m2m3_basic)[1]
    laygen.route_vh(laygen.layers['metal'][3], laygen.layers['metal'][2], viadel_ST_xy, np.array([0,viadel_ST_xy[1]+13]), rg_m2m3_basic)

    viadel_RST_xy = laygen.get_inst_pin_xy(viacell[way_index_odd[0]].name, 'RST', rg_m2m3)[1]
    laygen.route(None, laygen.layers['metal'][3], xy0=viadel_RST_xy, xy1=np.array([viadel_RST_xy[0],viadel_RST_xy[1]+4]), gridname0=rg_m2m3)
    laygen.via(None, xy=np.array([viadel_RST_xy[0],viadel_RST_xy[1]+4]), gridname=rg_m2m3)

    for i in range(int(num_ways/2)-1):#way_index
        viadel_RST_xy = laygen.get_inst_pin_xy(viacell[way_index_odd[i+1]].name, 'RST', rg_m2m3_basic)[1]
        laygen.route_vh(laygen.layers['metal'][3], laygen.layers['metal'][2], viadel_RST_xy, np.array([0,viadel_RST_xy[1]+13]), rg_m2m3_basic)

        viadel_ST_xy = laygen.get_inst_pin_xy(viacell[way_index_odd[i+1]].name, 'ST', rg_m2m3)[1]
        laygen.route(None, laygen.layers['metal'][3], xy0=viadel_ST_xy, xy1=np.array([viadel_ST_xy[0],viadel_ST_xy[1]+4]), gridname0=rg_m2m3)
        laygen.via(None, xy=np.array([viadel_ST_xy[0],viadel_ST_xy[1]+4]), gridname=rg_m2m3)
      
    stn=laygen.route(None, laygen.layers['metal'][2], xy0=np.array([0, viadel_RST_xy[1]+13]), xy1=np.array([m2m3_x*num_ways, viadel_RST_xy[1]+13]), gridname0=rg_m2m3_basic)    
    laygen.boundary_pin_from_rect(stn, gridname=rg_m2m3_basic, pinname='RSTN', layer=laygen.layers['pin'][2], size=2, direction='left')
    '''

    ##VDD and VSS pin
    rvssl_m4 = []
    rvssr_m4 = []
    rvddl_m4 = []
    rvddr_m4 = []
    rvdd_m5 = []
    rvss_m5 = []
    for i in range(num_ways):
        for j in range(num_vss_h):
            vssl_xy = laygen.get_inst_pin_xy(viacell[i].name, 'VSS0_' + str(j),
                                             rg_m3m4_thick2)
            rvssl_m4.append(
                laygen.route(None,
                             laygen.layers['metal'][4],
                             xy0=vssl_xy[0],
                             xy1=vssl_xy[1],
                             gridname0=rg_m3m4_thick2))
            vssr_xy = laygen.get_inst_pin_xy(viacell[i].name, 'VSS1_' + str(j),
                                             rg_m3m4_thick2)
            rvssr_m4.append(
                laygen.route(None,
                             laygen.layers['metal'][4],
                             xy0=vssr_xy[0],
                             xy1=vssr_xy[1],
                             gridname0=rg_m3m4_thick2))
            # laygen.pin(name='VSS0_'+str(i)+'_'+str(j), layer=laygen.layers['pin'][4], xy=vssl_xy, gridname=rg_m3m4_thick2, netname='VSS')
            # laygen.pin(name='VSS1_'+str(i)+'_'+str(j), layer=laygen.layers['pin'][4], xy=vssr_xy, gridname=rg_m3m4_thick2, netname='VSS')
        for j in range(num_vdd_h):
            vddl_xy = laygen.get_inst_pin_xy(viacell[i].name, 'VDD0_' + str(j),
                                             rg_m3m4_thick2)
            rvddl_m4.append(
                laygen.route(None,
                             laygen.layers['metal'][4],
                             xy0=vddl_xy[0],
                             xy1=vddl_xy[1],
                             gridname0=rg_m3m4_thick2))
            vddr_xy = laygen.get_inst_pin_xy(viacell[i].name, 'VDD1_' + str(j),
                                             rg_m3m4_thick2)
            rvddr_m4.append(
                laygen.route(None,
                             laygen.layers['metal'][4],
                             xy0=vddr_xy[0],
                             xy1=vddr_xy[1],
                             gridname0=rg_m3m4_thick2))
            # laygen.pin(name='VDD0_'+str(i)+'_'+str(j), layer=laygen.layers['pin'][4], xy=vddl_xy, gridname=rg_m3m4_thick2, netname='VDD')
            # laygen.pin(name='VDD1_'+str(i)+'_'+str(j), layer=laygen.layers['pin'][4], xy=vddr_xy, gridname=rg_m3m4_thick2, netname='VDD')
        rvddl_m5, rvssl_m5 = laygenhelper.generate_power_rails_from_rails_rect(
            laygen,
            routename_tag='M5L',
            layer=laygen.layers['metal'][5],
            gridname=rg_m4m5_thick,
            netnames=['VDD', 'VSS'],
            direction='y',
            input_rails_rect=[rvddl_m4, rvssl_m4],
            generate_pin=False,
            overwrite_start_coord=0,
            overwrite_end_coord=None,
            overwrite_num_routes=None,
            offset_start_index=0,
            offset_end_index=0)
        rvddr_m5, rvssr_m5 = laygenhelper.generate_power_rails_from_rails_rect(
            laygen,
            routename_tag='M5L',
            layer=laygen.layers['metal'][5],
            gridname=rg_m4m5_thick,
            netnames=['VDD', 'VSS'],
            direction='y',
            input_rails_rect=[rvddr_m4, rvssr_m4],
            generate_pin=False,
            overwrite_start_coord=0,
            overwrite_end_coord=None,
            overwrite_num_routes=None,
            offset_start_index=0,
            offset_end_index=-2)
        rvddl_m4 = []
        rvssl_m4 = []
        rvddr_m4 = []
        rvssr_m4 = []
        rvdd_m5 += rvddl_m5
        rvdd_m5 += rvddr_m5
        rvss_m5 += rvssl_m5
        rvss_m5 += rvssr_m5
    rvdd_m6, rvss_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='M6',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=[rvdd_m5, rvss_m5],
        generate_pin=True,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        overwrite_num_routes=None,
        offset_start_index=2,
        offset_end_index=0)
def generate_tisaradc_body_core(laygen, objectname_pfix, ret_libname, sar_libname, clkdist_libname, space_1x_libname, ret_name, sar_name, clkdist_name, space_1x_name,
                                placement_grid,
                                routing_grid_m3m4, routing_grid_m4m5, routing_grid_m4m5_basic_thick, routing_grid_m5m6, routing_grid_m5m6_thick, 
                                routing_grid_m5m6_thick_basic,
                                routing_grid_m6m7_thick, 
                                num_bits=9, num_slices=8, clkdist_offset=14.4, origin=np.array([0, 0])):
    """generate sar array """
    pg = placement_grid

    rg_m3m4 = routing_grid_m3m4
    rg_m4m5 = routing_grid_m4m5
    rg_m4m5_basic_thick = routing_grid_m4m5_basic_thick
    rg_m5m6 = routing_grid_m5m6
    rg_m5m6_thick = routing_grid_m5m6_thick
    rg_m5m6_thick_basic = routing_grid_m5m6_thick_basic
    rg_m6m7_thick = routing_grid_m6m7_thick

    # placement
    # ret/sar/clk
    iret = laygen.place(name="I" + objectname_pfix + 'RET0', templatename=ret_name,
                      gridname=pg, xy=origin, template_libname=ret_libname)
    sar_xy = origin + (laygen.get_template_size(ret_name, gridname=pg, libname=ret_libname)*np.array([0,1]) )
    isar = laygen.relplace(name="I" + objectname_pfix + 'SAR0', templatename=sar_name,
                      gridname=pg, refinstname=iret.name, direction='top', template_libname=sar_libname)
    clkdist_offset_pg=int(clkdist_offset/laygen.get_grid(pg).height)
    clkdist_xy = laygen.get_inst_xy(name=isar.name, gridname=pg) \
                 + (laygen.get_template_size(sar_name, gridname=pg, libname=sar_libname)*np.array([0,1])) \
                 + np.array([0, clkdist_offset_pg])

    iclkdist = laygen.place(name="I" + objectname_pfix + 'CLKDIST0', templatename=clkdist_name,
                      gridname=pg, xy=clkdist_xy, template_libname=clkdist_libname)


    sar_template = laygen.templates.get_template(sar_name, sar_libname)
    sar_pins=sar_template.pins
    sar_xy=isar.xy
    ret_template = laygen.templates.get_template(ret_name, ret_libname)
    ret_pins=ret_template.pins
    ret_xy=iret.xy
    clkdist_template = laygen.templates.get_template(clkdist_name, clkdist_libname)
    clkdist_pins=clkdist_template.pins
    clkdist_xy=iclkdist.xy

    #prboundary
    sar_size = laygen.templates.get_template(sar_name, libname=sar_libname).size
    ret_size = laygen.templates.get_template(ret_name, libname=ret_libname).size
    space_size = laygen.templates.get_template(space_1x_name, libname=space_1x_libname).size
    clkdist_size = laygen.templates.get_template(clkdist_name, libname=clkdist_libname).size+clkdist_offset
    size_x=max((sar_size[0], ret_size[0]))
    print(sar_size[1]+ret_size[1]+clkdist_size[1], sar_size[1],ret_size[1],clkdist_size[1],space_size[1])
    size_y=int((sar_size[1]+ret_size[1]+clkdist_size[1]-9.6-3.84)/space_size[1]+1)*space_size[1] #clkdist_size is not correct.. will be fixed later
    laygen.add_rect(None, np.array([origin, origin+np.array([size_x, size_y])]), laygen.layers['prbnd'])

    pdict_m2m3=laygen.get_inst_pin_coord(None, None, rg_m2m3)
    pdict_m3m4=laygen.get_inst_pin_coord(None, None, rg_m3m4)
    pdict_m4m5=laygen.get_inst_pin_coord(None, None, rg_m4m5)
    pdict_m5m6=laygen.get_inst_pin_coord(None, None, rg_m5m6)
    pdict_m5m6_thick=laygen.get_inst_pin_coord(None, None, rg_m5m6_thick)
    pdict_m5m6_thick_basic=laygen.get_inst_pin_coord(None, None, rg_m5m6_thick_basic)

    #VDD/VSS pins for sar and ret (just duplicate from lower hierarchy cells)
    vddsampcnt=0
    vddsarcnt=0
    vsscnt=0
    for pn, p in sar_pins.items():
        if pn.startswith('VDDSAMP'):
            pxy=sar_xy+sar_pins[pn]['xy']
            laygen.add_pin('VDDSAMP' + str(vddsampcnt), 'VDDSAMP:', pxy, sar_pins[pn]['layer'])
            vddsampcnt+=1
        if pn.startswith('VDDSAR'):
            pxy=sar_xy+sar_pins[pn]['xy']
            laygen.add_pin('VDDSAR' + str(vddsarcnt), 'VDDSAR:', pxy, sar_pins[pn]['layer'])
            vddsarcnt+=1
        if pn.startswith('VSS'):
            pxy=sar_xy+sar_pins[pn]['xy']
            laygen.add_pin('VSS' + str(vsscnt), 'VSS:', pxy, sar_pins[pn]['layer'])
            if pn=='VSS4':
                print(pn, vsscnt, p)
            vsscnt+=1
    for pn, p in ret_pins.items():
        if pn.startswith('VDD'):
            pxy=ret_xy+ret_pins[pn]['xy']
            laygen.add_pin('VDDSAR' + str(vddsarcnt), 'VDDSAR:', pxy, ret_pins[pn]['layer'])
            vddsarcnt+=1
        if pn.startswith('VSS'):
            pxy=ret_xy+ret_pins[pn]['xy']
            laygen.add_pin('VSS' + str(vsscnt), 'VSS:', pxy, ret_pins[pn]['layer'])
            vsscnt+=1
    #VDD/VSS pins for clkdist
    rvdd_ckd_m5=[]
    rvss_ckd_m5=[]
    for i in range(num_slices):
        rvdd, rvss = laygenhelper.generate_power_rails_from_rails_inst(laygen, routename_tag='', layer=laygen.layers['metal'][5], gridname=rg_m4m5_thick, netnames=['VDD', 'VSS'], 
            direction='y', input_rails_instname=iclkdist.name, input_rails_pin_prefix=['VDD0_'+str(i), 'VSS0_'+str(i)], generate_pin=False, 
            overwrite_start_coord=None, overwrite_end_coord=None, overwrite_num_routes=None, offset_start_index=1, offset_end_index=-1)
        rvdd_ckd_m5+=rvdd
        rvss_ckd_m5+=rvss
        rvdd, rvss = laygenhelper.generate_power_rails_from_rails_inst(laygen, routename_tag='', layer=laygen.layers['metal'][5], gridname=rg_m4m5_thick, netnames=['VDD', 'VSS'], 
            direction='y', input_rails_instname=iclkdist.name, input_rails_pin_prefix=['VDD1_'+str(i), 'VSS1_'+str(i)], generate_pin=False, 
            overwrite_start_coord=None, overwrite_end_coord=None, overwrite_num_routes=None, offset_start_index=1, offset_end_index=-1)
        rvdd_ckd_m5+=rvdd
        rvss_ckd_m5+=rvss
    laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_CLKD_', layer=laygen.layers['pin'][6], gridname=rg_m5m6_thick, netnames=['VDDSAMP:', 'VSS:'], direction='x', 
                                         input_rails_rect=[rvdd_ckd_m5, rvss_ckd_m5], generate_pin=True, 
                                         overwrite_start_coord=0, overwrite_end_coord=None, overwrite_num_routes=None,
                                         offset_start_index=0, offset_end_index=0)

    #input pins
    #make virtual grids and route on the grids (assuming drc clearance of each block)
    rg_m5m6_thick_temp_sig='route_M5_M6_thick_temp_sig'
    laygenhelper.generate_grids_from_inst(laygen, gridname_input=rg_m5m6_thick, gridname_output=rg_m5m6_thick_temp_sig,
                                          instname=isar.name, 
                                          inst_pin_prefix=['INP', 'INM'], xy_grid_type='xgrid')
    pdict_m5m6_thick_temp_sig = laygen.get_inst_pin_coord(None, None, rg_m5m6_thick_temp_sig)
    inp_x_list=[]
    inm_x_list=[]
    num_input_track=4
    in_x0 = pdict_m5m6_thick_temp_sig[isar.name]['INP0'][0][0]
    in_x1 = pdict_m5m6_thick_temp_sig[isar.name]['INM0'][0][0]
    in_y0 = pdict_m5m6_thick_temp_sig[isar.name]['INP0'][1][1]
    in_y1 = in_y0+6
    in_y2 = in_y1+2*num_input_track
    for i in range(num_slices):
        in_x0 = min(in_x0, pdict_m5m6_thick_temp_sig[isar.name]['INP'+str(i)][0][0])
        in_x1 = max(in_x1, pdict_m5m6_thick_temp_sig[isar.name]['INM'+str(i)][0][0])
        laygen.route(None, laygen.layers['metal'][5],
                     xy0=np.array([pdict_m5m6_thick_temp_sig[isar.name]['INP'+str(i)][0][0], in_y0]),
                     xy1=np.array([pdict_m5m6_thick_temp_sig[isar.name]['INP'+str(i)][0][0], in_y2]), 
                     gridname0=rg_m5m6_thick_temp_sig)
        laygen.route(None, laygen.layers['metal'][5],
                     xy0=np.array([pdict_m5m6_thick_temp_sig[isar.name]['INM'+str(i)][0][0], in_y0]),
                     xy1=np.array([pdict_m5m6_thick_temp_sig[isar.name]['INM'+str(i)][0][0], in_y2]), 
                     gridname0=rg_m5m6_thick_temp_sig)
        for j in range(num_input_track):
            laygen.via(None,np.array([pdict_m5m6_thick_temp_sig[isar.name]['INP'+str(i)][0][0], in_y1+2*j]), rg_m5m6_thick_temp_sig)
            laygen.via(None,np.array([pdict_m5m6_thick_temp_sig[isar.name]['INM'+str(i)][0][0], in_y1+2*j+1]), rg_m5m6_thick_temp_sig)
    #in_x0 -= 2
    #in_x1 += 2
    rinp=[]
    rinm=[]
    for i in range(num_input_track):
        rinp.append(laygen.route(None, laygen.layers['metal'][6], xy0=np.array([in_x0, in_y1+2*i]), xy1=np.array([in_x1, in_y1+2*i]), gridname0=rg_m5m6_thick_temp_sig))
        rinm.append(laygen.route(None, laygen.layers['metal'][6], xy0=np.array([in_x0, in_y1+2*i+1]), xy1=np.array([in_x1, in_y1+2*i+1]), gridname0=rg_m5m6_thick_temp_sig))
        laygen.add_pin('INP' + str(i), 'INP', rinp[-1].xy, laygen.layers['pin'][6])
        laygen.add_pin('INM' + str(i), 'INM', rinm[-1].xy, laygen.layers['pin'][6])

    #clk output pins
    #laygen.add_pin('CLKBOUT_NC', 'CLKBOUT_NC', np.array([sar_xy, sar_xy])+sar_pins['CLKO07']['xy'], sar_pins['CLKO07']['layer'])
    laygen.add_pin('CLKOUT_DES', 'CLKOUT_DES', ret_pins['ck_out']['xy'], ret_pins['ck_out']['layer'])
    
    #retimer output pins
    for i in range(num_slices):
        for j in range(num_bits):
            pn='out_'+str(i)+'<'+str(j)+'>'
            pn_out='ADCOUT'+str(i)+'<'+str(j)+'>'
            xy=pdict_m3m4[iret.name][pn]
            xy[0][1]=0
            r=laygen.route(None, layer=laygen.layers['metal'][3], xy0=xy[0], xy1=xy[1], gridname0=rg_m3m4)
            laygen.create_boundary_pin_form_rect(r, rg_m3m4, pn_out, laygen.layers['pin'][3], size=4, direction='bottom')
    #extclk_sel pins
    for i in range(num_slices):
            pn='EXTSEL_CLK'+str(i)
            pn_out='EXTSEL_CLK'+str(i)
            xy=pdict_m5m6[isar.name][pn]
            xy[0][1]=0
            r=laygen.route(None, layer=laygen.layers['metal'][5], xy0=xy[0], xy1=xy[1], gridname0=rg_m5m6)
            laygen.create_boundary_pin_form_rect(r, rg_m5m6, pn_out, laygen.layers['pin'][5], size=4, direction='bottom')
    #asclkd pins
    for i in range(num_slices):
        for j in range(4):
            pn='ASCLKD'+str(i)+'<'+str(j)+'>'
            pn_out='ASCLKD'+str(i)+'<'+str(j)+'>'
            xy=pdict_m5m6[isar.name][pn]
            xy[0][1]=0 
            r=laygen.route(None, layer=laygen.layers['metal'][5], xy0=xy[0], xy1=xy[1], gridname0=rg_m5m6)
            laygen.create_boundary_pin_form_rect(r, rg_m5m6, pn_out, laygen.layers['pin'][5], size=4, direction='bottom')
    #osp/osm pins
    for i in range(num_slices):
        laygen.pin(name='OSP' + str(i), layer=laygen.layers['pin'][4], xy=pdict_m4m5[isar.name]['OSP' + str(i)], gridname=rg_m4m5)
        laygen.pin(name='OSM' + str(i), layer=laygen.layers['pin'][4], xy=pdict_m4m5[isar.name]['OSM' + str(i)], gridname=rg_m4m5)
    #vref pins
    num_vref_routes=4
    for i in range(num_vref_routes):
        laygen.pin(name='VREF' + str(i) + '<0>', layer=laygen.layers['pin'][6], xy=pdict_m5m6_thick[isar.name]['VREF<0>_M6_' + str(i)], gridname=rg_m5m6_thick, netname='VREF<0>')
        laygen.pin(name='VREF' + str(i) + '<1>', layer=laygen.layers['pin'][6], xy=pdict_m5m6_thick[isar.name]['VREF<1>_M6_' + str(i)], gridname=rg_m5m6_thick, netname='VREF<1>')
        laygen.pin(name='VREF' + str(i) + '<2>', layer=laygen.layers['pin'][6], xy=pdict_m5m6_thick[isar.name]['VREF<2>_M6_' + str(i)], gridname=rg_m5m6_thick, netname='VREF<2>')
    #clkcal pins
    for i in range(num_slices):
        for j in range(5):
            pn='CLKCAL'+str(i)+'<'+str(j)+'>'
            pxy=clkdist_xy+clkdist_pins[pn]['xy']
            laygen.add_pin(pn, pn, pxy, clkdist_pins[pn]['layer'])
    #clkin pins
    for i in range(12):
        pn='CLKIP'+'_'+str(i)
        nn='CLKIP'
        laygen.add_pin(pn, nn, clkdist_xy+clkdist_pins[pn]['xy'], clkdist_pins[pn]['layer'])
        pn='CLKIN'+'_'+str(i)
        nn='CLKIN'
        laygen.add_pin(pn, nn, clkdist_xy+clkdist_pins[pn]['xy'], clkdist_pins[pn]['layer'])
    laygen.add_pin('RSTP', 'RSTP', clkdist_xy+clkdist_pins['RSTP']['xy'], clkdist_pins['RSTP']['layer'])
    laygen.add_pin('RSTN', 'RSTN', clkdist_xy+clkdist_pins['RSTN']['xy'], clkdist_pins['RSTN']['layer'])

    #clkdist-sar routes (clock)
    #make virtual grids and route on the grids (assuming drc clearance of each block)

    rg_m4m5_temp='route_M4_M5_temp'
    laygenhelper.generate_grids_from_inst(laygen, gridname_input=rg_m4m5_basic_thick, gridname_output=rg_m4m5_temp,
                                          instname=isar.name, 
                                          inst_pin_prefix=['CLK'], xy_grid_type='xgrid')
    pdict_m4m5_temp = laygen.get_inst_pin_coord(None, None, rg_m4m5_temp)
    for i in range(num_slices):
        x0=pdict_m4m5[isar.name]['CLK'+str(i)][0][0]
        y1=pdict_m4m5[iclkdist.name]['CLKO'+str(i)+'<0>'][0][1]+4
        laygen.route_vh(layerv=laygen.layers['metal'][5], layerh=laygen.layers['metal'][4], 
                        xy0=pdict_m4m5[iclkdist.name]['CLKO'+str(i)+'<0>'][0],
                        xy1=np.array([x0, y1]), gridname=rg_m4m5)
        laygen.route_vh(layerv=laygen.layers['metal'][5], layerh=laygen.layers['metal'][4], 
                        xy0=pdict_m4m5[iclkdist.name]['CLKO'+str(i)+'<1>'][0],
                        xy1=np.array([x0, y1+2]), gridname=rg_m4m5)
        laygen.route_vh(layerv=laygen.layers['metal'][5], layerh=laygen.layers['metal'][4], 
                        xy0=pdict_m4m5[iclkdist.name]['CLKO'+str(i)+'<0>'][0]+np.array([4,0]),
                        xy1=np.array([x0, y1+4]), gridname=rg_m4m5)
        laygen.route_vh(layerv=laygen.layers['metal'][5], layerh=laygen.layers['metal'][4], 
                        xy0=pdict_m4m5[iclkdist.name]['CLKO'+str(i)+'<1>'][0]+np.array([4,0]),
                        xy1=np.array([x0, y1+6]), gridname=rg_m4m5)
        laygen.route(None, layer=laygen.layers['metal'][5], 
                        xy0=pdict_m4m5_temp[isar.name]['CLK'+str(i)][0], 
                        xy1=np.array([pdict_m4m5_temp[isar.name]['CLK'+str(i)][0][0], y1+8]),
                        gridname0=rg_m4m5_temp)
        laygen.via(None,np.array([pdict_m4m5_temp[isar.name]['CLK'+str(i)][0][0], y1]), rg_m4m5_temp)
        laygen.via(None,np.array([pdict_m4m5_temp[isar.name]['CLK'+str(i)][0][0], y1+2]), rg_m4m5_temp)
        laygen.via(None,np.array([pdict_m4m5_temp[isar.name]['CLK'+str(i)][0][0], y1+4]), rg_m4m5_temp)
        laygen.via(None,np.array([pdict_m4m5_temp[isar.name]['CLK'+str(i)][0][0], y1+6]), rg_m4m5_temp)
    

    #sar-retimer routes (data)
    for i in range(num_slices):
        for j in range(num_bits):
            laygen.route_vhv(layerv0=laygen.layers['metal'][5], layerh=laygen.layers['metal'][4], 
                            xy0=pdict_m4m5[isar.name]['ADCOUT'+str(i)+'<'+str(j)+'>'][0], 
                            xy1=pdict_m3m4[iret.name]['in_'+str(i)+'<'+str(j)+'>'][0], 
                            track_y=pdict_m4m5[isar.name]['ADCOUT'+str(i)+'<'+str(j)+'>'][0][1]+j*2+2, 
                            gridname=rg_m4m5, layerv1=laygen.layers['metal'][3], gridname1=rg_m3m4, extendl=0, extendr=0)
    #sar-retimer routes (clock)
    #finding clock_bar phases <- added by Jaeduk
    #rules:
    # 1) last stage latches: num_slices-1
    # 2) second last stage latches: int(num_slices/2)-1
    # 3) the first half of first stage latches: int((int(num_slices/2)+1)%num_slices)
    # 4) the second half of first stage latches: 1
    # 5) the output phase = the second last latch phase
    ck_phase_2=num_slices-1
    ck_phase_1=int(num_slices/2)-1
    ck_phase_0_0=int((int(num_slices/2)+1)%num_slices)
    ck_phase_0_1=1
    ck_phase_out=ck_phase_1
    ck_phase_buf=sorted(set([ck_phase_2, ck_phase_1, ck_phase_0_0, ck_phase_0_1]))
    rg_m3m4_temp_clk='route_M3_M4_basic_temp_clk'
    laygenhelper.generate_grids_from_inst(laygen, gridname_input=rg_m3m4, gridname_output=rg_m3m4_temp_clk,
                                          instname=iret.name, 
                                          inst_pin_prefix=['clk'+str(i) for i in ck_phase_buf], xy_grid_type='xgrid')
    pdict_m3m4_temp_clk = laygen.get_inst_pin_coord(None, None, rg_m3m4_temp_clk)
    for i in ck_phase_buf:
        for j in range(4):
            laygen.route_vhv(layerv0=laygen.layers['metal'][5], layerh=laygen.layers['metal'][4], 
                            xy0=pdict_m4m5[isar.name]['CLKO0'+str(i)][0], 
                            xy1=pdict_m3m4_temp_clk[iret.name]['clk'+str(i)][0], 
                            track_y=pdict_m4m5[isar.name]['CLKO00'][0][1]+num_bits*2+2+2*j, 
                            gridname=rg_m4m5, layerv1=laygen.layers['metal'][3], gridname1=rg_m3m4_temp_clk, extendl=0, extendr=0)
            laygen.route_vhv(layerv0=laygen.layers['metal'][5], layerh=laygen.layers['metal'][4], 
                            xy0=pdict_m4m5[isar.name]['CLKO1'+str(i)][0], 
                            xy1=pdict_m3m4_temp_clk[iret.name]['clk'+str(i)][0], 
                            track_y=pdict_m4m5[isar.name]['CLKO00'][0][1]+num_bits*2+2+2*j, 
                            gridname=rg_m4m5, layerv1=laygen.layers['metal'][3], gridname1=rg_m3m4_temp_clk, extendl=0, extendr=0)
        r=laygen.route(None, layer=laygen.layers['metal'][3], xy0=pdict_m3m4_temp_clk[iret.name]['clk'+str(i)][0], 
                        xy1=np.array([pdict_m3m4_temp_clk[iret.name]['clk'+str(i)][0][0], 
                                      pdict_m4m5[isar.name]['CLKO00'][0][1]+num_bits*2+2+2*4+1]), gridname0=rg_m3m4_temp_clk)
    '''    
Exemple #7
0
def generate_tisaradc_dcap_array(laygen,
                                 objectname_pfix,
                                 templib_logic,
                                 placement_grid,
                                 routing_grid_m4m5,
                                 routing_grid_m5m6,
                                 rg_m3m4_basic_thick,
                                 rg_m5m6_thick,
                                 m,
                                 num_bits,
                                 num_hori,
                                 num_vert,
                                 origin=np.array([0, 0])):
    """generate r2rdac """
    r2r_name = 'tisaradc_dcap'
    sar_name = 'sar_wsamp_bb_doubleSA'
    ret_name = 'adc_retimer'
    tisar_name = 'tisaradc_splash'
    tgate_name = 'tgate_' + str(m) + 'x'
    pg = placement_grid

    rg_m4m5 = routing_grid_m4m5
    rg_m5m6 = routing_grid_m5m6
    # rg_m4m5 = routing_grid_m4m5
    # rg_m4m5_basic_thick = routing_grid_m4m5_basic_thick
    # rg_m4m5_thick = routing_grid_m4m5_thick
    # rg_m5m6 = routing_grid_m5m6
    # rg_m5m6_thick = routing_grid_m5m6_thick
    # rg_m5m6_thick_basic = routing_grid_m5m6_thick_basic
    # rg_m6m7_thick = routing_grid_m6m7_thick

    # Calculate reference coordinate
    x1_phy = laygen.get_template_xy(name=tisar_name,
                                    gridname=None,
                                    libname=workinglib)[0]
    dcap_x = laygen.get_template_xy(name=r2r_name,
                                    gridname=None,
                                    libname=workinglib)[0]
    num_hori = int(x1_phy / dcap_x)

    # placement
    irdac = laygen.relplace(name="I" + objectname_pfix + 'Idcap',
                            templatename=r2r_name,
                            gridname=pg,
                            refinstname=None,
                            xy=origin,
                            shape=[num_hori, 1],
                            template_libname=workinglib)

    # m5 supply
    pdict_m5m6_thick = laygen.get_inst_pin_xy(None, None, rg_m5m6_thick)
    rvdd_m5 = []
    rvss_m5 = []
    for i in range(num_hori):
        for p in pdict_m5m6_thick[irdac.name]:
            if p.startswith('VDD'):
                r0 = laygen.route(
                    None,
                    laygen.layers['metal'][5],
                    xy0=laygen.get_inst_pin_xy(irdac.name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[0],
                    xy1=laygen.get_inst_pin_xy(irdac.name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[1],
                    gridname0=rg_m5m6_thick)
                rvdd_m5.append(r0)
        for p in pdict_m5m6_thick[irdac.name]:
            if p.startswith('VSS'):
                r0 = laygen.route(
                    None,
                    laygen.layers['metal'][5],
                    xy0=laygen.get_inst_pin_xy(irdac.name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[0],
                    xy1=laygen.get_inst_pin_xy(irdac.name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[1],
                    gridname0=rg_m5m6_thick)
                rvss_m5.append(r0)

    # m6
    input_rails_rect = [rvdd_m5, rvss_m5]
    rvdd_m6_0, rvss_m6_0 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_0_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        overwrite_end_index=None)
def generate_tisaradc_body(laygen,
                           objectname_pfix,
                           libname,
                           tisar_core_name,
                           tisar_space_name,
                           tisar_space2_name,
                           placement_grid,
                           routing_grid_m3m4,
                           routing_grid_m4m5,
                           routing_grid_m4m5_basic_thick,
                           routing_grid_m5m6,
                           routing_grid_m5m6_thick,
                           routing_grid_m5m6_thick_basic,
                           routing_grid_m6m7_thick,
                           routing_grid_m7m8_thick,
                           num_bits=9,
                           num_slices=8,
                           origin=np.array([0, 0])):
    """generate sar array """
    pg = placement_grid

    rg_m3m4 = routing_grid_m3m4
    rg_m4m5 = routing_grid_m4m5
    rg_m4m5 = routing_grid_m4m5
    rg_m4m5_basic_thick = routing_grid_m4m5_basic_thick
    rg_m5m6 = routing_grid_m5m6
    rg_m5m6_thick = routing_grid_m5m6_thick
    rg_m5m6_thick_basic = routing_grid_m5m6_thick_basic
    rg_m6m7_thick = routing_grid_m6m7_thick
    rg_m7m8_thick = routing_grid_m7m8_thick

    # placement
    ispace20 = laygen.place(name="I" + objectname_pfix + 'SP20',
                            templatename=tisar_space2_name,
                            gridname=pg,
                            xy=origin,
                            template_libname=libname)
    space20_template = laygen.templates.get_template(tisar_space2_name,
                                                     libname)
    space20_pins = space20_template.pins
    space20_xy = ispace20.xy
    space0_origin = laygen.get_xy(obj=ispace20.template,
                                  gridname=pg) * np.array([1, 0])
    ispace0 = laygen.place(name="I" + objectname_pfix + 'SP0',
                           templatename=tisar_space_name,
                           gridname=pg,
                           xy=space0_origin,
                           template_libname=libname)
    space_template = laygen.templates.get_template(tisar_space_name, libname)
    space_pins = space_template.pins
    space0_xy = ispace0.xy
    sar_origin = space0_origin + laygen.get_xy(obj=ispace0.template,
                                               gridname=pg) * np.array([1, 0])
    isar = laygen.place(name="I" + objectname_pfix + 'SAR0',
                        templatename=tisar_core_name,
                        gridname=pg,
                        xy=sar_origin,
                        template_libname=libname)
    sar_template = laygen.templates.get_template(tisar_core_name, libname)
    sar_pins = sar_template.pins
    sar_xy = isar.xy
    space1_origin = sar_origin + laygen.get_xy(obj=isar.template,
                                               gridname=pg) * np.array([1, 0])
    ispace1 = laygen.place(name="I" + objectname_pfix + 'SP1',
                           templatename=tisar_space_name,
                           gridname=pg,
                           xy=space1_origin,
                           template_libname=libname)
    space1_xy = ispace1.xy
    space21_origin = space1_origin + laygen.get_xy(
        obj=ispace1.template, gridname=pg) * np.array([1, 0])
    ispace21 = laygen.place(name="I" + objectname_pfix + 'SP21',
                            templatename=tisar_space2_name,
                            gridname=pg,
                            xy=space21_origin,
                            template_libname=libname)
    space21_xy = ispace21.xy

    #prboundary
    sar_size = laygen.templates.get_template(tisar_core_name,
                                             libname=libname).size
    space_size = laygen.templates.get_template(tisar_space_name,
                                               libname=libname).size
    space2_size = laygen.templates.get_template(tisar_space2_name,
                                                libname=libname).size

    #VDD/VSS/VREF integration
    rvddclkd = []
    rvddsamp = []
    rvddsar = []
    rvddsar_upper = []
    rvref0 = []
    rvref1 = []
    rvref2 = []
    rvssclkd = []
    rvsssamp = []
    rvsssar = []
    rvsssar_upper = []
    vddsampcnt = 0
    vddsarcnt = 0
    y_vddsar_max = 0
    y_vddsamp_min = 500000
    y_vddsamp_max = 0
    y_vddclkd_min = 500000
    y_vref0 = sar_pins['VREF0<0>']['xy'][0][1]
    #categorize vdd pins and fiugre out htresholds
    for pn, p in sar_pins.items():
        if pn.startswith('VDDCLKD'):
            pxy = np.array([[0, space0_xy[1] + sar_pins[pn]['xy'][0][1]],
                            [
                                space_size[0] * 4 + sar_size[0],
                                space1_xy[1] + sar_pins[pn]['xy'][1][1]
                            ]])
            rvddclkd.append(
                laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pxy[1][1] < y_vddclkd_min:
                y_vddclkd_min = pxy[1][1]
        if pn.startswith('VDDSAMP'):
            pxy = np.array([[0, space0_xy[1] + sar_pins[pn]['xy'][0][1]],
                            [
                                space_size[0] * 4 + sar_size[0],
                                space1_xy[1] + sar_pins[pn]['xy'][1][1]
                            ]])
            rvddsamp.append(
                laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pxy[1][1] < y_vddsamp_min:
                y_vddsamp_min = pxy[1][1]
            if pxy[1][1] > y_vddsamp_max:
                y_vddsamp_max = pxy[1][1]
        if pn.startswith('VDDSAR'):
            pxy = np.array([[0, space0_xy[1] + sar_pins[pn]['xy'][0][1]],
                            [
                                space_size[0] * 4 + sar_size[0],
                                space1_xy[1] + sar_pins[pn]['xy'][1][1]
                            ]])
            if pxy[0][1] > y_vref0:
                rvddsar_upper.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            else:
                rvddsar.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pxy[1][1] > y_vddsar_max:
                y_vddsar_max = pxy[1][1]
        if pn.startswith('VREF'):
            pxy = np.array([[0, space0_xy[1] + sar_pins[pn]['xy'][0][1]],
                            [
                                space_size[0] * 4 + sar_size[0],
                                space1_xy[1] + sar_pins[pn]['xy'][1][1]
                            ]])
            if pn.endswith('<0>'):
                rvref0.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pn.endswith('<1>'):
                rvref1.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pn.endswith('<2>'):
                rvref2.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
    y_vss_th = 0.5 * (y_vddsar_max + y_vddsamp_min)  #find out threshold
    y_vss_th2 = 0.5 * (y_vddsamp_max + y_vddclkd_min)  #find out threshold
    for pn, p in sar_pins.items():
        if pn.startswith('VSS'):
            pxy = np.array([[0, space0_xy[1] + sar_pins[pn]['xy'][0][1]],
                            [
                                space_size[0] * 4 + sar_size[0],
                                space1_xy[1] + sar_pins[pn]['xy'][1][1]
                            ]])
            if pxy[0][1] > y_vss_th2:
                rvssclkd.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            elif pxy[0][1] > y_vss_th:
                rvsssamp.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            elif pxy[0][1] > y_vref0:
                rvsssar_upper.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            else:
                rvsssar.append(
                    laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
    # for i in range(len(rvddsar)):
    #     print(rvddsar[i].xy)
    #M7 rails
    rg_route = 'route_M6_M7_thick_temp_pwr'
    if input_htree == False:
        laygenhelper.generate_grids_from_inst(
            laygen,
            gridname_input=rg_m6m7_thick,
            gridname_output=rg_route,
            instname=[isar.name],
            inst_pin_prefix=['VDD', 'VSS', 'VREF'],
            xy_grid_type='ygrid')
        rg_route_m7m8 = 'route_M7_M8_thick'
    else:
        x_ref = laygen.templates.get_template('sar_wsamp',
                                              libname=workinglib).size[0]
        x_pitch = laygen.get_grid(rg_m6m7_thick).xy[1][0]
        xy = np.array([[0, 0],
                       [x_ref, laygen.get_grid(rg_m6m7_thick).xy[1][1]]])
        ywidth = np.array(laygen.get_grid(rg_m6m7_thick).ywidth)
        # xgrid = np.array([x_pitch, x_ref])
        xgrid = np.array(
            [x_pitch, 2 * x_pitch, x_ref - 1 * x_pitch, x_ref - 0 * x_pitch])
        ygrid = np.array([0])
        vianame = list(laygen.get_grid(rg_m6m7_thick).viamap.keys())[0]
        viamap = {vianame: []}
        for x in range(len(xgrid)):
            viamap[vianame].append([x, 0])
        xwidth = np.repeat(laygen.get_grid(rg_m6m7_thick).xwidth, len(xgrid))
        viamap[vianame] = np.array(viamap[vianame])
        laygen.grids.add_route_grid(name=rg_route,
                                    xy=xy,
                                    xgrid=xgrid,
                                    ygrid=ygrid,
                                    xwidth=xwidth,
                                    ywidth=ywidth,
                                    viamap=viamap)

        rg_route_m7m8 = 'route_M7_M8_thick_temp_pwr'
        xy = np.array([[0, 0],
                       [
                           laygen.get_grid(rg_route).xy[1][0],
                           laygen.get_grid(rg_m7m8_thick).xy[1][1]
                       ]])
        ywidth = np.array(laygen.get_grid(rg_m7m8_thick).ywidth)
        xgrid = laygen.get_grid(rg_route).xgrid
        ygrid = laygen.get_grid(rg_route).ygrid
        vianame = list(laygen.get_grid(rg_m7m8_thick).viamap.keys())[0]
        viamap = {vianame: []}
        for x in range(len(xgrid)):
            viamap[vianame].append([x, 0])
        xwidth = np.repeat(laygen.get_grid(rg_m7m8_thick).xwidth, len(xgrid))
        viamap[vianame] = np.array(viamap[vianame])
        laygen.grids.add_route_grid(name=rg_route_m7m8,
                                    xy=xy,
                                    xgrid=xgrid,
                                    ygrid=ygrid,
                                    xwidth=xwidth,
                                    ywidth=ywidth,
                                    viamap=viamap)
    laygen.grids.display(libname=None, gridname=rg_route)
    laygen.grids.display(libname=None, gridname=rg_route_m7m8)
    '''
    #M7 rails-clkd
    input_rails_rect = [rvddclkd, rvssclkd]
    rvddclkd_m7_pre, rvssclkd_m7_pre= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_CLKD_M7_', 
                layer=laygen.layers['metal'][7], gridname=rg_route, netnames=['VDDCLKD', 'VSS'], direction='y', 
                input_rails_rect=input_rails_rect, generate_pin=False, overwrite_start_coord=None, offset_end_coord=None,
                offset_start_index=2, offset_end_index=-2)
    '''
    #M7 rails-samp
    input_rails_rect = [rvddsamp, rvsssamp]
    rvddsamp_m7_pre, rvsssamp_m7_pre = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_SAMP_M7_',
        layer=laygen.layers['metal'][7],
        gridname=rg_route,
        netnames=['VDDSAMP', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        offset_end_coord=None,
        offset_start_index=1,
        offset_end_index=-1)
    #M7 rails-sar_lower
    input_rails_rect = [rvddsar, rvsssar]
    rvddsar_m7, rvsssar_m7 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_SAR_M7_',
        layer=laygen.layers['metal'][7],
        gridname=rg_route,
        netnames=['VDDSAR', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=1,
        offset_end_index=-1)
    #M7 rails-sar_upper(+vref)
    input_rails_rect = [
        rvddsar_upper, rvsssar_upper, rvref0, rvsssar_upper, rvref1,
        rvsssar_upper, rvref2, rvsssar_upper
    ]
    rvddsar_m7_upper, rvsssar0_m7, rvref0_m7_pre, rvsssar1_m7, rvref1_m7_pre, rvsssar2_m7, rvref2_m7_pre, rvsssar3_m7 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_SAR_UPPER_M7_',
        layer=laygen.layers['metal'][7],
        gridname=rg_route,
        netnames=[
            'VDDSAR', 'VSS', 'VREF<0>', 'VSS', 'VREF<1>', 'VSS', 'VREF<2>',
            'VSS'
        ],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=1,
        offset_end_index=-1)
    rvsssar_m7_upper_pre = rvsssar0_m7 + rvsssar1_m7 + rvsssar2_m7 + rvsssar3_m7
    #extend m7 rails for clkd and samp and vref, rvsssar_m7_upper
    #rvddclkd_m7=[]
    #rvssclkd_m7=[]
    rvddsamp_m7 = []
    rvsssamp_m7 = []
    rvref0_m7 = []
    rvref1_m7 = []
    rvref2_m7 = []
    rvsssar_m7_upper = []
    '''
    for r in rvddclkd_m7_pre:
        rxy=laygen.get_xy(obj = r, gridname=rg_m6m7_thick, sort=True)
        rxy[1][1]+=12
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvddclkd_m7.append(r2)
    for r in rvssclkd_m7_pre:
        rxy=laygen.get_xy(obj = r, gridname=rg_m6m7_thick, sort=True)
        rxy[1][1]+=12
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvssclkd_m7.append(r2)
    '''
    for r in rvddsamp_m7_pre:
        rxy = laygen.get_xy(obj=r, gridname=rg_route, sort=True)
        #rxy[0][1]-=1
        rxy[1][1] += 24
        r2 = laygen.route(None,
                          laygen.layers['metal'][7],
                          xy0=rxy[0],
                          xy1=rxy[1],
                          gridname0=rg_route)
        rvddsamp_m7.append(r2)
    for r in rvsssamp_m7_pre:
        rxy = laygen.get_xy(obj=r, gridname=rg_route, sort=True)
        #rxy[0][1]-=1
        rxy[1][1] += 24
        r2 = laygen.route(None,
                          laygen.layers['metal'][7],
                          xy0=rxy[0],
                          xy1=rxy[1],
                          gridname0=rg_route)
        rvsssamp_m7.append(r2)
        #extend upper vss routes in the space area down to zero (for vss short)
        if r.xy[0][0] < sar_xy[0]:
            laygen.route(None,
                         laygen.layers['metal'][7],
                         xy0=[rxy[0][0], 0],
                         xy1=rxy[1],
                         gridname0=rg_route)
        if r.xy[0][0] > sar_xy[0] + sar_template.width:
            laygen.route(None,
                         laygen.layers['metal'][7],
                         xy0=[rxy[0][0], 0],
                         xy1=rxy[1],
                         gridname0=rg_route)
    for r in rvref0_m7_pre:
        rxy = laygen.get_xy(obj=r, gridname=rg_route, sort=True)
        # rxy[0][1]-=24
        #rxy[1][1]+=24
        r2 = laygen.route(None,
                          laygen.layers['metal'][7],
                          xy0=rxy[0],
                          xy1=rxy[1],
                          gridname0=rg_route)
        print(r2.xy)
        rvref0_m7.append(r2)
    for r in rvref1_m7_pre:
        rxy = laygen.get_xy(obj=r, gridname=rg_route, sort=True)
        # rxy[0][1]-=24
        #rxy[1][1]+=24
        r2 = laygen.route(None,
                          laygen.layers['metal'][7],
                          xy0=rxy[0],
                          xy1=rxy[1],
                          gridname0=rg_route)
        rvref1_m7.append(r2)
    for r in rvref2_m7_pre:
        rxy = laygen.get_xy(obj=r, gridname=rg_route, sort=True)
        # rxy[0][1]-=24
        #rxy[1][1]+=24
        r2 = laygen.route(None,
                          laygen.layers['metal'][7],
                          xy0=rxy[0],
                          xy1=rxy[1],
                          gridname0=rg_route)
        rvref2_m7.append(r2)
    for r in rvsssar_m7_upper_pre:
        rxy = laygen.get_xy(obj=r, gridname=rg_route, sort=True)
        # rxy[0][1]-=24
        #rxy[1][1]+=24
        r2 = laygen.route(None,
                          laygen.layers['metal'][7],
                          xy0=rxy[0],
                          xy1=rxy[1],
                          gridname0=rg_route)
        rvsssar_m7_upper.append(r2)
    #connect VDDSAR/VSS in sar region
    for r in rvddsar_m7_upper:
        rxy = laygen.get_xy(obj=r, gridname=rg_route, sort=True)
        rxy[0][1] = 1
        r2 = laygen.route(None,
                          laygen.layers['metal'][7],
                          xy0=rxy[0],
                          xy1=rxy[1],
                          gridname0=rg_route)
    for r in rvsssar_m7_upper:
        rxy = laygen.get_xy(obj=r, gridname=rg_route, sort=True)
        rxy[0][1] = 1
        r2 = laygen.route(None,
                          laygen.layers['metal'][7],
                          xy0=rxy[0],
                          xy1=rxy[1],
                          gridname0=rg_route)

    #connect VSS between sar/samp/clkd in space region
    '''
    for r in rvssclkd_m7:
        if r.xy[1][0] < sar_xy[0]:
            laygen.add_rect(None, np.array([[r.xy[0][0],rvsssar_m7[0].xy[0][1]], r.xy[1]]), laygen.layers['metal'][7])
        if r.xy[0][0] > space1_xy[0]:
            laygen.add_rect(None, np.array([[r.xy[0][0],rvsssar_m7[0].xy[0][1]], r.xy[1]]), laygen.layers['metal'][7])
    '''
    #create VSS pins for connection to core
    for i, r in enumerate(rvsssar_m7):
        pxy = np.array([[r.xy[0][0], 0], r.xy[1]])
        laygen.add_rect(None, pxy, laygen.layers['metal'][7])
        # laygen.add_pin('VSS_SAR_M7_'+str(i), 'VSS', pxy, laygen.layers['pin'][7])
    '''
    #connect VDDSAMP between samp/clkd in space region
    for r in rvddclkd_m7:
        if r.xy[1][0] < sar_xy[0]:
            laygen.add_rect(None, np.array([[r.xy[0][0],rvddsamp_m7[0].xy[0][1]], r.xy[1]]), laygen.layers['metal'][7])
        if r.xy[0][0] > space1_xy[0]:
            laygen.add_rect(None, np.array([[r.xy[0][0],rvddsamp_m7[0].xy[0][1]], r.xy[1]]), laygen.layers['metal'][7])
    #M8 routes
    #input_rails_rect = [rvddclkd_m7, rvssclkd_m7]
    #rvddclkd_m8, rvssclkd_m8= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_CLKD_M8_', 
    #            layer=laygen.layers['pin'][8], gridname=rg_m7m8_thick, netnames=['VDDSAMP', 'VSS'], direction='x', 
    #            input_rails_rect=input_rails_rect, generate_pin=True, overwrite_start_coord=None, overwrite_end_coord=None,
    #            offset_start_index=1, offset_end_index=0)
    input_rails_rect = [rvssclkd_m7, rvddclkd_m7]
    rvssclkd_m8, rvddclkd_m8= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_CLKD_M8_', 
                layer=laygen.layers['pin'][8], gridname=rg_m7m8_thick, netnames=['VSS', 'VDDSAMP'], direction='x', 
                input_rails_rect=input_rails_rect, generate_pin=True, overwrite_start_coord=None, overwrite_end_coord=None,
                offset_start_index=1, offset_end_index=0)
    '''
    #M8 routes
    input_rails_rect = [rvsssamp_m7, rvddsamp_m7]
    rvsssamp_m8, rvddsamp_m8 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_SAMP_M8_',
        layer=laygen.layers['pin'][8],
        gridname=rg_route_m7m8,
        netnames=['VSS', 'VDDSAMP'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=1,
        offset_end_index=0)
    input_rails_rect = [rvddsar_m7, rvsssar_m7]
    rvddsar_m8, rvsssar_m8 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_SAR_M8_',
        layer=laygen.layers['pin'][8],
        gridname=rg_route_m7m8,
        netnames=['VDDSAR', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    input_rails_rect = [
        rvref0_m7, rvsssar_m7_upper, rvref1_m7, rvsssar_m7_upper, rvref2_m7,
        rvsssar_m7_upper
    ]
    laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_VREF_M8_',
        layer=laygen.layers['metal'][8],
        gridname=rg_route_m7m8,
        netnames=['VREF<0>', 'VSS', 'VREF<1>', 'VSS', 'VREF<2>', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=1,
        offset_end_index=0)

    #osp/osm route
    if use_offset == True:  # To be done
        pdict_os_m4m5 = laygen.get_inst_pin_xy(None, None, rg_m4m5)
        rosp_m5 = []
        rosm_m5 = []
        for i in range(num_slices):
            rh0, rv0 = laygen.route_hv(
                laygen.layers['metal'][4],
                laygen.layers['metal'][5],
                pdict_os_m4m5[isar.name]['OSP' + str(i)][0],
                pdict_os_m4m5[isar.name]['OSP' + str(i)][0] +
                np.array([-2 * i - 2, -10]),
                gridname0=rg_m4m5)
            rosp_m5.append(rv0)
            rh0, rv0 = laygen.route_hv(
                laygen.layers['metal'][4],
                laygen.layers['metal'][5],
                pdict_os_m4m5[isar.name]['OSM' + str(i)][0],
                pdict_os_m4m5[isar.name]['OSM' + str(i)][0] +
                np.array([-2 * num_slices - 2 * i - 2, -10]),
                gridname0=rg_m4m5)
            rosm_m5.append(rv0)
        pdict_os_m5m6 = laygen.get_inst_pin_xy(None, None, rg_m5m6)
        x0 = pdict_os_m5m6[isar.name]['VREF0<0>'][0][0] - 4 * num_slices
        y0 = pdict_os_m5m6[isar.name]['VREF0<0>'][0][1] - 8
        rosp_m6 = []
        rosm_m6 = []
        for i in range(num_slices):
            xy0 = laygen.get_xy(obj=rosp_m5[i], gridname=rg_m5m6, sort=True)[0]
            rv0, rh0 = laygen.route_vh(laygen.layers['metal'][5],
                                       laygen.layers['metal'][6],
                                       xy0,
                                       np.array([x0, y0 - 2 * i]),
                                       gridname0=rg_m5m6)
            laygen.boundary_pin_from_rect(rh0,
                                          rg_m5m6,
                                          'OSP' + str(i),
                                          laygen.layers['pin'][6],
                                          size=6,
                                          direction='left')
            rosp_m6.append(rh0)
            xy0 = laygen.get_xy(obj=rosm_m5[i], gridname=rg_m5m6, sort=True)[0]
            rv0, rh0 = laygen.route_vh(laygen.layers['metal'][5],
                                       laygen.layers['metal'][6],
                                       xy0,
                                       np.array([x0, y0 - 2 * i - 1]),
                                       gridname0=rg_m5m6)
            laygen.boundary_pin_from_rect(rh0,
                                          rg_m5m6,
                                          'OSM' + str(i),
                                          laygen.layers['pin'][6],
                                          size=6,
                                          direction='left')
            rosm_m6.append(rh0)
        # pdict_os_m4m5 = laygen.get_inst_pin_xy(None, None, rg_m4m5_basic_thick)
        # rosp_m5=[]
        # rosm_m5=[]
        # for i in range(num_slices):
        #     rh0, rv0 = laygen.route_hv(laygen.layers['metal'][4], laygen.layers['metal'][5],
        #                     pdict_os_m4m5[isar.name]['OSP'+str(i)][0], pdict_os_m4m5[isar.name]['OSP'+str(i)][0]+np.array([-i-2, -10]), gridname=rg_m4m5_basic_thick)
        #     rosp_m5.append(rv0)
        #     rh0, rv0 = laygen.route_hv(laygen.layers['metal'][4], laygen.layers['metal'][5],
        #                     pdict_os_m4m5[isar.name]['OSM'+str(i)][0], pdict_os_m4m5[isar.name]['OSM'+str(i)][0]+np.array([-num_slices-i-2, -10]), gridname=rg_m4m5_basic_thick)
        #     rosm_m5.append(rv0)
        # pdict_os_m5m6 = laygen.get_inst_pin_xy(None, None, rg_m5m6_thick)
        # x0=pdict_os_m5m6[isar.name]['VREF0<0>'][0][0]-4*num_slices
        # y0=pdict_os_m5m6[isar.name]['VREF0<0>'][0][1]-8
        # rosp_m6=[]
        # rosm_m6=[]
        # for i in range(num_slices):
        #     xy0=laygen.get_xy(obj = rosp_m5[i], gridname=rg_m5m6_thick, sort=True)[0]
        #     rv0, rh0 = laygen.route_vh(laygen.layers['metal'][5], laygen.layers['metal'][6], xy0, np.array([x0, y0-2*i]), gridname=rg_m5m6_thick)
        #     laygen.boundary_pin_from_rect(rh0, rg_m5m6_thick, 'OSP' + str(i), laygen.layers['pin'][6], size=6,
        #                                   direction='left')
        #     rosp_m6.append(rh0)
        #     xy0=laygen.get_xy(obj = rosm_m5[i], gridname=rg_m5m6_thick, sort=True)[0]
        #     rv0, rh0 = laygen.route_vh(laygen.layers['metal'][5], laygen.layers['metal'][6], xy0, np.array([x0, y0-2*i-1]), gridname=rg_m5m6_thick)
        #     laygen.boundary_pin_from_rect(rh0, rg_m5m6_thick, 'OSM' + str(i), laygen.layers['pin'][6], size=6,
        #                                   direction='left')
        #     rosm_m6.append(rh0)

    #other pins - duplicate
    pin_prefix_list = [
        'INP', 'INM', 'VREF', 'ASCLKD', 'EXTSEL_CLK', 'ADCOUT', 'CLKOUT_DES',
        'CLKCAL', 'RSTP', 'RSTN', 'CLKIP', 'CLKIN', 'SF_', 'VREF_SF_',
        'MODESEL'
    ]
    if use_sf == False:
        pin_prefix_list.remove('SF_')
    if vref_sf == False:
        pin_prefix_list.remove('VREF_SF_')
    if clkgen_mode == False:
        pin_prefix_list.remove('MODESEL')
    for pn, p in sar_pins.items():
        for pfix in pin_prefix_list:
            if pn.startswith(pfix):
                laygen.add_pin(pn, sar_pins[pn]['netname'],
                               sar_xy + sar_pins[pn]['xy'],
                               sar_pins[pn]['layer'])

    bndvss = rvsssamp_m7[-1].get_xy()[1]
    bndvdd = rvddsamp_m7[-1].get_xy()[1]
    xbnd = space_size * 2 + space2_size * 2 + sar_size
    bnd = (xbnd[0], max([bndvdd[1], bndvss[1]]))

    laygen.add_rect(None, np.array([origin, bnd]), laygen.layers['prbnd'])
def generate_clkdis_viadel_htree(laygen,
                                 objectname_pfix,
                                 logictemp_lib,
                                 working_lib,
                                 grid,
                                 pitch_x,
                                 num_ways,
                                 trackm,
                                 m_clko,
                                 num_bits,
                                 origin=np.array([0, 0])):
    """generate htree cell """

    pg = grid['pg']
    rg_m1m2 = grid['rg_m1m2']
    rg_m1m2_thick = grid['rg_m1m2_thick']
    rg_m2m3 = grid['rg_m2m3']
    rg_m2m3_basic = grid['rg_m2m3_basic']
    rg_m2m3_thick = grid['rg_m2m3_thick']
    rg_m2m3_thick2 = grid['rg_m2m3_thick2']
    rg_m3m4 = grid['rg_m3m4']
    rg_m3m4_dense = grid['rg_m3m4_dense']
    rg_m3m4_thick2 = grid['rg_m3m4_thick2']
    rg_m4m5 = grid['rg_m4m5']
    rg_m4m5_thick = grid['rg_m4m5_thick']
    rg_m5m6 = grid['rg_m5m6']
    rg_m5m6_thick = grid['rg_m5m6_thick']
    rg_m6m7 = grid['rg_m6m7']

    # trackm = 24
    #len_h = laygen.grids.get_absgrid_coord_x(gridname=rg_m4m5, x=20.16)
    len_h = laygen.grids.get_absgrid_coord_x(gridname=rg_m4m5,
                                             x=pitch_x) * num_ways / 16
    len_in = laygen.grids.get_absgrid_coord_x(gridname=rg_m4m5, x=2)
    #num_ways = 8
    # num_bits = 5
    # m_clko = 2
    num_vss_h = 4
    num_vdd_h = 4

    ##place all viadel and h grids
    viadel = laygen.place(name='I' + objectname_pfix + 'VIADEL0',
                          templatename='clk_dis_viadel',
                          gridname=pg,
                          xy=origin,
                          template_libname=working_lib)
    vd_CLKI0_xy = laygen.get_inst_pin_xy(viadel.name,
                                         'CLKI0_' + str(trackm - 1),
                                         rg_m4m5)[0]
    vd_CLKI1_xy = laygen.get_inst_pin_xy(viadel.name,
                                         'CLKI1_' + str(trackm - 1),
                                         rg_m4m5)[0]
    htree0 = laygen.place(name='I' + objectname_pfix + 'HTREE0',
                          templatename='clk_dis_htree',
                          gridname=pg,
                          xy=origin,
                          template_libname=working_lib)
    ht0_WO_xy = laygen.get_inst_pin_xy(htree0.name, 'WO0_0_0', rg_m4m5)[0]
    ht0_WI_xy = laygen.get_inst_pin_xy(htree0.name, 'WI_0', rg_m4m5)[0]
    #move htree0
    res_x = laygen.get_grid(gridname=rg_m4m5).width
    res_y = laygen.get_grid(gridname=rg_m4m5).height
    htree0.xy = np.array([(vd_CLKI0_xy[0] - ht0_WO_xy[0]) * res_x,
                          (vd_CLKI0_xy[1] - ht0_WO_xy[1]) * res_y])
    ht0_WI_xy = np.array([
        ht0_WI_xy[0] + (vd_CLKI0_xy[0] - ht0_WO_xy[0]),
        ht0_WI_xy[1] + (vd_CLKI0_xy[1] - ht0_WO_xy[1])
    ])
    htree1 = laygen.place(name='I' + objectname_pfix + 'HTREE1',
                          templatename='clk_dis_htree',
                          gridname=pg,
                          xy=origin,
                          template_libname=working_lib)
    ht1_WO_xy = laygen.get_inst_pin_xy(htree1.name, 'WO0_0_0', rg_m4m5)[0]
    ht1_WI_xy = laygen.get_inst_pin_xy(htree1.name, 'WI_0', rg_m4m5)[0]
    #move htree1
    #htree1.xy = np.array([(vd_CLKI1_xy[0]-ht1_WO_xy[0])*0.08, (vd_CLKI1_xy[1]-ht1_WO_xy[1])*0.08])
    htree1.xy = np.array([(vd_CLKI1_xy[0] - ht1_WO_xy[0]) * res_x,
                          (vd_CLKI1_xy[1] - ht1_WO_xy[1]) * res_y])
    ht1_WI_xy = np.array([
        ht1_WI_xy[0] + (vd_CLKI1_xy[0] - ht1_WO_xy[0]),
        ht1_WI_xy[1] + (vd_CLKI1_xy[1] - ht1_WO_xy[1])
    ])
    #Create input wire
    #print(ht0_WI_xy)
    #print(ht1_WI_xy)

    ##create input vias and metals

    for i in range(trackm):
        # for j in range (trackm):
        #     laygen.via(None, xy=np.array([ht0_WI_xy[0]+2*i, ht0_WI_xy[1]+2*j]), gridname=rg_m4m5)
        #     laygen.via(None, xy=np.array([ht1_WI_xy[0]+2*i, ht1_WI_xy[1]+2*j]), gridname=rg_m4m5)

        vipx = laygen.route(None,
                            laygen.layers['metal'][5],
                            xy0=np.array([ht0_WI_xy[0] + 2 * i, ht0_WI_xy[1]]),
                            xy1=np.array([
                                ht0_WI_xy[0] + 2 * i,
                                ht0_WI_xy[1] + 2 * (trackm - 1) + trackm
                            ]),
                            gridname0=rg_m4m5)
        vinx = laygen.route(None,
                            laygen.layers['metal'][5],
                            xy0=np.array([ht1_WI_xy[0] + 2 * i, ht1_WI_xy[1]]),
                            xy1=np.array([
                                ht1_WI_xy[0] + 2 * i,
                                ht1_WI_xy[1] + 2 * (trackm - 1) + trackm
                            ]),
                            gridname0=rg_m4m5)

        # laygen.route(None, laygen.layers['metal'][4], xy0=np.array([ht0_WI_xy[0], ht0_WI_xy[1]+2*i]), xy1=np.array([ht0_WI_xy[0]+(len_h*2-2), ht0_WI_xy[1]+2*i]),
        #         gridname0=rg_m4m5)
        # laygen.route(None, laygen.layers['metal'][4], xy0=np.array([ht1_WI_xy[0]+2*(trackm-1), ht1_WI_xy[1]+2*i]), xy1=np.array([ht1_WI_xy[0]-(len_h*2-2), ht1_WI_xy[1]+2*i]),
        #         gridname0=rg_m4m5)

        # for j in range (trackm):
        #     laygen.via(None, xy=np.array([ht0_WI_xy[0]+(len_h*2-2)-2*i, ht0_WI_xy[1]+2*j]), gridname=rg_m4m5)
        #     laygen.via(None, xy=np.array([ht1_WI_xy[0]-(len_h*2-2)+2*i, ht1_WI_xy[1]+2*j]), gridname=rg_m4m5)

        # vipx=laygen.route(None, laygen.layers['metal'][5], xy0=np.array([ht0_WI_xy[0]+(len_h*2-2)-2*i, ht0_WI_xy[1]]),
        #                   xy1=np.array([ht0_WI_xy[0]+(len_h*2-2)-2*i, ht0_WI_xy[1]+trackm*2]),
        #         gridname0=rg_m4m5)
        laygen.boundary_pin_from_rect(vipx,
                                      gridname=rg_m4m5,
                                      name='CLKIP_' + str(i),
                                      layer=laygen.layers['pin'][5],
                                      size=2,
                                      direction='top',
                                      netname='CLKIP')
        # vinx=laygen.route(None, laygen.layers['metal'][5], xy0=np.array([ht1_WI_xy[0]-(len_h*2-2)+2*i, ht1_WI_xy[1]]),
        #                   xy1=np.array([ht1_WI_xy[0]-(len_h*2-2)+2*i, ht1_WI_xy[1]+trackm*2]),
        #         gridname0=rg_m4m5)
        laygen.boundary_pin_from_rect(vinx,
                                      gridname=rg_m4m5,
                                      name='CLKIN_' + str(i),
                                      layer=laygen.layers['pin'][5],
                                      size=2,
                                      direction='top',
                                      netname='CLKIN')

    #Create pins
    #set and rst
    RST_xy = laygen.get_inst_pin_xy(viadel.name,
                                    pinname='RSTP',
                                    gridname=rg_m2m3_basic)
    laygen.pin(name='RSTP',
               layer=laygen.layers['pin'][2],
               xy=RST_xy,
               gridname=rg_m2m3_basic)
    RST_xy = laygen.get_inst_pin_xy(viadel.name,
                                    pinname='RSTN',
                                    gridname=rg_m2m3_basic)
    laygen.pin(name='RSTN',
               layer=laygen.layers['pin'][2],
               xy=RST_xy,
               gridname=rg_m2m3_basic)

    #CLKCAL
    for i in range(num_ways):
        for j in range(num_bits):
            CAL_xy = laygen.get_inst_pin_xy(viadel.name,
                                            pinname='CLKCAL' + str(i) + '<' +
                                            str(j) + '>',
                                            gridname=rg_m2m3_basic)
            laygen.pin(name='CLKCAL' + str(i) + '<' + str(j) + '>',
                       layer=laygen.layers['pin'][2],
                       xy=CAL_xy,
                       gridname=rg_m2m3_basic)

    #CLKO
    for i in range(num_ways):
        for j in range(m_clko):
            CLKO_xy = laygen.get_inst_pin_xy(viadel.name,
                                             pinname='CLKO' + str(i) + '_' +
                                             str(j),
                                             gridname=rg_m5m6)
            laygen.pin(name='CLKO' + str(i) + '<' + str(j) + '>',
                       layer=laygen.layers['pin'][5],
                       xy=CLKO_xy,
                       gridname=rg_m5m6,
                       netname='CLKO<' + str(i) + '>')

    #DATAO
    for i in range(num_ways):
        DATAO_xy = laygen.get_inst_pin_xy(viadel.name,
                                          pinname='DATAO<' + str(i) + '>',
                                          gridname=rg_m3m4)
        laygen.pin(name='DATAO<' + str(i) + '>',
                   layer=laygen.layers['pin'][3],
                   xy=DATAO_xy,
                   gridname=rg_m3m4,
                   netname='DATAO<' + str(i) + '>')

    ##VDD and VSS pin
    rvssl_m4 = []
    rvssr_m4 = []
    rvddl_m4 = []
    rvddr_m4 = []
    rvdd_m5 = []
    rvss_m5 = []
    for i in range(num_ways):
        for j in range(num_vss_h):
            vssl_xy = laygen.get_inst_pin_xy(viadel.name,
                                             'VSS0_' + str(i) + '_' + str(j),
                                             rg_m3m4_thick2)
            rvssl_m4.append(
                laygen.route(None,
                             laygen.layers['metal'][4],
                             xy0=vssl_xy[0],
                             xy1=vssl_xy[1],
                             gridname0=rg_m3m4_thick2))
            vssr_xy = laygen.get_inst_pin_xy(viadel.name,
                                             'VSS1_' + str(i) + '_' + str(j),
                                             rg_m3m4_thick2)
            rvssr_m4.append(
                laygen.route(None,
                             laygen.layers['metal'][4],
                             xy0=vssr_xy[0],
                             xy1=vssr_xy[1],
                             gridname0=rg_m3m4_thick2))
            # laygen.pin(name='VSS0_'+str(i)+'_'+str(j), layer=laygen.layers['pin'][4], xy=vssl_xy, gridname=rg_m3m4_thick2, netname='VSS')
            # laygen.pin(name='VSS1_'+str(i)+'_'+str(j), layer=laygen.layers['pin'][4], xy=vssr_xy, gridname=rg_m3m4_thick2, netname='VSS')
        for j in range(num_vdd_h):
            vddl_xy = laygen.get_inst_pin_xy(viadel.name,
                                             'VDD0_' + str(i) + '_' + str(j),
                                             rg_m3m4_thick2)
            rvddl_m4.append(
                laygen.route(None,
                             laygen.layers['metal'][4],
                             xy0=vddl_xy[0],
                             xy1=vddl_xy[1],
                             gridname0=rg_m3m4_thick2))
            vddr_xy = laygen.get_inst_pin_xy(viadel.name,
                                             'VDD1_' + str(i) + '_' + str(j),
                                             rg_m3m4_thick2)
            rvddr_m4.append(
                laygen.route(None,
                             laygen.layers['metal'][4],
                             xy0=vddr_xy[0],
                             xy1=vddr_xy[1],
                             gridname0=rg_m3m4_thick2))
            # laygen.pin(name='VDD0_'+str(i)+'_'+str(j), layer=laygen.layers['pin'][4], xy=vddl_xy, gridname=rg_m3m4_thick2, netname='VDD')
            # laygen.pin(name='VDD1_'+str(i)+'_'+str(j), layer=laygen.layers['pin'][4], xy=vddr_xy, gridname=rg_m3m4_thick2, netname='VDD')
        rvddl_m5, rvssl_m5 = laygenhelper.generate_power_rails_from_rails_rect(
            laygen,
            routename_tag='M5L',
            layer=laygen.layers['metal'][5],
            gridname=rg_m4m5_thick,
            netnames=['VDD', 'VSS'],
            direction='y',
            input_rails_rect=[rvddl_m4, rvssl_m4],
            generate_pin=False,
            overwrite_start_coord=None,
            overwrite_end_coord=None,
            overwrite_num_routes=None,
            offset_start_index=0,
            offset_end_index=0)
        rvddr_m5, rvssr_m5 = laygenhelper.generate_power_rails_from_rails_rect(
            laygen,
            routename_tag='M5L',
            layer=laygen.layers['metal'][5],
            gridname=rg_m4m5_thick,
            netnames=['VDD', 'VSS'],
            direction='y',
            input_rails_rect=[rvddr_m4, rvssr_m4],
            generate_pin=False,
            overwrite_start_coord=None,
            overwrite_end_coord=None,
            overwrite_num_routes=None,
            offset_start_index=0,
            offset_end_index=-2)
        rvddl_m4 = []
        rvssl_m4 = []
        rvddr_m4 = []
        rvssr_m4 = []
        rvdd_m5 += rvddl_m5
        rvdd_m5 += rvddr_m5
        rvss_m5 += rvssl_m5
        rvss_m5 += rvssr_m5
    rvdd_m6, rvss_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='M6',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=[rvdd_m5, rvss_m5],
        generate_pin=True,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        overwrite_num_routes=None,
        offset_start_index=2,
        offset_end_index=0)

    #prboundary
    size_x = laygen.templates.get_template('clk_dis_viadel',
                                           libname='clk_dis_generated').size[0]
    y_grid = laygen.get_template('tap', libname=logictemplib).size[1]
    size_y = (int(laygen.get_rect(vinx.name).xy1[1] / y_grid) + 1) * y_grid
    print('prb:', size_x, size_y)
    laygen.add_rect(None,
                    np.array([origin, origin + np.array([size_x, size_y])]),
                    laygen.layers['prbnd'])
Exemple #10
0
def generate_tisaradc_dcap(laygen,
                           objectname_pfix,
                           templib_logic,
                           placement_grid,
                           routing_grid_m2m3,
                           routing_grid_m3m4,
                           rg_m3m4_basic_thick,
                           rg_m4m5_thick,
                           num_bits=9,
                           origin=np.array([0, 0])):
    """generate tisaradc """
    inv_name = 'inv_2x'
    tap_name = 'tap'
    dcap_unit_name = 'tisaradc_dcap_unit'
    pg = placement_grid

    rg_m2m3 = routing_grid_m2m3
    rg_m3m4 = routing_grid_m3m4
    # rg_m4m5 = routing_grid_m4m5
    # rg_m4m5_basic_thick = routing_grid_m4m5_basic_thick
    # rg_m4m5_thick = routing_grid_m4m5_thick
    # rg_m5m6 = routing_grid_m5m6
    # rg_m5m6_thick = routing_grid_m5m6_thick
    # rg_m5m6_thick_basic = routing_grid_m5m6_thick_basic
    # rg_m6m7_thick = routing_grid_m6m7_thick

    #boundaries
    x0=laygen.templates.get_template('tisaradc_dcap_unit', workinglib).xy[1][0] + \
           laygen.templates.get_template('tap', templib_logic).xy[1][0]*2
    m_bnd_float = x0 / laygen.templates.get_template(
        'boundary_bottom').xy[1][0]
    m_bnd = int(m_bnd_float)
    if not m_bnd_float == m_bnd:
        m_bnd += 1

    devname_bnd_left = []
    devname_bnd_right = []
    transform_bnd_left = []
    transform_bnd_right = []
    num_row = num_bits * 4
    for i in range(num_row):
        if i % 2 == 0:
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
    [bnd_bottom, bnd_top, bnd_left, bnd_right] = generate_boundary(
        laygen,
        objectname_pfix='BND0',
        placement_grid=pg,
        devname_bottom=[
            'boundary_bottomleft', 'boundary_bottom', 'boundary_bottomright'
        ],
        shape_bottom=[
            np.array([1, 1]),
            np.array([m_bnd, 1]),
            np.array([1, 1])
        ],
        devname_top=['boundary_topleft', 'boundary_top', 'boundary_topright'],
        shape_top=[np.array([1, 1]),
                   np.array([m_bnd, 1]),
                   np.array([1, 1])],
        devname_left=devname_bnd_left,
        transform_left=transform_bnd_left,
        devname_right=devname_bnd_right,
        transform_right=transform_bnd_right,
        origin=np.array([0, 0]))

    #Calculate layout size
    array_origin = origin + laygen.get_template_xy(
        name='boundary_bottomleft', gridname=pg, libname=utemplib)
    tapr_origin = np.array([laygen.get_template_xy(name='tisaradc_dcap_unit', gridname=pg, libname=workinglib)[0], 0]) \
                  + np.array([0, laygen.get_template_xy(name='boundary_bottomleft', gridname=pg, libname=utemplib)[1]]) \
                  + np.array([laygen.get_template_xy(name='boundary_bottomleft', gridname=pg, libname=utemplib)[0], 0]) \
                  + np.array([laygen.get_template_xy(name=tap_name, gridname=pg, libname=templib_logic)[0], 0])

    # placement
    itapl = []
    idcap = []
    for i in range(num_row):
        if i % 2 == 0: tf = 'R0'
        else: tf = 'MX'
        if i == 0:
            itapl.append(
                laygen.relplace(name="I" + objectname_pfix + 'ITAPL' + str(i),
                                templatename=tap_name,
                                gridname=pg,
                                refinstname=None,
                                xy=array_origin,
                                template_libname=templib_logic))
        else:
            itapl.append(
                laygen.relplace(name="I" + objectname_pfix + 'ITAPL' + str(i),
                                templatename=tap_name,
                                gridname=pg,
                                refinstname=itapl[-1].name,
                                template_libname=templib_logic,
                                direction='top',
                                transform=tf))
        idcap.append(
            laygen.relplace(name="I" + objectname_pfix + 'Idcap' + str(i),
                            templatename=dcap_unit_name,
                            gridname=pg,
                            refinstname=itapl[-1].name,
                            template_libname=workinglib,
                            direction='right',
                            transform=tf))
    itapr = []
    for i in range(num_row):
        if i % 2 == 0: tf = 'R0'
        else: tf = 'MX'
        if i == 0:
            itapr.append(
                laygen.relplace(name="I" + objectname_pfix + 'ITAPR' + str(i),
                                templatename=tap_name,
                                gridname=pg,
                                refinstname=None,
                                xy=tapr_origin,
                                template_libname=templib_logic))
        else:
            itapr.append(
                laygen.relplace(name="I" + objectname_pfix + 'ITAPR' + str(i),
                                templatename=tap_name,
                                gridname=pg,
                                refinstname=itapr[-1].name,
                                template_libname=templib_logic,
                                direction='top',
                                transform=tf))

    # pins
    pdict = laygen.get_inst_pin_xy(None, None, rg_m3m4_basic_thick)
    # laygen.pin(name='I', layer=laygen.layers['pin'][4], xy=laygen.get_inst_pin_xy(idcap[-1].name, 'I', rg_m3m4_basic_thick),
    #                gridname=rg_m3m4_basic_thick)

    # power pin
    pwr_dim = laygen.get_template_xy(name=itapl[0].cellname,
                                     gridname=rg_m2m3,
                                     libname=itapl[0].libname)
    rvddl_m3 = []
    rvssl_m3 = []
    rvddr_m3 = []
    rvssr_m3 = []
    for i in range(0, int(pwr_dim[0] / 2)):
        rvddl_m3.append(
            laygen.route(None,
                         laygen.layers['metal'][3],
                         xy0=np.array([2 * i + 1, 0]),
                         xy1=np.array([2 * i + 1, 0]),
                         gridname0=rg_m2m3,
                         refinstname0=itapl[0].name,
                         refpinname0='VSS',
                         refinstindex0=np.array([0, 0]),
                         refinstname1=itapl[num_row - 1].name,
                         refpinname1='VSS',
                         refinstindex1=np.array([0, 0])))
        rvssl_m3.append(
            laygen.route(None,
                         laygen.layers['metal'][3],
                         xy0=np.array([2 * i + 2, 0]),
                         xy1=np.array([2 * i + 2, 0]),
                         gridname0=rg_m2m3,
                         refinstname0=itapl[0].name,
                         refpinname0='VSS',
                         refinstindex0=np.array([0, 0]),
                         refinstname1=itapl[num_row - 1].name,
                         refpinname1='VSS',
                         refinstindex1=np.array([0, 0])))
        for j in range(num_row):
            laygen.via(None,
                       xy=np.array([2 * i + 1, 0]),
                       gridname=rg_m2m3,
                       refinstname=itapl[j].name,
                       refpinname='VDD')
            laygen.via(None,
                       xy=np.array([2 * i + 2, 0]),
                       gridname=rg_m2m3,
                       refinstname=itapl[j].name,
                       refpinname='VSS')
        # laygen.pin(name = 'VDDL'+str(i), layer = laygen.layers['pin'][3], refobj = rvddl_m3[-1], gridname=rg_m2m3, netname='VDD')
        # laygen.pin(name = 'VSSL'+str(i), layer = laygen.layers['pin'][3], refobj = rvssl_m3[-1], gridname=rg_m2m3, netname='VSS')
        rvddr_m3.append(
            laygen.route(None,
                         laygen.layers['metal'][3],
                         xy0=np.array([2 * i + 1, 0]),
                         xy1=np.array([2 * i + 1, 0]),
                         gridname0=rg_m2m3,
                         refinstname0=itapr[0].name,
                         refpinname0='VSS',
                         refinstindex0=np.array([0, 0]),
                         refinstname1=itapr[num_row - 1].name,
                         refpinname1='VSS',
                         refinstindex1=np.array([0, 0])))
        rvssr_m3.append(
            laygen.route(None,
                         laygen.layers['metal'][3],
                         xy0=np.array([2 * i + 2, 0]),
                         xy1=np.array([2 * i + 2, 0]),
                         gridname0=rg_m2m3,
                         refinstname0=itapr[0].name,
                         refpinname0='VSS',
                         refinstindex0=np.array([0, 0]),
                         refinstname1=itapr[num_row - 1].name,
                         refpinname1='VSS',
                         refinstindex1=np.array([0, 0])))
        for j in range(num_row):
            laygen.via(None,
                       xy=np.array([2 * i + 1, 0]),
                       gridname=rg_m2m3,
                       refinstname=itapr[j].name,
                       refpinname='VDD')
            laygen.via(None,
                       xy=np.array([2 * i + 2, 0]),
                       gridname=rg_m2m3,
                       refinstname=itapr[j].name,
                       refpinname='VSS')
        # laygen.pin(name = 'VDDR'+str(i), layer = laygen.layers['pin'][3], refobj = rvddr_m3[-1], gridname=rg_m2m3, netname='VDD')
        # laygen.pin(name = 'VSSR'+str(i), layer = laygen.layers['pin'][3], refobj = rvssr_m3[-1], gridname=rg_m2m3, netname='VSS')

    #m4
    input_rails_rect = [rvddl_m3, rvssl_m3]
    rvddl_m4, rvssl_m4 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='L_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_basic_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=2,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    x1_phy = laygen.get_xy(obj =bnd_right[0])[0]\
         +laygen.get_xy(obj =bnd_right[0].template)[0]
    x1 = laygen.grids.get_absgrid_x(rg_m3m4_basic_thick, x1_phy)
    input_rails_rect = [rvddr_m3, rvssr_m3]
    rvddr_m4, rvssr_m4 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='R_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_basic_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=x1 - 2,
        offset_start_index=0,
        offset_end_index=0)

    #m5
    input_rails_rect = [rvddl_m4, rvssl_m4]
    rvddl_m5, rvssl_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='L_M5_',
        layer=laygen.layers['pin'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    y1_phy = laygen.get_xy(obj =bnd_top[0])[1]\
         +laygen.get_xy(obj =bnd_top[0].template)[1]
    y1 = laygen.grids.get_absgrid_x(rg_m4m5_thick, y1_phy)
    input_rails_rect = [rvddr_m4, rvssr_m4]
    rvddr_m5, rvssr_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='R_M5_',
        layer=laygen.layers['pin'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
def generate_r2r_dac_array(laygen, objectname_pfix, templib_logic, placement_grid, routing_grid_m4m5, routing_grid_m5m6,
                           rg_m3m4_basic_thick, rg_m5m6_thick, m, num_bits, num_hori, num_vert, origin=np.array([0, 0])):
    """generate r2rdac """
    r2r_name='r2r_dac'
    bcap_name='r2r_dac_bcap_array'
    sar_name='sar_wsamp'
    tisar_name='tisaradc_body'
    ret_name='adc_retimer'
    tgate_name = 'tgate_'+str(m)+'x'
    pg = placement_grid

    rg_m4m5 = routing_grid_m4m5
    rg_m5m6 = routing_grid_m5m6
    # rg_m4m5 = routing_grid_m4m5
    # rg_m4m5_basic_thick = routing_grid_m4m5_basic_thick
    # rg_m4m5_thick = routing_grid_m4m5_thick
    # rg_m5m6 = routing_grid_m5m6
    # rg_m5m6_thick = routing_grid_m5m6_thick
    # rg_m5m6_thick_basic = routing_grid_m5m6_thick_basic
    # rg_m6m7_thick = routing_grid_m6m7_thick

    #boundaries
    x0=laygen.templates.get_template('capdac', workinglib).xy[1][0] - \
           laygen.templates.get_template('boundary_bottomleft').xy[1][0]*2
    m_bnd_float = x0 / laygen.templates.get_template('boundary_bottom').xy[1][0]
    m_bnd = int(m_bnd_float)
    if not m_bnd_float == m_bnd:
        m_bnd += 1

    #Calculate reference coordinate
    bcap_origin = np.array([laygen.get_template_xy(name=r2r_name, gridname=pg, libname=workinglib)[0]*num_hori, 0])
    x1_phy = laygen.get_template_xy(name=r2r_name, gridname=None, libname=workinglib)[0]*num_hori \
             + laygen.get_template_xy(name=bcap_name, gridname=None, libname=workinglib)[0]
    pin_origin_x = laygen.grids.get_absgrid_x(rg_m5m6, x1_phy)
    pin_origin_x_thick = laygen.grids.get_absgrid_x(rg_m5m6_thick, x1_phy)
    if use_sf == True and vref_sf == True:
        y1_phy = origin[1] + laygen.get_template_xy(name=sar_name, gridname=None, libname=workinglib)[1] \
                       + laygen.get_template_xy(name=ret_name, gridname=None, libname=workinglib)[1]
        pin_origin_y = laygen.grids.get_absgrid_y(rg_m5m6, y1_phy)
    elif use_offset == True:
        pin_origin_y = origin[1] + laygen.get_template_pin_xy(tisar_name, 'OSM'+str(num_slices-1), gridname=rg_m5m6, libname=workinglib)[0][1] - num_slices*2 - 6
    else:
        y1_phy = origin[1] + laygen.get_template_xy(name=sar_name, gridname=None, libname=workinglib)[1] \
                       + laygen.get_template_xy(name=ret_name, gridname=None, libname=workinglib)[1]
        pin_origin_y = laygen.grids.get_absgrid_y(rg_m5m6, y1_phy)
    # pin_origin_y1_thick = origin[1] + laygen.get_template_pin_xy(sar_name, 'SF_BIAS', rg_m5m6_thick, libname=workinglib)[0][1] \
    #                + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    # pin_origin_y0_thick = origin[1] + laygen.get_template_pin_xy(sar_name, 'VREF_SF_BIAS', rg_m5m6_thick, libname=workinglib)[0][1] \
    #                + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    if use_sf == True:
        pin_origin_y1_thick = origin[1] + \
                              laygen.get_template_pin_xy(sar_name, 'SF_BIAS', rg_m5m6_thick, libname=workinglib)[0][1] \
                              + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    else:
        pin_origin_y1_thick = origin[1] + 0 \
                              + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    if vref_sf == True:
        pin_origin_y0_thick = origin[1] + \
                              laygen.get_template_pin_xy(sar_name, 'VREF_SF_BIAS', rg_m5m6_thick, libname=workinglib)[0][1] \
                              + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    else:
        pin_origin_y0_thick = origin[1] + 2 \
                              + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    # pin_origin_y0_thick = laygen.grids.get_absgrid_y(rg_m5m6_thick, y0_phy)

    # placement
    irdac = []
    for i in range(num_vert):
        if i == 0:
            irdac.append(laygen.relplace(name="I" + objectname_pfix + 'IRDAC'+str(i), templatename=r2r_name,
                              gridname=pg, refinstname=None, xy=origin, shape=[num_hori, 1], template_libname=workinglib))
        else:
            irdac.append(laygen.relplace(name="I" + objectname_pfix + 'IRDAC'+str(i), templatename=r2r_name,
                              gridname=pg, refinstname=irdac[-1].name, shape=[num_hori, 1], template_libname=workinglib, direction='top'))
    ibcap = laygen.relplace(name="I" + objectname_pfix + 'IBCAP', templatename=bcap_name,
                    gridname=pg, refinstname=None, xy=bcap_origin, template_libname=workinglib)

    # output routing
    for i in range(num_hori):
        for j in range(num_vert):
            if i == num_hori-1 and j == num_vert-1: # VREF_SF_BIAS routing with thick wire
                rv1, rh1 = laygen.route_vh(laygen.layers['metal'][5], laygen.layers['metal'][6],
                                           xy0=laygen.get_inst_pin_xy(irdac[j].name, 'out', rg_m5m6_basic_thick,
                                                                      index=np.array([i, 0]))[0] - np.array([j, -1]),
                                           xy1=np.array([pin_origin_x, pin_origin_y0_thick]),
                                           gridname0=rg_m5m6_basic_thick)
                laygen.via(None, xy=laygen.get_inst_pin_xy(irdac[j].name, 'out', rg_m4m5, index=np.array([i, 0]))[
                                        0] - np.array([j, 0]), gridname=rg_m4m5)
                laygen.boundary_pin_from_rect(rh1, rg_m5m6_thick, 'out<' + str(num_hori * j + i) + '>',
                                              laygen.layers['pin'][6], size=4, direction='right')
            elif num_hori * j + i == num_slices*2: # SF_BIAS routing with thick wire
                rv1, rh1 = laygen.route_vh(laygen.layers['metal'][5], laygen.layers['metal'][6],
                                           xy0=laygen.get_inst_pin_xy(irdac[j].name, 'out', rg_m5m6_basic_thick,
                                                                      index=np.array([i, 0]))[0] - np.array([j, -1]),
                                           xy1=np.array([pin_origin_x, pin_origin_y1_thick]),
                                           gridname0=rg_m5m6_basic_thick)
                laygen.via(None, xy=laygen.get_inst_pin_xy(irdac[j].name, 'out', rg_m4m5, index=np.array([i, 0]))[
                                        0] - np.array([j, 0]), gridname=rg_m4m5)
                laygen.boundary_pin_from_rect(rh1, rg_m5m6_thick, 'out<' + str(num_hori * j + i) + '>',
                                              laygen.layers['pin'][6], size=4, direction='right')
            else:
                rv0, rh0 = laygen.route_vh(laygen.layers['metal'][5], laygen.layers['metal'][6],
                                           xy0=laygen.get_inst_pin_xy(irdac[j].name, 'out', rg_m5m6, index=np.array([i,0]))[0]-np.array([j,0]),
                                           xy1=np.array([pin_origin_x, pin_origin_y+4+num_hori*j+i]), gridname0=rg_m5m6)
                laygen.via(None, xy=laygen.get_inst_pin_xy(irdac[j].name, 'out', rg_m4m5, index=np.array([i,0]))[0]-np.array([j,0]), gridname=rg_m4m5)
                laygen.boundary_pin_from_rect(rh0, rg_m5m6, 'out<'+str(num_hori*j+i)+'>', laygen.layers['pin'][6], size=4, direction='right')
    pin_origin_y2_thick=laygen.grids.get_absgrid_y(rg_m5m6_thick, laygen.grids.get_phygrid_y(rg_m5m6, pin_origin_y+4+num_hori*num_vert))
    # pin_origin_y2_thick=laygen.get_rect_xy(rh0.name, rg_m5m6_thick)[1][1]

    # input routing
    # tgate_x = laygen.get_template_xy(tgate_name, gridname=rg_m4m5, libname=logictemplib)[0]
    for i in range(num_hori):
        for j in range(num_vert):
            x_ref = laygen.get_inst_pin_xy(irdac[j].name, 'SEL<0>', rg_m4m5, index=np.array([i, 0]))[1][0]
            for k in range(num_bits):
                rh0, rv0 = laygen.route_hv(laygen.layers['metal'][4], laygen.layers['metal'][5],
                                       xy0=laygen.get_inst_pin_xy(irdac[j].name, 'SEL<'+str(k)+'>', rg_m4m5, index=np.array([i, 0]))[0],
                                       xy1=np.array([x_ref + 12 + num_bits * j + k, 0]), gridname0=rg_m4m5)
                laygen.boundary_pin_from_rect(rv0, rg_m4m5, 'SEL<'+str((num_hori*j+i)*num_bits+k)+'>', laygen.layers['pin'][5], size=4, direction='bottom')

    # m5 supply
    pdict_m5m6_thick = laygen.get_inst_pin_xy(None, None, rg_m5m6_thick)
    rvdd_m5=[]
    rvss_m5=[]
    for i in range(num_hori):
        for p in pdict_m5m6_thick[irdac[0].name]:
            if p.startswith('VDD'):
                r0=laygen.route(None, laygen.layers['metal'][5],
                                xy0=laygen.get_inst_pin_xy(irdac[0].name, p, rg_m5m6_thick, index=np.array([i,0]))[0],
                                xy1=laygen.get_inst_pin_xy(irdac[num_vert-1].name, p, rg_m5m6_thick, index=np.array([i,0]))[1],
                                gridname0=rg_m5m6_thick)
                rvdd_m5.append(r0)
        for p in pdict_m5m6_thick[irdac[0].name]:
            if p.startswith('VSS'):
                r0=laygen.route(None, laygen.layers['metal'][5],
                                xy0=laygen.get_inst_pin_xy(irdac[0].name, p, rg_m5m6_thick, index=np.array([i,0]))[0],
                                xy1=laygen.get_inst_pin_xy(irdac[num_vert-1].name, p, rg_m5m6_thick, index=np.array([i,0]))[1],
                                gridname0=rg_m5m6_thick)
                rvss_m5.append(r0)

    # m6
    # print(pin_origin_y0_thick, pin_origin_y1_thick, pin_origin_y2_thick)
    # input_rails_rect = [rvdd_m5, rvss_m5]
    # rvdd_m6_0, rvss_m6_0 = laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_M6_0_',
    #                                                                        layer=laygen.layers['pin'][6],
    #                                                                        gridname=rg_m5m6_thick,
    #                                                                        netnames=['VDD', 'VSS'],
    #                                                                        direction='x',
    #                                                                        input_rails_rect=input_rails_rect,
    #                                                                        generate_pin=True,
    #                                                                        overwrite_start_coord=None,
    #                                                                        overwrite_end_coord=pin_origin_x_thick-2,
    #                                                                        offset_start_index=0,
    #                                                                        overwrite_end_index=pin_origin_y0_thick-2)
    # rvdd_m6_1, rvss_m6_1 = laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_M6_1_',
    #                                                                        layer=laygen.layers['pin'][6],
    #                                                                        gridname=rg_m5m6_thick,
    #                                                                        netnames=['VDD', 'VSS'],
    #                                                                        direction='x',
    #                                                                        input_rails_rect=input_rails_rect,
    #                                                                        generate_pin=True,
    #                                                                        overwrite_start_coord=None,
    #                                                                        overwrite_end_coord=pin_origin_x_thick-2,
    #                                                                        overwrite_start_index=pin_origin_y0_thick+2,
    #                                                                        overwrite_end_index=pin_origin_y1_thick-2)
    # rvdd_m6_2, rvss_m6_2 = laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_M6_2_',
    #                                                                        layer=laygen.layers['pin'][6],
    #                                                                        gridname=rg_m5m6_thick,
    #                                                                        netnames=['VDD', 'VSS'],
    #                                                                        direction='x',
    #                                                                        input_rails_rect=input_rails_rect,
    #                                                                        generate_pin=True,
    #                                                                        overwrite_start_coord=None,
    #                                                                        overwrite_end_coord=pin_origin_x_thick-2,
    #                                                                        overwrite_start_index=pin_origin_y2_thick+2,
    #                                                                        offset_end_index=0)

    # m6 (extract VDD/VSS grid from tisar and make power pins)
    tisar_name = 'tisaradc_body_core'
    tisar_libname = 'adc_sar_generated'
    rg_m5m6_thick_temp_tisar = 'route_M5_M6_thick_temp_tisar_VDD'
    # x_end = laygen.get_template_xy(r2r_name, gridname=rg_m5m6_thick_temp_tisar, libname=workinglib)[0] + \
    #         laygen.get_template_xy(bcap_name, gridname=rg_m5m6_thick_temp_tisar, libname=workinglib)[0]
    bnd = laygen.get_template(tisar_name, libname=tisar_libname).xy
    laygenhelper.generate_grids_from_template(laygen, gridname_input=rg_m5m6_thick,
                                              gridname_output=rg_m5m6_thick_temp_tisar,
                                              template_name=tisar_name, template_libname=tisar_libname,
                                              template_pin_prefix=['VDDSAR'], bnd=bnd, xy_grid_type='ygrid')

    laygen.grids.display(libname=None, gridname=rg_m5m6_thick_temp_tisar)
    input_rails_rect = [rvdd_m5]
    rvdd_m6 = laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_M6_',
                                                                layer=laygen.layers['pin'][6],
                                                                gridname=rg_m5m6_thick_temp_tisar, netnames=['VDD'],
                                                                direction='x',
                                                                input_rails_rect=input_rails_rect, generate_pin=True,
                                                                overwrite_start_coord=None, overwrite_end_coord=pin_origin_x_thick,
                                                                offset_start_index=0, offset_end_index=0)
    rg_m5m6_thick_temp_tisar = 'route_M5_M6_thick_temp_tisar_VSS'
    laygenhelper.generate_grids_from_template(laygen, gridname_input=rg_m5m6_thick,
                                              gridname_output=rg_m5m6_thick_temp_tisar,
                                              template_name=tisar_name, template_libname=tisar_libname,
                                              template_pin_prefix=['VSS'], xy_grid_type='ygrid')
    laygen.grids.display(libname=None, gridname=rg_m5m6_thick_temp_tisar)
    input_rails_rect = [rvss_m5]
    rvss_m6 = laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_M6_',
                                                                layer=laygen.layers['pin'][6],
                                                                gridname=rg_m5m6_thick_temp_tisar, netnames=['VSS'],
                                                                direction='x',
                                                                input_rails_rect=input_rails_rect, generate_pin=True,
                                                                overwrite_start_coord=None, overwrite_end_coord=pin_origin_x_thick,
                                                                offset_start_index=0, offset_end_index=0)
def generate_r2r_dac_bcap_array(laygen,
                                objectname_pfix,
                                templib_logic,
                                placement_grid,
                                routing_grid_m4m5,
                                routing_grid_m5m6,
                                rg_m3m4_basic_thick,
                                rg_m5m6_thick,
                                m,
                                num_bits,
                                num_hori,
                                num_vert,
                                origin=np.array([0, 0])):
    """generate r2rdac """
    r2r_name = 'r2r_dac_bcap_vref'
    sar_name = 'sar_wsamp'
    ret_name = 'adc_retimer'
    tgate_name = 'tgate_' + str(m) + 'x'
    pg = placement_grid

    rg_m4m5 = routing_grid_m4m5
    rg_m5m6 = routing_grid_m5m6
    # rg_m4m5 = routing_grid_m4m5
    # rg_m4m5_basic_thick = routing_grid_m4m5_basic_thick
    # rg_m4m5_thick = routing_grid_m4m5_thick
    # rg_m5m6 = routing_grid_m5m6
    # rg_m5m6_thick = routing_grid_m5m6_thick
    # rg_m5m6_thick_basic = routing_grid_m5m6_thick_basic
    # rg_m6m7_thick = routing_grid_m6m7_thick

    # Calculate reference coordinate
    x1_phy = laygen.get_template_xy(
        name=r2r_name, gridname=None, libname=workinglib)[0] * num_hori
    pin_origin_x = laygen.grids.get_absgrid_x(rg_m5m6_thick_basic, x1_phy)
    y1_phy = origin[1] + laygen.get_template_xy(name=sar_name, gridname=None, libname=workinglib)[1] \
             + laygen.get_template_xy(name=ret_name, gridname=None, libname=workinglib)[1]
    pin_origin_y = laygen.grids.get_absgrid_y(rg_m5m6, y1_phy)
    pin_origin_y2_thick = origin[1] + \
                          laygen.get_template_pin_xy(sar_name, 'VREF<2>_M6_0', rg_m5m6_thick, libname=workinglib)[0][1] \
                          + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    pin_origin_y1_thick = origin[1] + \
                          laygen.get_template_pin_xy(sar_name, 'VREF<1>_M6_0', rg_m5m6_thick, libname=workinglib)[0][1] \
                          + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    pin_origin_y0_thick = origin[1] + \
                          laygen.get_template_pin_xy(sar_name, 'VREF<0>_M6_0', rg_m5m6_thick, libname=workinglib)[0][1] \
                          + laygen.get_template_xy(name=ret_name, gridname=rg_m5m6_thick, libname=workinglib)[1]
    # pin_origin_y0_thick = laygen.grids.get_absgrid_y(rg_m5m6_thick, y0_phy)

    # placement
    irdac = []
    for i in range(num_vert):
        if i == 0:
            irdac.append(
                laygen.relplace(name="I" + objectname_pfix + 'IBCAP' + str(i),
                                templatename=r2r_name,
                                gridname=pg,
                                refinstname=None,
                                xy=origin,
                                shape=[num_hori, 1],
                                template_libname=workinglib))
        else:
            irdac.append(
                laygen.relplace(name="I" + objectname_pfix + 'IBCAP' + str(i),
                                templatename=r2r_name,
                                gridname=pg,
                                refinstname=irdac[-1].name,
                                shape=[num_hori, 1],
                                template_libname=workinglib,
                                direction='top'))

    # output routing
    for k in range(4):
        rv2, rh2 = laygen.route_vh(
            laygen.layers['metal'][5],
            laygen.layers['metal'][6],
            xy0=laygen.get_inst_pin_xy(
                irdac[2].name, 'I', rg_m5m6_thick, index=np.array([0, 0]))[0] +
            np.array([2, 0]),
            xy1=np.array([pin_origin_x, pin_origin_y2_thick + k]),
            gridname0=rg_m5m6_thick)
        rv1, rh1 = laygen.route_vh(
            laygen.layers['metal'][5],
            laygen.layers['metal'][6],
            xy0=laygen.get_inst_pin_xy(
                irdac[1].name, 'I', rg_m5m6_thick, index=np.array([0, 0]))[0] +
            np.array([1, 0]),
            xy1=np.array([pin_origin_x, pin_origin_y1_thick + k]),
            gridname0=rg_m5m6_thick)
        rv0, rh0 = laygen.route_vh(
            laygen.layers['metal'][5],
            laygen.layers['metal'][6],
            xy0=laygen.get_inst_pin_xy(
                irdac[0].name, 'I', rg_m5m6_thick, index=np.array([0, 0]))[0] +
            np.array([0, 0]),
            xy1=np.array([pin_origin_x, pin_origin_y0_thick + k]),
            gridname0=rg_m5m6_thick)
        for j in range(num_vert):
            laygen.via(
                None,
                xy=laygen.get_inst_pin_xy(
                    irdac[j].name, 'I', rg_m4m5, index=np.array([0, 0]))[0] +
                np.array([j, 0]),
                gridname=rg_m4m5)
        laygen.boundary_pin_from_rect(rh2,
                                      rg_m5m6_thick,
                                      'VREF' + str(k) + '<' + str(2) + '>',
                                      laygen.layers['pin'][6],
                                      size=4,
                                      direction='right',
                                      netname='VREF<' + str(2) + '>')
        laygen.boundary_pin_from_rect(rh1,
                                      rg_m5m6_thick,
                                      'VREF' + str(k) + '<' + str(1) + '>',
                                      laygen.layers['pin'][6],
                                      size=4,
                                      direction='right',
                                      netname='VREF<' + str(1) + '>')
        laygen.boundary_pin_from_rect(rh0,
                                      rg_m5m6_thick,
                                      'VREF' + str(k) + '<' + str(0) + '>',
                                      laygen.layers['pin'][6],
                                      size=4,
                                      direction='right',
                                      netname='VREF<' + str(0) + '>')

    pin_origin_y2_thick = laygen.grids.get_absgrid_y(
        rg_m5m6_thick,
        laygen.grids.get_phygrid_y(rg_m5m6,
                                   pin_origin_y + 4 + num_hori * num_vert))
    # m5 supply
    pdict_m5m6_thick = laygen.get_inst_pin_xy(None, None, rg_m5m6_thick)
    rvdd_m5 = []
    rvss_m5 = []
    for i in range(num_hori):
        for p in pdict_m5m6_thick[irdac[0].name]:
            if p.startswith('VDD'):
                r0 = laygen.route(
                    None,
                    laygen.layers['metal'][5],
                    xy0=laygen.get_inst_pin_xy(irdac[0].name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[0],
                    xy1=laygen.get_inst_pin_xy(irdac[num_vert - 1].name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[1],
                    gridname0=rg_m5m6_thick)
                rvdd_m5.append(r0)
        for p in pdict_m5m6_thick[irdac[0].name]:
            if p.startswith('VSS'):
                r0 = laygen.route(
                    None,
                    laygen.layers['metal'][5],
                    xy0=laygen.get_inst_pin_xy(irdac[0].name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[0],
                    xy1=laygen.get_inst_pin_xy(irdac[num_vert - 1].name,
                                               p,
                                               rg_m5m6_thick,
                                               index=np.array([i, 0]))[1],
                    gridname0=rg_m5m6_thick)
                rvss_m5.append(r0)

    #m6 (extract VDD/VSS grid from tisar and make power pins)
    tisar_name = 'tisaradc_body_core'
    tisar_libname = 'adc_sar_generated'
    rg_m5m6_thick_temp_tisar = 'route_M5_M6_thick_temp_tisar_VDD'
    # bnd = laygen.get_template(tisar_name, libname=tisar_libname).xy
    laygenhelper.generate_grids_from_template(
        laygen,
        gridname_input=rg_m5m6_thick,
        gridname_output=rg_m5m6_thick_temp_tisar,
        template_name=tisar_name,
        template_libname=tisar_libname,
        template_pin_prefix=['VDD'],
        bnd=None,
        xy_grid_type='ygrid')
    input_rails_rect = [rvdd_m5]
    rvdd_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick_temp_tisar,
        netnames=['VDD'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    rg_m5m6_thick_temp_tisar = 'route_M5_M6_thick_temp_tisar_VSS'
    laygenhelper.generate_grids_from_template(
        laygen,
        gridname_input=rg_m5m6_thick,
        gridname_output=rg_m5m6_thick_temp_tisar,
        template_name=tisar_name,
        template_libname=tisar_libname,
        template_pin_prefix=['VSS'],
        xy_grid_type='ygrid')
    input_rails_rect = [rvss_m5]
    rvss_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_',
        layer=laygen.layers['pin'][6],
        gridname=rg_m5m6_thick_temp_tisar,
        netnames=['VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=True,
        overwrite_start_coord=None,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
Exemple #13
0
def generate_tisaradc_body(laygen, objectname_pfix, libname, tisar_core_name, tisar_space_name, tisar_space2_name, 
                           placement_grid,
                           routing_grid_m3m4, routing_grid_m4m5, routing_grid_m4m5_basic_thick, routing_grid_m5m6, routing_grid_m5m6_thick, 
                           routing_grid_m5m6_thick_basic, routing_grid_m6m7_thick, routing_grid_m7m8_thick, 
                           num_bits=9, num_slices=8, origin=np.array([0, 0])):
    """generate sar array """
    pg = placement_grid

    rg_m3m4 = routing_grid_m3m4
    rg_m4m5 = routing_grid_m4m5
    rg_m4m5 = routing_grid_m4m5
    rg_m4m5_basic_thick = routing_grid_m4m5_basic_thick
    rg_m5m6 = routing_grid_m5m6
    rg_m5m6_thick = routing_grid_m5m6_thick
    rg_m5m6_thick_basic = routing_grid_m5m6_thick_basic
    rg_m6m7_thick = routing_grid_m6m7_thick
    rg_m7m8_thick = routing_grid_m7m8_thick

    # placement
    ispace20 = laygen.place(name="I" + objectname_pfix + 'SP20', templatename=tisar_space2_name,
                      gridname=pg, xy=origin, template_libname=libname)
    space20_template = laygen.templates.get_template(tisar_space2_name, libname)
    space20_pins=space20_template.pins
    space20_xy=ispace20.xy
    space0_origin = laygen.get_template_size(ispace20.cellname, gridname=pg, libname=workinglib)*np.array([1, 0])
    ispace0 = laygen.place(name="I" + objectname_pfix + 'SP0', templatename=tisar_space_name,
                      gridname=pg, xy=space0_origin, template_libname=libname)
    space_template = laygen.templates.get_template(tisar_space_name, libname)
    space_pins=space_template.pins
    space0_xy=ispace0.xy
    sar_origin = space0_origin + laygen.get_template_size(ispace0.cellname, gridname=pg, libname=workinglib)*np.array([1, 0])
    isar = laygen.place(name="I" + objectname_pfix + 'SAR0', templatename=tisar_core_name,
                      gridname=pg, xy=sar_origin, template_libname=libname)
    sar_template = laygen.templates.get_template(tisar_core_name, libname)
    sar_pins=sar_template.pins
    sar_xy=isar.xy
    space1_origin = sar_origin + laygen.get_template_size(isar.cellname, gridname=pg, libname=workinglib)*np.array([1, 0])
    ispace1 = laygen.place(name="I" + objectname_pfix + 'SP1', templatename=tisar_space_name,
                      gridname=pg, xy=space1_origin, template_libname=libname)
    space1_xy=ispace1.xy
    space21_origin = space1_origin + laygen.get_template_size(ispace1.cellname, gridname=pg, libname=workinglib)*np.array([1, 0])
    ispace21 = laygen.place(name="I" + objectname_pfix + 'SP21', templatename=tisar_space2_name,
                      gridname=pg, xy=space21_origin, template_libname=libname)
    space21_xy=ispace21.xy

    #prboundary
    sar_size = laygen.templates.get_template(tisar_core_name, libname=libname).size
    space_size = laygen.templates.get_template(tisar_space_name, libname=libname).size
    space2_size = laygen.templates.get_template(tisar_space2_name, libname=libname).size

    #VDD/VSS/VREF integration 
    rvddclkd=[]
    rvddsamp=[]
    rvddsar=[]
    rvddsar_upper=[]
    rvref0=[]
    rvref1=[]
    rvref2=[]
    rvssclkd=[]
    rvsssamp=[]
    rvsssar=[]
    rvsssar_upper=[]
    vddsampcnt=0
    vddsarcnt=0
    y_vddsar_max=0
    y_vddsamp_min=500000
    y_vddsamp_max=0
    y_vddclkd_min=500000
    y_vref0=sar_pins['VREF0<0>']['xy'][0][1]
    #categorize vdd pins and fiugre out htresholds
    for pn, p in sar_pins.items():
        if pn.startswith('VDDCLKD'):
            pxy=np.array([[0, space0_xy[1]+sar_pins[pn]['xy'][0][1]], 
                          [space_size[0]*4+sar_size[0], space1_xy[1]+sar_pins[pn]['xy'][1][1]]])
            rvddclkd.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pxy[1][1]<y_vddclkd_min:
                y_vddclkd_min=pxy[1][1]
        if pn.startswith('VDDSAMP'):
            pxy=np.array([[0, space0_xy[1]+sar_pins[pn]['xy'][0][1]], 
                          [space_size[0]*4+sar_size[0], space1_xy[1]+sar_pins[pn]['xy'][1][1]]])
            rvddsamp.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pxy[1][1]<y_vddsamp_min:
                y_vddsamp_min=pxy[1][1]
            if pxy[1][1]>y_vddsamp_max:
                y_vddsamp_max=pxy[1][1]
        if pn.startswith('VDDSAR'):
            pxy=np.array([[0, space0_xy[1]+sar_pins[pn]['xy'][0][1]], 
                          [space_size[0]*4+sar_size[0], space1_xy[1]+sar_pins[pn]['xy'][1][1]]])
            if pxy[0][1] > y_vref0:
                rvddsar_upper.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            else:
                rvddsar.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pxy[1][1]>y_vddsar_max:
                y_vddsar_max=pxy[1][1]
        if pn.startswith('VREF'):
            pxy=np.array([[0, space0_xy[1]+sar_pins[pn]['xy'][0][1]], 
                          [space_size[0]*4+sar_size[0], space1_xy[1]+sar_pins[pn]['xy'][1][1]]])
            if pn.endswith('<0>'):
                rvref0.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pn.endswith('<1>'):
                rvref1.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            if pn.endswith('<2>'):
                rvref2.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
    y_vss_th=0.5*(y_vddsar_max+y_vddsamp_min) #find out threshold
    y_vss_th2=0.5*(y_vddsamp_max+y_vddclkd_min) #find out threshold
    for pn, p in sar_pins.items():
        if pn.startswith('VSS'):
            pxy=np.array([[0, space0_xy[1]+sar_pins[pn]['xy'][0][1]], 
                          [space_size[0]*4+sar_size[0], space1_xy[1]+sar_pins[pn]['xy'][1][1]]])
            if pxy[0][1] > y_vss_th2:
                rvssclkd.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            elif pxy[0][1] > y_vss_th:
                rvsssamp.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            elif pxy[0][1] > y_vref0:
                rvsssar_upper.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
            else:
                rvsssar.append(laygen.add_rect(None, pxy, laygen.layers['metal'][6]))
    #M7 rails
    rg_route='route_M6_M7_thick_temp_pwr'
    laygenhelper.generate_grids_from_inst(laygen, gridname_input=rg_m6m7_thick, gridname_output=rg_route,
                                          instname=[isar.name],
                                          inst_pin_prefix=['VDD', 'VSS', 'VREF'], xy_grid_type='ygrid')
    '''
    #M7 rails-clkd
    input_rails_rect = [rvddclkd, rvssclkd]
    rvddclkd_m7_pre, rvssclkd_m7_pre= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_CLKD_M7_', 
                layer=laygen.layers['metal'][7], gridname=rg_route, netnames=['VDDCLKD', 'VSS'], direction='y', 
                input_rails_rect=input_rails_rect, generate_pin=False, overwrite_start_coord=None, offset_end_coord=None,
                offset_start_index=2, offset_end_index=-2)
    '''
    #M7 rails-samp
    input_rails_rect = [rvddsamp, rvsssamp]
    rvddsamp_m7_pre, rvsssamp_m7_pre= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_SAMP_M7_', 
                layer=laygen.layers['metal'][7], gridname=rg_route, netnames=['VDDSAMP', 'VSS'], direction='y', 
                input_rails_rect=input_rails_rect, generate_pin=False, overwrite_start_coord=None, offset_end_coord=None,
                offset_start_index=2, offset_end_index=-2)
    #M7 rails-sar_lower
    input_rails_rect = [rvddsar, rvsssar]
    rvddsar_m7, rvsssar_m7= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_SAR_M7_', 
                layer=laygen.layers['metal'][7], gridname=rg_route, netnames=['VDDSAR', 'VSS'], direction='y', 
                input_rails_rect=input_rails_rect, generate_pin=False, overwrite_start_coord=None, overwrite_end_coord=None,
                offset_start_index=2, offset_end_index=-2)
    #M7 rails-sar_upper(+vref)
    input_rails_rect = [rvddsar_upper, rvsssar_upper, rvref0, rvsssar_upper, rvref1, rvsssar_upper, rvref2, rvsssar_upper]
    rvddsar_m7_upper, rvsssar0_m7, rvref0_m7_pre, rvsssar1_m7, rvref1_m7_pre, rvsssar2_m7, rvref2_m7_pre, rvsssar3_m7= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_SAR_UPPER_M7_', 
                layer=laygen.layers['metal'][7], gridname=rg_route, netnames=['VDDSAR', 'VSS', 'VREF<0>', 'VSS', 'VREF<1>', 'VSS', 'VREF<2>', 'VSS'], direction='y', 
                input_rails_rect=input_rails_rect, generate_pin=False, overwrite_start_coord=None, overwrite_end_coord=None,
                offset_start_index=2, offset_end_index=-2)
    rvsssar_m7_upper_pre=rvsssar0_m7+rvsssar1_m7+rvsssar2_m7+rvsssar3_m7
    #extend m7 rails for clkd and samp and vref, rvsssar_m7_upper
    #rvddclkd_m7=[]
    #rvssclkd_m7=[]
    rvddsamp_m7=[]
    rvsssamp_m7=[]
    rvref0_m7=[]
    rvref1_m7=[]
    rvref2_m7=[]
    rvsssar_m7_upper=[]
    '''
    for r in rvddclkd_m7_pre:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        rxy[1][1]+=12
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvddclkd_m7.append(r2)
    for r in rvssclkd_m7_pre:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        rxy[1][1]+=12
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvssclkd_m7.append(r2)
    '''
    for r in rvddsamp_m7_pre:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        #rxy[0][1]-=1
        rxy[1][1]+=24
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvddsamp_m7.append(r2)
    for r in rvsssamp_m7_pre:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        #rxy[0][1]-=1
        rxy[1][1]+=24
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvsssamp_m7.append(r2)
        #extend upper vss routes in the space area down to zero (for vss short)
        if r.xy[0][0]<sar_xy[0]:
            laygen.route(None, laygen.layers['metal'][7], xy0=[rxy[0][0], 0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        if r.xy[0][0]>sar_xy[0]+sar_template.width:
            laygen.route(None, laygen.layers['metal'][7], xy0=[rxy[0][0], 0], xy1=rxy[1], gridname0=rg_m6m7_thick)
    for r in rvref0_m7_pre:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        rxy[0][1]-=24
        #rxy[1][1]+=24
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvref0_m7.append(r2)
    for r in rvref1_m7_pre:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        rxy[0][1]-=24
        #rxy[1][1]+=24
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvref1_m7.append(r2)
    for r in rvref2_m7_pre:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        rxy[0][1]-=24
        #rxy[1][1]+=24
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvref2_m7.append(r2)
    for r in rvsssar_m7_upper_pre:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        rxy[0][1]-=24
        #rxy[1][1]+=24
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
        rvsssar_m7_upper.append(r2)
    #connect VDDSAR/VSS in sar region
    for r in rvddsar_m7_upper:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        rxy[0][1]=1
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
    for r in rvsssar_m7_upper:
        rxy=laygen.get_rect_xy(r.name, gridname=rg_m6m7_thick, sort=True)
        rxy[0][1]=1
        r2=laygen.route(None, laygen.layers['metal'][7], xy0=rxy[0], xy1=rxy[1], gridname0=rg_m6m7_thick)
  
    #connect VSS between sar/samp/clkd in space region
    '''
    for r in rvssclkd_m7:
        if r.xy[1][0] < sar_xy[0]:
            laygen.add_rect(None, np.array([[r.xy[0][0],rvsssar_m7[0].xy[0][1]], r.xy[1]]), laygen.layers['metal'][7])
        if r.xy[0][0] > space1_xy[0]:
            laygen.add_rect(None, np.array([[r.xy[0][0],rvsssar_m7[0].xy[0][1]], r.xy[1]]), laygen.layers['metal'][7])
    '''
    #create VSS pins for connection to core
    for i, r in enumerate(rvsssar_m7):
        pxy=np.array([[r.xy[0][0],0], r.xy[1]])
        laygen.add_rect(None, pxy, laygen.layers['metal'][7])
        laygen.add_pin('VSS_SAR_M7_'+str(i), 'VSS', pxy, laygen.layers['pin'][7])
    '''
    #connect VDDSAMP between samp/clkd in space region
    for r in rvddclkd_m7:
        if r.xy[1][0] < sar_xy[0]:
            laygen.add_rect(None, np.array([[r.xy[0][0],rvddsamp_m7[0].xy[0][1]], r.xy[1]]), laygen.layers['metal'][7])
        if r.xy[0][0] > space1_xy[0]:
            laygen.add_rect(None, np.array([[r.xy[0][0],rvddsamp_m7[0].xy[0][1]], r.xy[1]]), laygen.layers['metal'][7])
    #M8 routes
    #input_rails_rect = [rvddclkd_m7, rvssclkd_m7]
    #rvddclkd_m8, rvssclkd_m8= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_CLKD_M8_', 
    #            layer=laygen.layers['pin'][8], gridname=rg_m7m8_thick, netnames=['VDDSAMP', 'VSS'], direction='x', 
    #            input_rails_rect=input_rails_rect, generate_pin=True, overwrite_start_coord=None, overwrite_end_coord=None,
    #            offset_start_index=1, offset_end_index=0)
    input_rails_rect = [rvssclkd_m7, rvddclkd_m7]
    rvssclkd_m8, rvddclkd_m8= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_CLKD_M8_', 
                layer=laygen.layers['pin'][8], gridname=rg_m7m8_thick, netnames=['VSS', 'VDDSAMP'], direction='x', 
                input_rails_rect=input_rails_rect, generate_pin=True, overwrite_start_coord=None, overwrite_end_coord=None,
                offset_start_index=1, offset_end_index=0)
    '''
    #M8 routes
    input_rails_rect = [rvsssamp_m7, rvddsamp_m7]
    rvsssamp_m8, rvddsamp_m8= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_SAMP_M8_', 
                layer=laygen.layers['pin'][8], gridname=rg_m7m8_thick, netnames=['VSS', 'VDDSAMP'], direction='x', 
                input_rails_rect=input_rails_rect, generate_pin=True, overwrite_start_coord=None, overwrite_end_coord=None,
                offset_start_index=1, offset_end_index=0)
    input_rails_rect = [rvddsar_m7, rvsssar_m7]
    rvddsar_m8, rvsssar_m8= laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_SAR_M8_', 
                layer=laygen.layers['pin'][8], gridname=rg_m7m8_thick, netnames=['VDDSAR', 'VSS'], direction='x', 
                input_rails_rect=input_rails_rect, generate_pin=True, overwrite_start_coord=None, overwrite_end_coord=None,
                offset_start_index=0, offset_end_index=0)
    input_rails_rect = [rvref0_m7, rvsssar_m7_upper, rvref1_m7, rvsssar_m7_upper, rvref2_m7, rvsssar_m7_upper]
    laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_VREF_M8_', 
                layer=laygen.layers['metal'][8], gridname=rg_m7m8_thick, netnames=['VREF<0>', 'VSS', 'VREF<1>', 'VSS', 'VREF<2>', 'VSS'], direction='x', 
                input_rails_rect=input_rails_rect, generate_pin=False, overwrite_start_coord=None, overwrite_end_coord=None,
                offset_start_index=1, offset_end_index=0)

    #osp/osm route
    pdict_os_m4m5 = laygen.get_inst_pin_coord(None, None, rg_m4m5_basic_thick)
    rosp_m5=[]
    rosm_m5=[]
    for i in range(num_slices):
        rh0, rv0 = laygen.route_hv(laygen.layers['metal'][4], laygen.layers['metal'][5], 
                        pdict_os_m4m5[isar.name]['OSP'+str(i)][0], pdict_os_m4m5[isar.name]['OSP'+str(i)][0]+np.array([-i-2, -10]), gridname=rg_m4m5_basic_thick)
        rosp_m5.append(rv0)
        rh0, rv0 = laygen.route_hv(laygen.layers['metal'][4], laygen.layers['metal'][5], 
                        pdict_os_m4m5[isar.name]['OSM'+str(i)][0], pdict_os_m4m5[isar.name]['OSM'+str(i)][0]+np.array([-num_slices-i-2, -10]), gridname=rg_m4m5_basic_thick)
        rosm_m5.append(rv0)
    pdict_os_m5m6 = laygen.get_inst_pin_coord(None, None, rg_m5m6_thick)
    x0=pdict_os_m5m6[isar.name]['VREF0<0>'][0][0]-4*num_slices
    y0=pdict_os_m5m6[isar.name]['VREF0<0>'][0][1]-8
    rosp_m6=[]
    rosm_m6=[]
    for i in range(num_slices):
        xy0=laygen.get_rect_xy(rosp_m5[i].name, gridname=rg_m5m6_thick, sort=True)[0]
        rv0, rh0 = laygen.route_vh(laygen.layers['metal'][5], laygen.layers['metal'][6], xy0, np.array([x0, y0-2*i]), gridname=rg_m5m6_thick)
        laygen.create_boundary_pin_form_rect(rh0, rg_m5m6_thick, 'OSP'+str(i), laygen.layers['pin'][6], size=6, direction='left')
        rosp_m6.append(rh0)
        xy0=laygen.get_rect_xy(rosm_m5[i].name, gridname=rg_m5m6_thick, sort=True)[0]
        rv0, rh0 = laygen.route_vh(laygen.layers['metal'][5], laygen.layers['metal'][6], xy0, np.array([x0, y0-2*i-1]), gridname=rg_m5m6_thick)
        laygen.create_boundary_pin_form_rect(rh0, rg_m5m6_thick, 'OSM'+str(i), laygen.layers['pin'][6], size=6, direction='left')
        rosm_m6.append(rh0)
    
            
    #other pins - duplicate
    pin_prefix_list=['INP', 'INM', 'VREF', 'ASCLKD', 'EXTSEL_CLK', 'ADCOUT', 'CLKOUT_DES', 'CLKBOUT_NC', 'CLKCAL', 'RSTP', 'RSTN', 'CLKIP', 'CLKIN']
    for pn, p in sar_pins.items():
        for pfix in pin_prefix_list:
            if pn.startswith(pfix):
                laygen.add_pin(pn, sar_pins[pn]['netname'], sar_xy+sar_pins[pn]['xy'], sar_pins[pn]['layer'])
def generate_sarabe_dualdelay(laygen,
                              objectname_pfix,
                              workinglib,
                              placement_grid,
                              routing_grid_m2m3,
                              routing_grid_m3m4_thick,
                              routing_grid_m4m5_thick,
                              routing_grid_m5m6_thick,
                              routing_grid_m4m5,
                              num_bits=9,
                              origin=np.array([0, 0])):
    """generate sar backend """
    pg = placement_grid

    rg_m2m3 = routing_grid_m2m3
    rg_m3m4_thick = routing_grid_m3m4_thick
    rg_m4m5_thick = routing_grid_m4m5_thick
    rg_m5m6_thick = routing_grid_m5m6_thick
    rg_m4m5 = routing_grid_m4m5

    #sarfsm_name = 'sarfsm_'+str(num_bits)+'b'
    sarfsm_name = 'sarfsm'  #_'+str(num_bits)+'b'
    #sarlogic_name = 'sarlogic_wret_v2_array_'+str(num_bits)+'b'
    sarlogic_name = 'sarlogic_wret_v2_array_bb_pe'  #_'+str(num_bits)+'b'
    sarclkgen_name = 'sarclkgen_static'
    #sarret_name = 'sarret_wckbuf_'+str(num_bits)+'b'
    sarret_name = 'sarret_wckbuf'  #_'+str(num_bits)+'b'
    #space_name = 'space_dcap_nmos'
    space_name = 'space'

    xy0 = laygen.get_template_size(name=space_name,
                                   gridname=pg,
                                   libname=workinglib)
    xsp = xy0[0]
    ysp = xy0[1]

    # placement
    core_origin = origin + laygen.get_template_size('boundary_bottomleft', pg)
    isp = []
    devname_bnd_left = []
    devname_bnd_right = []
    transform_bnd_left = []
    transform_bnd_right = []
    #additional space for routing area
    isp.append(
        laygen.place(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                     templatename=space_name,
                     gridname=pg,
                     xy=core_origin,
                     transform='R0',
                     template_libname=workinglib))
    refi = isp[-1].name
    isp.append(
        laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                        templatename=space_name,
                        gridname=pg,
                        refinstname=refi,
                        direction='top',
                        transform='MX',
                        template_libname=workinglib))
    refi = isp[-1].name
    devname_bnd_left += [
        'nmos4_fast_left', 'pmos4_fast_left', 'pmos4_fast_left',
        'nmos4_fast_left'
    ]
    devname_bnd_right += [
        'nmos4_fast_right', 'pmos4_fast_right', 'pmos4_fast_right',
        'nmos4_fast_right'
    ]
    transform_bnd_left += ['R0', 'MX', 'R0', 'MX']
    transform_bnd_right += ['R0', 'MX', 'R0', 'MX']
    #ret
    iret = laygen.relplace(name="I" + objectname_pfix + 'RET0',
                           templatename=sarret_name,
                           gridname=pg,
                           refinstname=refi,
                           direction='top',
                           template_libname=workinglib)
    refi = iret.name
    yret = int(
        laygen.get_template_size(
            name=sarret_name, gridname=pg, libname=workinglib)[1] / ysp)
    for i in range(yret):  #boundary cells
        if i % 2 == 0:
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
    # space insertion if number of rows is odd
    if not yret % 2 == 0:
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='MX',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']
    #fsm
    ifsm = laygen.relplace(name="I" + objectname_pfix + 'FSM0',
                           templatename=sarfsm_name,
                           gridname=pg,
                           refinstname=refi,
                           direction='top',
                           template_libname=workinglib)
    refi = ifsm.name
    yfsm = int(
        laygen.get_template_size(
            name=sarfsm_name, gridname=pg, libname=workinglib)[1] / ysp)
    for i in range(yfsm):  #boundary cells
        if i % 2 == 0:
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
    # space insertion if number of rows is odd
    if not yfsm % 2 == 0:
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='MX',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']
    # sarlogic
    isl = laygen.relplace(name="I" + objectname_pfix + 'SL0',
                          templatename=sarlogic_name,
                          gridname=pg,
                          refinstname=refi,
                          direction='top',
                          template_libname=workinglib)
    refi = isl.name
    ysl = int(
        laygen.get_template_size(
            name=sarlogic_name, gridname=pg, libname=workinglib)[1] / ysp)
    for i in range(ysl):  #boundary cells
        if i % 2 == 0:
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
    # space insertion 2 if number of rows is even, 1 if odd
    if ysl % 2 == 0:
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='R0',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
        devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='MX',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']
    else:
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='MX',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']
    #clkdelay & clkgen
    ickg = laygen.relplace(name="I" + objectname_pfix + 'CKG0',
                           templatename=sarclkgen_name,
                           gridname=pg,
                           refinstname=refi,
                           direction='top',
                           transform='R0',
                           template_libname=workinglib)
    refi = ickg.name
    yck = laygen.get_template_size(
        name=sarclkgen_name, gridname=pg, libname=workinglib)[1] / ysp
    for i in range(int(yck)):  #boundary cells
        if i % 2 == 0:
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
    # space insertion if number of rows is odd
    if not yck % 2 == 0:
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='MX',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']

    # boundaries
    m_bnd = int(xsp /
                laygen.get_template_size('boundary_bottom', gridname=pg)[0])
    [bnd_bottom, bnd_top, bnd_left, bnd_right] \
        = generate_boundary(laygen, objectname_pfix='BND0', placement_grid=pg,
                            devname_bottom=['boundary_bottomleft', 'boundary_bottom', 'boundary_bottomright'],
                            shape_bottom=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_top=['boundary_topleft', 'boundary_top', 'boundary_topright'],
                            shape_top=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_left=devname_bnd_left,
                            transform_left=transform_bnd_left,
                            devname_right=devname_bnd_right,
                            transform_right=transform_bnd_right,
                            origin=origin)
    #route
    #reference coordinates
    pdict_m3m4 = laygen.get_inst_pin_xy(None, None, rg_m3m4)
    pdict_m4m5 = laygen.get_inst_pin_xy(None, None, rg_m4m5)
    pdict_m5m6 = laygen.get_inst_pin_xy(None, None, rg_m5m6)
    x_right = laygen.get_inst_xy(name=ifsm.name, gridname=rg_m5m6)[0]\
             +laygen.get_template_size(name=ifsm.cellname, gridname=rg_m5m6, libname=workinglib)[0] - 1
    y_top = laygen.get_inst_xy(name=ickg.name, gridname=rg_m5m6)[1] - 1
    xysl = laygen.get_inst_xy(name=isl.name, gridname=rg_m5m6)
    xyfsm = laygen.get_inst_xy(name=ifsm.name, gridname=rg_m5m6)
    xyret = laygen.get_inst_xy(name=iret.name, gridname=rg_m5m6)

    # rst signal route
    x0 = pdict_m4m5[ickg.name]['EXTSEL_CLK'][0][0]
    # very important because it sets the timing margin
    # ckg to sl
    [rh0, rrst0, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['RST'][0],
                          pdict_m5m6[isl.name]['RST'][0],
                          pdict_m5m6[ickg.name]['RST'][0][0] + 2, rg_m4m5)
    [rh0, rrst1, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['RST'][0],
                          pdict_m5m6[isl.name]['RST'][0],
                          pdict_m5m6[ickg.name]['RST'][0][0] + 4, rg_m4m5)
    #[rh0, rrst2, rh1] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
    #                                       pdict_m5m6[ickg.name]['RST'][0],
    #                                       pdict_m5m6[isl.name]['RST'][0],
    #                                       pdict_m5m6[ickg.name]['RST'][0][0]+6, rg_m4m5)
    # ckg to fsm
    [rh0, rrst0, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['RST'][0],
                          pdict_m5m6[ifsm.name]['RST'][0],
                          pdict_m5m6[ickg.name]['RST'][0][0] + 2, rg_m4m5)
    [rh0, rrst1, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['RST'][0],
                          pdict_m5m6[ifsm.name]['RST'][0],
                          pdict_m5m6[ickg.name]['RST'][0][0] + 4, rg_m4m5)
    #[rh0, rrst2, rh1] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
    #                                       pdict_m5m6[ickg.name]['RST'][0],
    #                                       pdict_m5m6[ifsm.name]['RST'][0],
    #                                       pdict_m5m6[ickg.name]['RST'][0][0]+6, rg_m4m5)
    # ckg to ret
    [rh0, rrst0, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['RST'][0],
                          pdict_m5m6[iret.name]['CLK'][0],
                          pdict_m5m6[ickg.name]['RST'][0][0] + 2, rg_m4m5)
    [rh0, rrst1, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['RST'][0],
                          pdict_m5m6[iret.name]['CLK'][0],
                          pdict_m5m6[ickg.name]['RST'][0][0] + 4, rg_m4m5)
    #[rh0, rrst2, rh1] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
    #                                       pdict_m5m6[ickg.name]['RST'][0],
    #                                       pdict_m5m6[iret.name]['CLK'][0],
    #                                       pdict_m5m6[ickg.name]['RST'][0][0]+6, rg_m4m5)
    # rst output to final retimer
    rrstout0 = laygen.route(None,
                            laygen.layers['metal'][5],
                            xy0=pdict_m5m6[iret.name]['CLKO0'][0],
                            xy1=np.array(
                                [pdict_m5m6[iret.name]['CLKO0'][0][0], 0]),
                            gridname0=rg_m5m6)
    laygen.boundary_pin_from_rect(rrstout0,
                                  rg_m4m5,
                                  'RSTOUT0',
                                  laygen.layers['pin'][5],
                                  size=6,
                                  direction='bottom',
                                  netname='RSTOUT')
    rrstout1 = laygen.route(None,
                            laygen.layers['metal'][5],
                            xy0=pdict_m5m6[iret.name]['CLKO1'][0],
                            xy1=np.array(
                                [pdict_m5m6[iret.name]['CLKO1'][0][0], 0]),
                            gridname0=rg_m5m6)
    laygen.boundary_pin_from_rect(rrstout1,
                                  rg_m4m5,
                                  'RSTOUT1',
                                  laygen.layers['pin'][5],
                                  size=6,
                                  direction='bottom',
                                  netname='RSTOUT')

    # clk input
    laygen.boundary_pin_from_rect(rrst0,
                                  rg_m5m6,
                                  'RST0',
                                  laygen.layers['pin'][5],
                                  size=6,
                                  direction='top',
                                  netname='RST')
    laygen.boundary_pin_from_rect(rrst1,
                                  rg_m5m6,
                                  'RST1',
                                  laygen.layers['pin'][5],
                                  size=6,
                                  direction='top',
                                  netname='RST')
    #laygen.boundary_pin_from_rect(rrst2, rg_m5m6, 'RST2' , laygen.layers['pin'][5], size=6, direction='top', netname='RST')

    # sarclk signal route
    # ckgen to fsm
    rh0, rv0, rh1 = laygen.route_hvh(laygen.layers['metal'][4],
                                     laygen.layers['metal'][5],
                                     pdict_m4m5[ickg.name]['CLKO'][0],
                                     pdict_m4m5[ifsm.name]['CLK'][0],
                                     pdict_m4m5[ickg.name]['CLKO'][1][0] + 2,
                                     rg_m4m5)
    # ckgen to fsm #2 (to reduce route resistance)
    rh0, rv0, rh1 = laygen.route_hvh(laygen.layers['metal'][4],
                                     laygen.layers['metal'][5],
                                     pdict_m4m5[ickg.name]['CLKO'][0],
                                     pdict_m4m5[ifsm.name]['CLK'][0],
                                     pdict_m4m5[ickg.name]['CLKO'][1][0] + 4,
                                     rg_m4m5)
    ## ckgen to fsm
    #rh0, rv0, rh1 = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
    #                                 pdict_m4m5[ickg.name]['CLKO'][0], pdict_m4m5[ifsm.name]['CLK'][0],
    #                                 pdict_m4m5[isl.name]['RETO<'+str(num_bits-2)+'>'][1][0]+9-4-1-3, rg_m4m5)
    ## ckgen to fsm #2 (to reduce route resistance)
    #rh0, rv0, rh1 = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
    #                                 pdict_m4m5[ickg.name]['CLKO'][0], pdict_m4m5[ifsm.name]['CLK'][0],
    #                                 pdict_m4m5[isl.name]['RETO<'+str(num_bits-2)+'>'][1][0]+11-4-1-3, rg_m4m5)

    # ckgen to sl route
    # saopb/saomb
    [rh0, rsaop0, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['SAOP'][0],
                          pdict_m5m6[isl.name]['SAOP'][0],
                          pdict_m5m6[ickg.name]['SAOP'][0][0] + 0, rg_m4m5)
    [rh0, rsaom0, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['SAOM'][0],
                          pdict_m5m6[isl.name]['SAOM'][0],
                          pdict_m5m6[ickg.name]['SAOM'][0][0] + 1, rg_m4m5)
    #equalize vertical route for pin generation
    rsaop0_xy = laygen.get_rect_xy(rsaop0.name, rg_m4m5, sort=True)
    rsaom0_xy = laygen.get_rect_xy(rsaom0.name, rg_m4m5, sort=True)
    rsao_y0 = min((rsaop0_xy[0][1], rsaom0_xy[0][1]))
    rsao_y1 = max((rsaop0_xy[1][1], rsaom0_xy[1][1]))
    rsaop0 = laygen.route(None,
                          laygen.layers['metal'][5],
                          xy0=np.array([rsaop0_xy[0][0], rsao_y0]),
                          xy1=np.array([rsaop0_xy[1][0], rsao_y1]),
                          gridname0=rg_m4m5)
    rsaom0 = laygen.route(None,
                          laygen.layers['metal'][5],
                          xy0=np.array([rsaom0_xy[0][0], rsao_y0]),
                          xy1=np.array([rsaom0_xy[1][0], rsao_y1]),
                          gridname0=rg_m4m5)
    # fsm to sl route
    # sb
    for i in range(num_bits):
        [rv0, rh0, rv1
         ] = laygen.route_vhv(laygen.layers['metal'][5],
                              laygen.layers['metal'][6],
                              pdict_m5m6[ifsm.name]['SB<' + str(i) + '>'][0],
                              pdict_m5m6[isl.name]['SB<' + str(i) + '>'][0],
                              xysl[1] - i - 1, rg_m5m6)
    # zp/zm/zmid
    for i in range(num_bits):
        rzp0 = laygen.route(None,
                            laygen.layers['metal'][5],
                            xy0=pdict_m4m5[isl.name]['ZP<' + str(i) + '>'][0],
                            xy1=pdict_m4m5[isl.name]['ZP<' + str(i) + '>'][0] +
                            np.array([0, 4]),
                            gridname0=rg_m5m6)
        laygen.boundary_pin_from_rect(rzp0,
                                      rg_m5m6,
                                      'ZP<' + str(i) + '>',
                                      laygen.layers['pin'][5],
                                      size=6,
                                      direction='top')
        rzm0 = laygen.route(None,
                            laygen.layers['metal'][5],
                            xy0=pdict_m4m5[isl.name]['ZM<' + str(i) + '>'][0],
                            xy1=pdict_m4m5[isl.name]['ZM<' + str(i) + '>'][0] +
                            np.array([0, 4]),
                            gridname0=rg_m5m6)
        laygen.boundary_pin_from_rect(rzm0,
                                      rg_m5m6,
                                      'ZM<' + str(i) + '>',
                                      laygen.layers['pin'][5],
                                      size=6,
                                      direction='top')
        rzmid0 = laygen.route(
            None,
            laygen.layers['metal'][5],
            xy0=pdict_m4m5[isl.name]['ZMID<' + str(i) + '>'][0],
            xy1=pdict_m4m5[isl.name]['ZMID<' + str(i) + '>'][0] +
            np.array([0, 4]),
            gridname0=rg_m5m6)
        laygen.boundary_pin_from_rect(rzmid0,
                                      rg_m5m6,
                                      'ZMID<' + str(i) + '>',
                                      laygen.layers['pin'][5],
                                      size=6,
                                      direction='top')
    # zmid to short
    #rh0, rv0 = laygen.route_hv(laygen.layers['metal'][4], laygen.layers['metal'][5],
    #                                 pdict_m4m5[ickg.name]['SHORTB'][0],
    #                                 pdict_m4m5[isl.name]['ZMID<5>'][0], rg_m4m5)
    rh0, rv0 = laygen.route_hv(
        laygen.layers['metal'][4], laygen.layers['metal'][5],
        pdict_m4m5[ickg.name]['SHORTB'][0],
        pdict_m4m5[isl.name]['ZMID<' + str(max(0, num_bits - 3)) + '>'][0],
        rg_m4m5)
    # ckdsel
    for i in range(2):
        rh0, rclkdsel0 = laygen.route_hv(
            laygen.layers['metal'][4], laygen.layers['metal'][5],
            pdict_m4m5[ickg.name]['SEL<' + str(i) + '>'][0],
            np.array([
                pdict_m4m5[ickg.name]['SEL<' + str(i) + '>'][1][0] + 1 + i +
                2 + 2 + 1, 0
            ]), rg_m4m5)
        laygen.boundary_pin_from_rect(rclkdsel0,
                                      rg_m4m5,
                                      'CKDSEL0<' + str(i) + '>',
                                      laygen.layers['pin'][5],
                                      size=6,
                                      direction='bottom')
    rh0, rclkdsel1 = laygen.route_hv(
        laygen.layers['metal'][4], laygen.layers['metal'][5],
        pdict_m4m5[ickg.name]['SEL<2>'][0],
        np.array(
            [pdict_m4m5[ickg.name]['SEL<2>'][1][0] + 1 + 3 + 2 + 2 + 1,
             0]), rg_m4m5)
    laygen.boundary_pin_from_rect(rclkdsel1,
                                  rg_m4m5,
                                  'CKDSEL1<0>',
                                  laygen.layers['pin'][5],
                                  size=6,
                                  direction='bottom')
    #ckdsel dummy
    xy0 = laygen.get_rect_xy(name=rclkdsel0.name, gridname=rg_m4m5, sort=True)
    rclkdsel1 = laygen.route(None,
                             laygen.layers['metal'][5],
                             xy0=xy0[0] + np.array([3, 0]),
                             xy1=xy0[0] + np.array([3, 4]),
                             gridname0=rg_m4m5)
    laygen.boundary_pin_from_rect(rclkdsel1,
                                  rg_m4m5,
                                  'CKDSEL1<1>',
                                  laygen.layers['pin'][5],
                                  size=6,
                                  direction='bottom')

    # SAOP/SAOM
    laygen.boundary_pin_from_rect(rsaop0,
                                  rg_m4m5,
                                  'SAOP',
                                  laygen.layers['pin'][5],
                                  size=6,
                                  direction='top')
    laygen.boundary_pin_from_rect(rsaom0,
                                  rg_m4m5,
                                  'SAOM',
                                  laygen.layers['pin'][5],
                                  size=6,
                                  direction='top')
    # extclk, extsel_clk
    rh0, rextsel_clk0 = laygen.route_hv(laygen.layers['metal'][4],
                                        laygen.layers['metal'][5],
                                        pdict_m4m5[ickg.name]['EXTSEL_CLK'][0],
                                        np.array([x0 - 2, 0]), rg_m4m5)
    #np.array([x0+13+3, 0]), rg_m4m5)
    laygen.boundary_pin_from_rect(rextsel_clk0,
                                  rg_m4m5,
                                  'EXTSEL_CLK',
                                  laygen.layers['pin'][5],
                                  size=6,
                                  direction='bottom')
    # fsm to ret (data)
    for i in range(num_bits):
        if i % 2 == 0:  #even channel
            [rv0, rh0, rv1] = laygen.route_vhv(
                laygen.layers['metal'][5], laygen.layers['metal'][6],
                pdict_m5m6[isl.name]['RETO<' + str(i) + '>'][0],
                pdict_m5m6[iret.name]['IN<' + str(i) + '>'][0],
                xyfsm[1] - int(i / 2) * 2 + 4, rg_m5m6)
        else:
            [rv0, rh0, rv1] = laygen.route_vhv(
                laygen.layers['metal'][5], laygen.layers['metal'][6],
                pdict_m5m6[isl.name]['RETO<' + str(i) + '>'][0],
                pdict_m5m6[iret.name]['IN<' + str(i) + '>'][0],
                xyfsm[1] - int(i / 2) * 2 - int(num_bits / 2) * 2 - 2, rg_m5m6)
    # adcout
    for i in range(num_bits):
        radco0 = laygen.route(
            None,
            laygen.layers['metal'][5],
            xy0=pdict_m4m5[iret.name]['OUT<' + str(i) + '>'][0],
            xy1=np.array(
                [pdict_m4m5[iret.name]['OUT<' + str(i) + '>'][0][0], 0]),
            gridname0=rg_m5m6)
        laygen.boundary_pin_from_rect(radco0,
                                      rg_m4m5,
                                      'ADCOUT<' + str(i) + '>',
                                      laygen.layers['pin'][5],
                                      size=6,
                                      direction='bottom')
    # probe outputs
    laygen.pin(name='PHI0',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['PHI0'],
               gridname=rg_m4m5)
    laygen.pin(name='UP',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['UP'],
               gridname=rg_m4m5)
    laygen.pin(name='DONE',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['DONE'],
               gridname=rg_m4m5)
    laygen.pin(name='SARCLK',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['CLKO'],
               gridname=rg_m4m5)
    laygen.pin(name='SARCLKB',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['CLKOB'],
               gridname=rg_m4m5)
    for i in range(num_bits):
        laygen.pin(name='SB<' + str(i) + '>',
                   layer=laygen.layers['pin'][5],
                   xy=pdict_m5m6[isl.name]['SB<' + str(i) + '>'],
                   gridname=rg_m5m6)
    # vdd/vss
    # m3
    rvddl_m3 = []
    rvssl_m3 = []
    rvddr_m3 = []
    rvssr_m3 = []
    xret_center = int(
        laygen.get_template_size(
            name=sarret_name, gridname=rg_m3m4_thick, libname=workinglib)[0] /
        2)
    for p in pdict_m3m4[iret.name]:
        if p.startswith('VDD'):
            r0 = laygen.route(None,
                              laygen.layers['metal'][3],
                              xy0=pdict_m3m4[iret.name][p][0],
                              xy1=pdict_m3m4[isp[-1].name][p][0],
                              gridname0=rg_m3m4)
            if pdict_m3m4[iret.name][p][0][0] < xret_center:
                rvddl_m3.append(r0)
            else:
                rvddr_m3.append(r0)
    for p in pdict_m3m4[iret.name]:
        if p.startswith('VSS'):
            r0 = laygen.route(None,
                              laygen.layers['metal'][3],
                              xy0=pdict_m3m4[iret.name][p][0],
                              xy1=pdict_m3m4[isp[-1].name][p][0],
                              gridname0=rg_m3m4)
            if pdict_m3m4[iret.name][p][0][0] < xret_center:
                rvssl_m3.append(r0)
            else:
                rvssr_m3.append(r0)
    #m4
    input_rails_rect = [rvddl_m3, rvssl_m3]
    rvddl_m4, rvssl_m4 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='L_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        offset_start_index=2,
        offset_end_index=-8)
    x1 = laygen.get_inst_xy(name=bnd_right[0].name, gridname=rg_m3m4_thick)[0]\
         +laygen.get_template_size(name=bnd_right[0].cellname, gridname=rg_m3m4_thick, libname=utemplib)[0]
    input_rails_rect = [rvddr_m3, rvssr_m3]
    rvddr_m4, rvssr_m4 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='R_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=x1,
        offset_start_index=2,
        offset_end_index=-8)
    #additional m4 routes
    inst_exclude = [isp[0], isp[1], iret, ifsm, isl, ickg, isp[-1], isp[-2]]
    x0 = laygen.get_inst_xy(name=bnd_left[0].name, gridname=rg_m3m4_thick)[0]
    y0 = laygen.get_inst_xy(name=bnd_left[0].name, gridname=rg_m3m4_thick)[1]
    x1 = laygen.get_inst_xy(name=bnd_right[0].name, gridname=rg_m3m4_thick)[0]\
         +laygen.get_template_size(name=bnd_right[0].cellname, gridname=rg_m3m4_thick, libname=utemplib)[0]
    y1 = laygen.get_inst_xy(name=bnd_left[-1].name, gridname=rg_m3m4_thick)[1]
    for i in range(y1 - y0):
        #check if y is not in the exclude area
        trig = 1
        for iex in inst_exclude:
            if iex.transform == 'MX':
                yex0 = laygen.get_inst_xy(name=iex.name, gridname=rg_m3m4_thick)[1]-1\
                       -laygen.get_template_size(name=iex.cellname, gridname=rg_m3m4_thick, libname=workinglib)[1]
                yex1 = laygen.get_inst_xy(name=iex.name,
                                          gridname=rg_m3m4_thick)[1] + 1
            else:
                yex0 = laygen.get_inst_xy(name=iex.name,
                                          gridname=rg_m3m4_thick)[1] - 1
                yex1 = laygen.get_inst_xy(name=iex.name, gridname=rg_m3m4_thick)[1]+1\
                       +laygen.get_template_size(name=iex.cellname, gridname=rg_m3m4_thick, libname=workinglib)[1]
            if y0 + i > yex0 and y0 + i < yex1:  #exclude
                trig = 0
        if trig == 1:
            r0 = laygen.route(None,
                              laygen.layers['metal'][4],
                              xy0=np.array([x0, y0 + i]),
                              xy1=np.array([x1, y0 + i]),
                              gridname0=rg_m3m4_thick)
    #m5
    y1 = laygen.get_inst_xy(name=bnd_top[0].name, gridname=rg_m4m5_thick)[1]\
                            +laygen.get_template_size(name=bnd_top[0].cellname, gridname=rg_m4m5_thick)[1]
    input_rails_rect = [rvddl_m4, rvssl_m4]
    rvddl_m5, rvssl_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='L_M5_',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=y1,
        offset_start_index=1,
        offset_end_index=0)
    input_rails_rect = [rvddr_m4, rvssr_m4]
    rvddr_m5, rvssr_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='R_M5_',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=y1,
        offset_start_index=1,
        offset_end_index=0)
    #m6
    input_rails_rect = [rvddl_m5, rvssl_m5]
    rvddl_m6, rvssl_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='L_M6_',
        layer=laygen.layers['metal'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        offset_start_index=1,
        offset_end_index=-1)
    x1 = laygen.get_inst_xy(name=bnd_right[0].name, gridname=rg_m5m6_thick)[0]\
         +laygen.get_template_size(name=bnd_right[0].cellname, gridname=rg_m5m6_thick, libname=utemplib)[0]
    x1_phy = laygen.get_inst_xy(name=bnd_right[0].name)[0]\
         +laygen.get_template_size(name=bnd_right[0].cellname, libname=utemplib)[0]
    input_rails_rect = [rvddr_m5, rvssr_m5]
    rvddr_m6, rvssr_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='R_M6_',
        layer=laygen.layers['metal'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=x1,
        offset_start_index=1,
        offset_end_index=-1)
    #trimming
    for r in rvddr_m6:
        r.xy1[0] = x1_phy
    for r in rvssr_m6:
        r.xy1[0] = x1_phy
    #addtional m6 routes
    rvdd_m6 = []
    rvss_m6 = []
    #inst_reference=[isp[0], isp[1], iret,ifsm,isl]
    inst_reference = [ifsm, isl]
    inst_reference_offset0 = [3, 1]
    inst_reference_offset1 = [4, 7]
    #num_route=[10,10]
    num_route = []
    for i, inst in enumerate(inst_reference):
        num_route.append(
            laygen.get_template_size(name=inst.cellname,
                                     gridname=rg_m5m6_thick,
                                     libname=workinglib)[1] - 2)
    x0 = laygen.get_inst_xy(name=bnd_left[0].name, gridname=rg_m5m6_thick)[0]
    x1 = laygen.get_inst_xy(name=bnd_right[0].name, gridname=rg_m5m6_thick)[0]\
         +laygen.get_template_size(name=bnd_right[0].cellname, gridname=rg_m5m6_thick, libname=utemplib)[0]
    n_vdd_m6 = 0  #number for m6 wires
    n_vss_m6 = 0  #number for m6 wires
    for i, inst in enumerate(inst_reference):
        for j in range(inst_reference_offset0[i],
                       num_route[i] - inst_reference_offset1[i]):
            if i == 0:
                y0 = laygen.get_inst_xy(name=inst.name,
                                        gridname=rg_m5m6_thick)[1] + 1
                r0 = laygen.route(None,
                                  laygen.layers['metal'][6],
                                  xy0=np.array([x0, y0 + j]),
                                  xy1=np.array([x1, y0 + j]),
                                  gridname0=rg_m5m6_thick)
                r0.xy1[0] = x1_phy
            else:
                y0 = laygen.get_inst_xy(name=inst.name,
                                        gridname=rg_m5m6_thick)[1] + 1
                r0 = laygen.route(None,
                                  laygen.layers['metal'][6],
                                  xy0=np.array([x0, y0 + j]),
                                  xy1=np.array([x1, y0 + j]),
                                  gridname0=rg_m5m6_thick)
                r0.xy1[0] = x1_phy
            if j % 2 == 0:
                rvdd_m6.append(r0)
                xy0 = laygen.get_rect_xy(name=r0.name, gridname=rg_m5m6_thick)
                laygen.pin(name='VDD_M6' + str(n_vdd_m6),
                           layer=laygen.layers['pin'][6],
                           xy=xy0,
                           gridname=rg_m5m6_thick,
                           netname='VDD')
                n_vdd_m6 += 1
            else:
                rvss_m6.append(r0)
                xy0 = laygen.get_rect_xy(name=r0.name, gridname=rg_m5m6_thick)
                laygen.pin(name='VSS_M6' + str(n_vss_m6),
                           layer=laygen.layers['pin'][6],
                           xy=xy0,
                           gridname=rg_m5m6_thick,
                           netname='VSS')
                n_vss_m6 += 1
    # VBB
    rvbb_m3 = []
    for p in pdict_m3m4[isl.name]:
        if p.startswith('VBB'):
            laygen.pin(name=str(p),
                       layer=laygen.layers['pin'][3],
                       xy=pdict_m3m4[isl.name][p],
                       gridname=rg_m3m4,
                       netname='VBB')
Exemple #15
0
def generate_sarabe_dualdelay(laygen,
                              objectname_pfix,
                              workinglib,
                              placement_grid,
                              routing_grid_m2m3,
                              routing_grid_m3m4_thick,
                              routing_grid_m4m5_thick,
                              routing_grid_m5m6_thick,
                              routing_grid_m4m5,
                              num_bits=9,
                              origin=np.array([0, 0])):
    """generate sar backend """
    pg = placement_grid

    rg_m2m3 = routing_grid_m2m3
    rg_m3m4_thick = routing_grid_m3m4_thick
    rg_m4m5_thick = routing_grid_m4m5_thick
    rg_m5m6_thick = routing_grid_m5m6_thick
    rg_m4m5 = routing_grid_m4m5

    sarfsm_name = 'sarfsm_' + str(num_bits) + 'b'
    sarlogic_name = 'sarlogic_wret_array_' + str(num_bits) + 'b'
    sarclkdelay_name = 'sarclkdelay_compact_dual'
    sarclkgen_name = 'sarclkgen_static'
    sarret_name = 'sarret_' + str(num_bits) + 'b'
    space_name = 'space'

    xy0 = laygen.get_template_size(name=space_name,
                                   gridname=pg,
                                   libname=workinglib)
    xsp = xy0[0]
    ysp = xy0[1]

    # placement
    core_origin = origin + laygen.get_template_size('boundary_bottomleft', pg)
    isp = []
    devname_bnd_left = []
    devname_bnd_right = []
    transform_bnd_left = []
    transform_bnd_right = []
    #additional space for routing area
    isp.append(
        laygen.place(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                     templatename=space_name,
                     gridname=pg,
                     xy=core_origin,
                     transform='R0',
                     template_libname=workinglib))
    refi = isp[-1].name
    isp.append(
        laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                        templatename=space_name,
                        gridname=pg,
                        refinstname=refi,
                        direction='top',
                        transform='MX',
                        template_libname=workinglib))
    refi = isp[-1].name
    devname_bnd_left += [
        'nmos4_fast_left', 'pmos4_fast_left', 'pmos4_fast_left',
        'nmos4_fast_left'
    ]
    devname_bnd_right += [
        'nmos4_fast_right', 'pmos4_fast_right', 'pmos4_fast_right',
        'nmos4_fast_right'
    ]
    transform_bnd_left += ['R0', 'MX', 'R0', 'MX']
    transform_bnd_right += ['R0', 'MX', 'R0', 'MX']
    #ret
    iret = laygen.relplace(name="I" + objectname_pfix + 'RET0',
                           templatename=sarret_name,
                           gridname=pg,
                           refinstname=refi,
                           direction='top',
                           template_libname=workinglib)
    refi = iret.name
    yret = int(
        laygen.get_template_size(
            name=sarret_name, gridname=pg, libname=workinglib)[1] / ysp)
    for i in range(yret):  #boundary cells
        if i % 2 == 0:
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
    # space insertion if number of rows is odd
    if not yret % 2 == 0:
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='MX',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']
    #fsm
    ifsm = laygen.relplace(name="I" + objectname_pfix + 'FSM0',
                           templatename=sarfsm_name,
                           gridname=pg,
                           refinstname=refi,
                           direction='top',
                           template_libname=workinglib)
    refi = ifsm.name
    yfsm = int(
        laygen.get_template_size(
            name=sarfsm_name, gridname=pg, libname=workinglib)[1] / ysp)
    for i in range(yfsm):  #boundary cells
        if i % 2 == 0:
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
    # space insertion if number of rows is odd
    if not yfsm % 2 == 0:
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='MX',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']
    #additional space for routing area
    isp.append(
        laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                        templatename=space_name,
                        gridname=pg,
                        refinstname=refi,
                        direction='top',
                        transform='R0',
                        template_libname=workinglib))
    refi = isp[-1].name
    isp.append(
        laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                        templatename=space_name,
                        gridname=pg,
                        refinstname=refi,
                        direction='top',
                        transform='MX',
                        template_libname=workinglib))
    refi = isp[-1].name
    devname_bnd_left += [
        'nmos4_fast_left', 'pmos4_fast_left', 'pmos4_fast_left',
        'nmos4_fast_left'
    ]
    devname_bnd_right += [
        'nmos4_fast_right', 'pmos4_fast_right', 'pmos4_fast_right',
        'nmos4_fast_right'
    ]
    transform_bnd_left += ['R0', 'MX', 'R0', 'MX']
    transform_bnd_right += ['R0', 'MX', 'R0', 'MX']
    # sarlogic
    isl = laygen.relplace(name="I" + objectname_pfix + 'SL0',
                          templatename=sarlogic_name,
                          gridname=pg,
                          refinstname=refi,
                          direction='top',
                          template_libname=workinglib)
    refi = isl.name
    ysl = int(
        laygen.get_template_size(
            name=sarlogic_name, gridname=pg, libname=workinglib)[1] / ysp)
    for i in range(ysl):  #boundary cells
        if i % 2 == 0:
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
    # space insertion if number of rows is odd
    if not ysl % 2 == 0:
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='MX',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']
    #clkdelay & clkgen
    ickd = laygen.relplace(name="I" + objectname_pfix + 'CKD0',
                           templatename=sarclkdelay_name,
                           gridname=pg,
                           refinstname=refi,
                           direction='top',
                           template_libname=workinglib)
    refi = ickd.name
    ickg = laygen.relplace(name="I" + objectname_pfix + 'CKG0',
                           templatename=sarclkgen_name,
                           gridname=pg,
                           refinstname=refi,
                           direction='top',
                           transform='MX',
                           template_libname=workinglib)
    refi = ickg.name
    # space insertion if number of rows is odd
    yck=(laygen.get_template_size(name=sarclkdelay_name, gridname=pg, libname=workinglib)[1]+\
         laygen.get_template_size(name=sarclkgen_name, gridname=pg, libname=workinglib)[1])/ysp
    for i in range(int(yck)):  #boundary cells
        if i % 2 == 0:
            devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
            devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
        else:
            devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
            devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
            transform_bnd_left += ['R0', 'MX']
            transform_bnd_right += ['R0', 'MX']
    if not int(yck / ysp) % 2 == 0:
        isp.append(
            laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                            templatename=space_name,
                            gridname=pg,
                            refinstname=refi,
                            direction='top',
                            transform='MX',
                            template_libname=workinglib))
        refi = isp[-1].name
        devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
        devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
        transform_bnd_left += ['R0', 'MX']
        transform_bnd_right += ['R0', 'MX']
    #additional space for routing area
    isp.append(
        laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                        templatename=space_name,
                        gridname=pg,
                        refinstname=refi,
                        direction='top',
                        transform='R0',
                        template_libname=workinglib))
    refi = isp[-1].name
    isp.append(
        laygen.relplace(name="I" + objectname_pfix + 'SP' + str(len(isp)),
                        templatename=space_name,
                        gridname=pg,
                        refinstname=refi,
                        direction='top',
                        transform='MX',
                        template_libname=workinglib))
    refi = isp[-1].name
    devname_bnd_left += [
        'nmos4_fast_left', 'pmos4_fast_left', 'pmos4_fast_left',
        'nmos4_fast_left'
    ]
    devname_bnd_right += [
        'nmos4_fast_right', 'pmos4_fast_right', 'pmos4_fast_right',
        'nmos4_fast_right'
    ]
    transform_bnd_left += ['R0', 'MX', 'R0', 'MX']
    transform_bnd_right += ['R0', 'MX', 'R0', 'MX']

    # boundaries
    m_bnd = int(xsp /
                laygen.get_template_size('boundary_bottom', gridname=pg)[0])
    [bnd_bottom, bnd_top, bnd_left, bnd_right] \
        = generate_boundary(laygen, objectname_pfix='BND0', placement_grid=pg,
                            devname_bottom=['boundary_bottomleft', 'boundary_bottom', 'boundary_bottomright'],
                            shape_bottom=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_top=['boundary_topleft', 'boundary_top', 'boundary_topright'],
                            shape_top=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
                            devname_left=devname_bnd_left,
                            transform_left=transform_bnd_left,
                            devname_right=devname_bnd_right,
                            transform_right=transform_bnd_right,
                            origin=origin)
    #route
    #reference coordinates
    pdict_m3m4 = laygen.get_inst_pin_coord(None, None, rg_m3m4)
    pdict_m4m5 = laygen.get_inst_pin_coord(None, None, rg_m4m5)
    pdict_m5m6 = laygen.get_inst_pin_coord(None, None, rg_m5m6)
    x_right = laygen.get_inst_xy(name=ifsm.name, gridname=rg_m5m6)[0]\
             +laygen.get_template_size(name=ifsm.cellname, gridname=rg_m5m6, libname=workinglib)[0] - 1
    y_top = laygen.get_inst_xy(name=ickg.name, gridname=rg_m5m6)[1] - 1
    xysl = laygen.get_inst_xy(name=isl.name, gridname=rg_m5m6)
    xyfsm = laygen.get_inst_xy(name=ifsm.name, gridname=rg_m5m6)
    xyret = laygen.get_inst_xy(name=iret.name, gridname=rg_m5m6)

    # rst signal route
    # very important because it sets the timing margin
    # ckd to sl
    rh0, rh0 = laygen.route_hv(laygen.layers['metal'][4],
                               laygen.layers['metal'][5],
                               pdict_m4m5[ickd.name]['RST'][0],
                               pdict_m4m5[isl.name]['RST'][0], rg_m4m5)
    # fsm to sl
    [rv0, rh0, rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                       laygen.layers['metal'][6],
                                       pdict_m5m6[ifsm.name]['RST'][0],
                                       pdict_m5m6[isl.name]['RST'][0],
                                       xyfsm[1] - num_bits - 2, rg_m5m6)
    # ckg to sl route
    rh0, rrst0 = laygen.route_hv(laygen.layers['metal'][4],
                                 laygen.layers['metal'][5],
                                 pdict_m4m5[ickg.name]['RST'][0],
                                 pdict_m4m5[isl.name]['RST'][0], rg_m4m5)
    # fsm to ret (rst)
    [rv0, rh0, rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                       laygen.layers['metal'][6],
                                       pdict_m5m6[isl.name]['RST'][0],
                                       pdict_m5m6[iret.name]['CLK'][0],
                                       xyfsm[1] - num_bits - 2, rg_m5m6)
    # clk output to final retimer
    #rrstout0 = laygen.route(None, laygen.layers['metal'][5],
    #                        xy0=pdict_m5m6[ifsm.name]['RST'][0],
    #                        xy1=np.array([pdict_m5m6[ifsm.name]['RST'][0][0], 0]), gridname0=rg_m5m6)
    rrstout0 = laygen.route(None,
                            laygen.layers['metal'][5],
                            xy0=pdict_m5m6[isl.name]['RST'][0],
                            xy1=np.array(
                                [pdict_m5m6[isl.name]['RST'][0][0], 0]),
                            gridname0=rg_m5m6)
    laygen.create_boundary_pin_form_rect(rrstout0,
                                         rg_m4m5,
                                         'RSTOUT',
                                         laygen.layers['pin'][5],
                                         size=6,
                                         direction='bottom',
                                         netname='RST')
    # clk input
    laygen.create_boundary_pin_form_rect(rrst0,
                                         rg_m5m6,
                                         'RST',
                                         laygen.layers['pin'][5],
                                         size=6,
                                         direction='top')

    # sarclk signal route
    # ckgen to fsm
    rh0, rv0, rh1 = laygen.route_hvh(laygen.layers['metal'][4],
                                     laygen.layers['metal'][5],
                                     pdict_m4m5[ickg.name]['CLKO'][0],
                                     pdict_m4m5[ifsm.name]['CLK'][0],
                                     pdict_m4m5[isl.name]['RETO<1>'][1][0] - 1,
                                     rg_m4m5)

    # datapath
    # ckdly route
    # sb
    rh0, rh0 = laygen.route_hv(laygen.layers['metal'][4],
                               laygen.layers['metal'][5],
                               pdict_m4m5[ickd.name]['SB'][0],
                               pdict_m4m5[isl.name]['SB<4>'][0], rg_m4m5)
    # done
    [rh0, rv0, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['DONE'][0],
                          pdict_m5m6[ickd.name]['I'][0],
                          pdict_m5m6[ickd.name]['I'][0][0], rg_m4m5)
    # up
    [rh0, rv0, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['UP'][0],
                          pdict_m5m6[ickd.name]['O'][0],
                          pdict_m5m6[ickg.name]['UP'][0][0], rg_m4m5)
    # ckgen to sl route
    # saopb/saomb
    [rh0, rsaopb0, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['SAOPB'][0],
                          pdict_m5m6[isl.name]['SAOPB'][0],
                          pdict_m5m6[ickg.name]['SAOPB'][0][0] + 1, rg_m4m5)
    [rh0, rsaomb0, rh1
     ] = laygen.route_hvh(laygen.layers['metal'][4], laygen.layers['metal'][5],
                          pdict_m5m6[ickg.name]['SAOMB'][0],
                          pdict_m5m6[isl.name]['SAOMB'][0],
                          pdict_m5m6[ickg.name]['SAOMB'][0][0] + 2, rg_m4m5)
    # fsm to sl route
    # sb
    for i in range(num_bits):
        [rv0, rh0, rv1
         ] = laygen.route_vhv(laygen.layers['metal'][5],
                              laygen.layers['metal'][6],
                              pdict_m5m6[ifsm.name]['SB<' + str(i) + '>'][0],
                              pdict_m5m6[isl.name]['SB<' + str(i) + '>'][0],
                              xysl[1] - i - 1, rg_m5m6)
    # zp/zm/zmid
    for i in range(num_bits):
        rzp0 = laygen.route(None,
                            laygen.layers['metal'][5],
                            xy0=pdict_m4m5[isl.name]['ZP<' + str(i) + '>'][0],
                            xy1=pdict_m4m5[isl.name]['ZP<' + str(i) + '>'][0] +
                            np.array([0, 4]),
                            gridname0=rg_m5m6)
        laygen.create_boundary_pin_form_rect(rzp0,
                                             rg_m5m6,
                                             'ZP<' + str(i) + '>',
                                             laygen.layers['pin'][5],
                                             size=6,
                                             direction='top')
        rzm0 = laygen.route(None,
                            laygen.layers['metal'][5],
                            xy0=pdict_m4m5[isl.name]['ZM<' + str(i) + '>'][0],
                            xy1=pdict_m4m5[isl.name]['ZM<' + str(i) + '>'][0] +
                            np.array([0, 4]),
                            gridname0=rg_m5m6)
        laygen.create_boundary_pin_form_rect(rzm0,
                                             rg_m5m6,
                                             'ZM<' + str(i) + '>',
                                             laygen.layers['pin'][5],
                                             size=6,
                                             direction='top')
        rzmid0 = laygen.route(
            None,
            laygen.layers['metal'][5],
            xy0=pdict_m4m5[isl.name]['ZMID<' + str(i) + '>'][0],
            xy1=pdict_m4m5[isl.name]['ZMID<' + str(i) + '>'][0] +
            np.array([0, 4]),
            gridname0=rg_m5m6)
        laygen.create_boundary_pin_form_rect(rzmid0,
                                             rg_m5m6,
                                             'ZMID<' + str(i) + '>',
                                             laygen.layers['pin'][5],
                                             size=6,
                                             direction='top')
    # ckdsel
    for i in range(2):
        rh0, rclkdsel0 = laygen.route_hv(
            laygen.layers['metal'][4], laygen.layers['metal'][5],
            pdict_m4m5[ickd.name]['SEL0<' + str(i) + '>'][0],
            np.array([pdict_m4m5[isl.name]['RETO<7>'][1][0] + 1 + i,
                      0]), rg_m4m5)
        laygen.create_boundary_pin_form_rect(rclkdsel0,
                                             rg_m4m5,
                                             'CKDSEL0<' + str(i) + '>',
                                             laygen.layers['pin'][5],
                                             size=6,
                                             direction='bottom')
        rh0, rclkdsel0 = laygen.route_hv(
            laygen.layers['metal'][4], laygen.layers['metal'][5],
            pdict_m4m5[ickd.name]['SEL1<' + str(i) + '>'][0],
            np.array([pdict_m4m5[isl.name]['RETO<7>'][1][0] + 1 + i + 2,
                      0]), rg_m4m5)
        laygen.create_boundary_pin_form_rect(rclkdsel0,
                                             rg_m4m5,
                                             'CKDSEL1<' + str(i) + '>',
                                             laygen.layers['pin'][5],
                                             size=6,
                                             direction='bottom')
    # SAOPB/SAOMB
    laygen.create_boundary_pin_form_rect(rsaopb0,
                                         rg_m4m5,
                                         'SAOPB',
                                         laygen.layers['pin'][5],
                                         size=6,
                                         direction='top')
    laygen.create_boundary_pin_form_rect(rsaomb0,
                                         rg_m4m5,
                                         'SAOMB',
                                         laygen.layers['pin'][5],
                                         size=6,
                                         direction='top')
    # extclk, extsel_clk
    rh0, rextclk0 = laygen.route_hv(
        laygen.layers['metal'][4], laygen.layers['metal'][5],
        pdict_m4m5[ickg.name]['EXTCLK'][0],
        np.array([pdict_m4m5[ickg.name]['EXTCLK'][0][0] - 2 + 16, 0]), rg_m4m5)
    laygen.create_boundary_pin_form_rect(rextclk0,
                                         rg_m4m5,
                                         'EXTCLK',
                                         laygen.layers['pin'][5],
                                         size=6,
                                         direction='bottom')
    rh0, rextsel_clk0 = laygen.route_hv(
        laygen.layers['metal'][4], laygen.layers['metal'][5],
        pdict_m4m5[ickg.name]['EXTSEL_CLK'][0],
        np.array([pdict_m4m5[ickg.name]['EXTSEL_CLK'][0][0] - 4 + 16,
                  0]), rg_m4m5)
    laygen.create_boundary_pin_form_rect(rextsel_clk0,
                                         rg_m4m5,
                                         'EXTSEL_CLK',
                                         laygen.layers['pin'][5],
                                         size=6,
                                         direction='bottom')
    # fsm to ret (data)
    for i in range(num_bits):
        [rv0, rh0, rv1
         ] = laygen.route_vhv(laygen.layers['metal'][5],
                              laygen.layers['metal'][6],
                              pdict_m5m6[isl.name]['RETO<' + str(i) + '>'][0],
                              pdict_m5m6[iret.name]['IN<' + str(i) + '>'][0],
                              xyfsm[1] - i - 1, rg_m5m6)
    # adcout
    for i in range(num_bits):
        radco0 = laygen.route(
            None,
            laygen.layers['metal'][5],
            xy0=pdict_m4m5[iret.name]['OUT<' + str(i) + '>'][0],
            xy1=np.array(
                [pdict_m4m5[iret.name]['OUT<' + str(i) + '>'][0][0], 0]),
            gridname0=rg_m5m6)
        laygen.create_boundary_pin_form_rect(radco0,
                                             rg_m4m5,
                                             'ADCOUT<' + str(i) + '>',
                                             laygen.layers['pin'][5],
                                             size=6,
                                             direction='bottom')
    # probe outputs
    laygen.pin(name='CLKPRB',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['CLKPRB'],
               gridname=rg_m4m5)
    laygen.pin(name='COMPOUT',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['COMPOUT'],
               gridname=rg_m4m5)
    laygen.pin(name='UP',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['UP'],
               gridname=rg_m4m5)
    laygen.pin(name='DONE',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['DONE'],
               gridname=rg_m4m5)
    laygen.pin(name='SARCLK',
               layer=laygen.layers['pin'][4],
               xy=pdict_m4m5[ickg.name]['CLKO'],
               gridname=rg_m4m5)
    rh0, rsclkb0 = laygen.route_hv(
        laygen.layers['metal'][4], laygen.layers['metal'][5],
        pdict_m4m5[ickg.name]['CLKOB'][0],
        np.array([pdict_m4m5[ickg.name]['CLKOB'][1][0] + 12, y_top]), rg_m4m5)
    laygen.create_boundary_pin_form_rect(rsclkb0,
                                         rg_m4m5,
                                         'SARCLKB',
                                         laygen.layers['pin'][5],
                                         size=6,
                                         direction='top')
    for i in range(num_bits):
        laygen.pin(name='SB<' + str(i) + '>',
                   layer=laygen.layers['pin'][5],
                   xy=pdict_m5m6[isl.name]['SB<' + str(i) + '>'],
                   gridname=rg_m5m6)
    # vdd/vss
    # m3
    rvddl_m3 = []
    rvssl_m3 = []
    rvddr_m3 = []
    rvssr_m3 = []
    xret_center = int(
        laygen.get_template_size(
            name=sarret_name, gridname=rg_m3m4_thick, libname=workinglib)[0] /
        2)
    for p in pdict_m3m4[iret.name]:
        if p.startswith('VDD'):
            r0 = laygen.route(None,
                              laygen.layers['metal'][3],
                              xy0=pdict_m3m4[iret.name][p][0],
                              xy1=pdict_m3m4[isp[-1].name][p][0],
                              gridname0=rg_m3m4)
            if pdict_m3m4[iret.name][p][0][0] < xret_center:
                rvddl_m3.append(r0)
            else:
                rvddr_m3.append(r0)
    for p in pdict_m3m4[iret.name]:
        if p.startswith('VSS'):
            r0 = laygen.route(None,
                              laygen.layers['metal'][3],
                              xy0=pdict_m3m4[iret.name][p][0],
                              xy1=pdict_m3m4[isp[-1].name][p][0],
                              gridname0=rg_m3m4)
            if pdict_m3m4[iret.name][p][0][0] < xret_center:
                rvssl_m3.append(r0)
            else:
                rvssr_m3.append(r0)
    #m4
    input_rails_rect = [rvddl_m3, rvssl_m3]
    rvddl_m4, rvssl_m4 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='L_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        offset_start_index=2,
        offset_end_index=-8)
    x1 = laygen.get_inst_xy(name=bnd_right[0].name, gridname=rg_m3m4_thick)[0]\
         +laygen.get_template_size(name=bnd_right[0].cellname, gridname=rg_m3m4_thick, libname=utemplib)[0]
    input_rails_rect = [rvddr_m3, rvssr_m3]
    rvddr_m4, rvssr_m4 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='R_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=x1,
        offset_start_index=2,
        offset_end_index=-8)
    #additional m4 routes
    inst_exclude = [
        isp[0], isp[1], iret, ifsm, isl, ickd, ickg, isp[-1], isp[-2]
    ]
    x0 = laygen.get_inst_xy(name=bnd_left[0].name, gridname=rg_m3m4_thick)[0]
    y0 = laygen.get_inst_xy(name=bnd_left[0].name, gridname=rg_m3m4_thick)[1]
    x1 = laygen.get_inst_xy(name=bnd_right[0].name, gridname=rg_m3m4_thick)[0]\
         +laygen.get_template_size(name=bnd_right[0].cellname, gridname=rg_m3m4_thick, libname=utemplib)[0]
    y1 = laygen.get_inst_xy(name=bnd_left[-1].name, gridname=rg_m3m4_thick)[1]
    for i in range(y1 - y0):
        #check if y is not in the exclude area
        trig = 1
        for iex in inst_exclude:
            if iex.transform == 'MX':
                yex0 = laygen.get_inst_xy(name=iex.name, gridname=rg_m3m4_thick)[1]-1\
                       -laygen.get_template_size(name=iex.cellname, gridname=rg_m3m4_thick, libname=workinglib)[1]
                yex1 = laygen.get_inst_xy(name=iex.name,
                                          gridname=rg_m3m4_thick)[1] + 1
            else:
                yex0 = laygen.get_inst_xy(name=iex.name,
                                          gridname=rg_m3m4_thick)[1] - 1
                yex1 = laygen.get_inst_xy(name=iex.name, gridname=rg_m3m4_thick)[1]+1\
                       +laygen.get_template_size(name=iex.cellname, gridname=rg_m3m4_thick, libname=workinglib)[1]
            if y0 + i > yex0 and y0 + i < yex1:  #exclude
                trig = 0
        if trig == 1:
            r0 = laygen.route(None,
                              laygen.layers['metal'][4],
                              xy0=np.array([x0, y0 + i]),
                              xy1=np.array([x1, y0 + i]),
                              gridname0=rg_m3m4_thick)
    #m5
    y1 = laygen.get_inst_xy(name=bnd_top[0].name, gridname=rg_m4m5_thick)[1]\
                            +laygen.get_template_size(name=bnd_top[0].cellname, gridname=rg_m4m5_thick)[1]
    input_rails_rect = [rvddl_m4, rvssl_m4]
    rvddl_m5, rvssl_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='L_M5_',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=y1,
        offset_start_index=1,
        offset_end_index=0)
    input_rails_rect = [rvddr_m4, rvssr_m4]
    rvddr_m5, rvssr_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='R_M5_',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=y1,
        offset_start_index=0,
        offset_end_index=0)
    #m6
    input_rails_rect = [rvddl_m5, rvssl_m5]
    rvddl_m6, rvssl_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='L_M6_',
        layer=laygen.layers['metal'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        offset_start_index=1,
        offset_end_index=-1)
    x1 = laygen.get_inst_xy(name=bnd_right[0].name, gridname=rg_m5m6_thick)[0]\
         +laygen.get_template_size(name=bnd_right[0].cellname, gridname=rg_m5m6_thick, libname=utemplib)[0]
    input_rails_rect = [rvddr_m5, rvssr_m5]
    rvddr_m6, rvssr_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='R_M6_',
        layer=laygen.layers['metal'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=x1,
        offset_start_index=1,
        offset_end_index=-1)
    #addtional m6 routes
    rvdd_m6 = []
    rvss_m6 = []
    inst_reference = [isp[0], isp[1], iret, ifsm, isl]
    #num_route=[10,10]
    num_route = []
    for i, inst in enumerate(inst_reference):
        num_route.append(
            laygen.get_template_size(name=inst.cellname,
                                     gridname=rg_m5m6_thick,
                                     libname=workinglib)[1] - 2)
    x0 = laygen.get_inst_xy(name=bnd_left[0].name, gridname=rg_m5m6_thick)[0]
    x1 = laygen.get_inst_xy(name=bnd_right[0].name, gridname=rg_m5m6_thick)[0]\
         +laygen.get_template_size(name=bnd_right[0].cellname, gridname=rg_m5m6_thick, libname=utemplib)[0]
    n_vdd_m6 = 0  #number for m6 wires
    n_vss_m6 = 0  #number for m6 wires
    for i, inst in enumerate(inst_reference):
        for j in range(num_route[i]):
            y0 = laygen.get_inst_xy(name=inst.name,
                                    gridname=rg_m5m6_thick)[1] + 1
            r0 = laygen.route(None,
                              laygen.layers['metal'][6],
                              xy0=np.array([x0, y0 + j]),
                              xy1=np.array([x1, y0 + j]),
                              gridname0=rg_m5m6_thick)
            if j % 2 == 0:
                rvdd_m6.append(r0)
                xy0 = laygen.get_rect_xy(name=r0.name, gridname=rg_m5m6_thick)
                laygen.pin(name='VDD_M6' + str(n_vdd_m6),
                           layer=laygen.layers['pin'][6],
                           xy=xy0,
                           gridname=rg_m5m6_thick,
                           netname='VDD')
                n_vdd_m6 += 1
            else:
                rvss_m6.append(r0)
                xy0 = laygen.get_rect_xy(name=r0.name, gridname=rg_m5m6_thick)
                laygen.pin(name='VSS_M6' + str(n_vss_m6),
                           layer=laygen.layers['pin'][6],
                           xy=xy0,
                           gridname=rg_m5m6_thick,
                           netname='VSS')
                n_vss_m6 += 1
def generate_sarafe_nsw(laygen, objectname_pfix, workinglib, placement_grid,
                    routing_grid_m2m3_thick, routing_grid_m3m4_thick,
                    routing_grid_m4m5_thick, routing_grid_m5m6_thick,
                    routing_grid_m5m6, routing_grid_m6m7,
                    num_bits=8, num_bits_vertical=6, m_sa=8, origin=np.array([0, 0])):
    """generate sar analog frontend """
    pg = placement_grid

    rg_m3m4_thick = routing_grid_m3m4_thick
    rg_m5m6 = routing_grid_m5m6
    rg_m6m7 = routing_grid_m6m7

    tap_name='tap'
    cdrv_name='capdrv_nsw_array_'+str(num_bits)+'b'
    #cdac_name='capdac_'+str(num_bits)+'b'
    cdac_name='capdac'
    sa_name='salatch_pmos'

    # placement
    xy0 = origin + (laygen.get_template_size(cdrv_name, gridname=pg, libname=workinglib)*np.array([1, 0]) )
    icdrvl = laygen.place(name="I" + objectname_pfix + 'CDRVL0', templatename=cdrv_name, gridname=pg,
                          xy=xy0, template_libname = workinglib, transform='MY')
    icdrvr = laygen.place(name="I" + objectname_pfix + 'CDRVR0', templatename=cdrv_name, gridname=pg,
                          xy=xy0, template_libname = workinglib)
    xy0 = origin + laygen.get_template_size(cdrv_name, gridname=pg, libname=workinglib)*np.array([0, 1]) \
                 + laygen.get_template_size(sa_name, gridname=pg, libname=workinglib) * np.array([0, 1])
    isa = laygen.place(name="I" + objectname_pfix + 'SA0', templatename=sa_name, gridname=pg,
                       xy=xy0, template_libname = workinglib, transform='MX')
    xy0 = origin + laygen.get_template_size(cdrv_name, gridname=pg, libname=workinglib)*np.array([0, 1]) \
                 + laygen.get_template_size(sa_name, gridname=pg, libname=workinglib) * np.array([0, 1]) \
                 + laygen.get_template_size(cdac_name, gridname=pg, libname=workinglib)*np.array([1, 0])
    icdacl = laygen.place(name="I" + objectname_pfix + 'CDACL0', templatename=cdac_name, gridname=pg,
                          xy=xy0, template_libname = workinglib, transform='MY')
    xy0 = origin + laygen.get_template_size(cdrv_name, gridname=pg, libname=workinglib)*np.array([2, 1]) \
                 + laygen.get_template_size(sa_name, gridname=pg, libname=workinglib) * np.array([0, 1]) \
                 - laygen.get_template_size(cdac_name, gridname=pg, libname=workinglib)*np.array([1, 0])
    icdacr = laygen.place(name="I" + objectname_pfix + 'CDACR0', templatename=cdac_name, gridname=pg,
                          xy=xy0, template_libname = workinglib)

    # pin informations
    pdict_m3m4_thick=laygen.get_inst_pin_coord(None, None, rg_m3m4_thick)

    # internal pins
    icdrvl_vo_xy = []
    icdacl_i_xy = []
    icdrvr_vo_xy = []
    icdacr_i_xy = []

    icdrvl_vo_c0_xy = laygen.get_inst_pin_coord(icdrvl.name, 'VO_C0', rg_m5m6)
    icdacl_i_c0_xy = laygen.get_inst_pin_coord(icdacl.name, 'I_C0', rg_m5m6)
    icdrvr_vo_c0_xy = laygen.get_inst_pin_coord(icdrvr.name, 'VO_C0', rg_m5m6)
    icdacr_i_c0_xy = laygen.get_inst_pin_coord(icdacr.name, 'I_C0', rg_m5m6)
    for i in range(num_bits):
        icdrvl_vo_xy.append(laygen.get_inst_pin_coord(icdrvl.name, 'VO<' + str(i) + '>', rg_m5m6))
        icdacl_i_xy.append(laygen.get_inst_pin_coord(icdacl.name, 'I<' + str(i) + '>', rg_m5m6))
        icdrvr_vo_xy.append(laygen.get_inst_pin_coord(icdrvr.name, 'VO<' + str(i) + '>', rg_m5m6))
        icdacr_i_xy.append(laygen.get_inst_pin_coord(icdacr.name, 'I<' + str(i) + '>', rg_m5m6))

    #route
    #capdrv to capdac
    #y0 = origin[1] + laygen.get_template_size(cdrv_name, gridname=rg_m5m6, libname=workinglib)[1]-2 #refer to capdrv
    y0 = origin[1] + laygen.get_template_size(cdrv_name, gridname=rg_m5m6, libname=workinglib)[1] \
         + laygen.get_template_size(sa_name, gridname=rg_m5m6, libname=workinglib)[1]-4 #refer to sa
    [rv0, rh0, rv1] = laygen.route_vhv(laygen.layers['metal'][5], laygen.layers['metal'][6], icdrvl_vo_c0_xy[0],
                                       icdacl_i_c0_xy[0], y0 + 2, rg_m5m6, layerv1=laygen.layers['metal'][7], gridname1=rg_m6m7)
    laygen.create_boundary_pin_form_rect(rv0, rg_m5m6, "VOL_C0", laygen.layers['pin'][5], size=4, direction='bottom', netname='VREF<1>')
    [rv0, rh0, rv1] = laygen.route_vhv(laygen.layers['metal'][5], laygen.layers['metal'][6], icdrvr_vo_c0_xy[0],
                                       icdacr_i_c0_xy[0], y0 + 2, rg_m5m6, layerv1=laygen.layers['metal'][7], gridname1=rg_m6m7)
    laygen.create_boundary_pin_form_rect(rv0, rg_m5m6, "VOR_C0", laygen.layers['pin'][5], size=4, direction='bottom', netname='VREF<1>')

    for i in range(num_bits):
        [rv0, rh0, rv1] = laygen.route_vhv(laygen.layers['metal'][5], laygen.layers['metal'][6], icdrvl_vo_xy[i][0],
                                           icdacl_i_xy[i][0], y0 - i, rg_m5m6, layerv1=laygen.layers['metal'][7], gridname1=rg_m6m7)
        laygen.create_boundary_pin_form_rect(rv0, rg_m5m6, "VOL<"+str(i)+">", laygen.layers['pin'][5], size=4, direction='bottom')
        [rv0, rh0, rv1] = laygen.route_vhv(laygen.layers['metal'][5], laygen.layers['metal'][6], icdrvr_vo_xy[i][0],
                                           icdacr_i_xy[i][0], y0 - i, rg_m5m6, layerv1=laygen.layers['metal'][7], gridname1=rg_m6m7)
        laygen.create_boundary_pin_form_rect(rv0, rg_m5m6, "VOR<"+str(i)+">", laygen.layers['pin'][5], size=4, direction='bottom')


    #vref
    rvref0=laygen.route(None, laygen.layers['metal'][4], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                        refinstname0=icdrvl.name, refpinname0='VREF<0>', gridname0=rg_m4m5,
                        refinstname1=icdrvr.name, refpinname1='VREF<0>')
    rvref1=laygen.route(None, laygen.layers['metal'][4], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                        refinstname0=icdrvl.name, refpinname0='VREF<1>', gridname0=rg_m4m5,
                        refinstname1=icdrvr.name, refpinname1='VREF<1>')
    rvref2=laygen.route(None, laygen.layers['metal'][4], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                        refinstname0=icdrvl.name, refpinname0='VREF<2>', gridname0=rg_m4m5,
                        refinstname1=icdrvr.name, refpinname1='VREF<2>')
    #input pins
    #y0 = laygen.get_inst_pin_coord(icdrvl.name, 'EN0<0>', rg_m4m5, index=np.array([0, 0]), sort=True)[0][1]
    y0 = 0
    rclkb=laygen.route(None, laygen.layers['metal'][5], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                      refinstname0=isa.name, refpinname0='CLKB', gridname0=rg_m4m5, direction='y')
    routp=laygen.route(None, laygen.layers['metal'][5], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                      refinstname0=isa.name, refpinname0='OUTP', gridname0=rg_m4m5, direction='y')
    routm=laygen.route(None, laygen.layers['metal'][5], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                      refinstname0=isa.name, refpinname0='OUTM', gridname0=rg_m4m5, direction='y')
    rosp=laygen.route(None, laygen.layers['metal'][3], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                      refinstname0=isa.name, refpinname0='OSP', gridname0=rg_m2m3, direction='y')
    rosm=laygen.route(None, laygen.layers['metal'][3], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                      refinstname0=isa.name, refpinname0='OSM', gridname0=rg_m2m3, direction='y')
    renl0 = []
    renl1 = []
    renl2 = []
    renr0 = []
    renr1 = []
    renr2 = []
    for i in range(num_bits):
        renl0.append(laygen.route(None, laygen.layers['metal'][5], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                     refinstname0=icdrvl.name, refpinname0='EN'+str(i)+'<0>', gridname0=rg_m5m6, direction='y'))
        renl1.append(laygen.route(None, laygen.layers['metal'][5], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                     refinstname0=icdrvl.name, refpinname0='EN'+str(i)+'<1>', gridname0=rg_m5m6, direction='y'))
        renl2.append(laygen.route(None, laygen.layers['metal'][5], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                     refinstname0=icdrvl.name, refpinname0='EN'+str(i)+'<2>', gridname0=rg_m5m6, direction='y'))
        renr0.append(laygen.route(None, laygen.layers['metal'][5], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                     refinstname0=icdrvr.name, refpinname0='EN'+str(i)+'<0>', gridname0=rg_m5m6, direction='y'))
        renr1.append(laygen.route(None, laygen.layers['metal'][5], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                     refinstname0=icdrvr.name, refpinname0='EN'+str(i)+'<1>', gridname0=rg_m5m6, direction='y'))
        renr2.append(laygen.route(None, laygen.layers['metal'][5], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
                     refinstname0=icdrvr.name, refpinname0='EN'+str(i)+'<2>', gridname0=rg_m5m6, direction='y'))
    #inp/inm
    pdict_m5m6 = laygen.get_inst_pin_coord(None, None, rg_m5m6)
    outcnt=0
    for pn in pdict_m5m6[icdacl.name]:
        if pn.startswith('O'): #out pin
            outcnt+=1
    x0 = laygen.get_inst_xy(icdacl.name, rg_m5m6)[0] - 8
    x1 = laygen.get_inst_xy(icdacr.name, rg_m5m6)[0] + 8
    nrin = 16  # number of M6 horizontal route stacks
    rinp=[]
    rinm=[]
    for i in range(nrin):
        xy0=laygen.get_inst_pin_coord(icdacl.name, "O"+str(outcnt-1-i), rg_m5m6, index=np.array([0, 0]), sort=True)[0]
        r = laygen.route(None, laygen.layers['metal'][6], xy0=xy0, xy1=np.array([x0, xy0[1]]), gridname0=rg_m5m6)
        rinp.append(r)
        xy0=laygen.get_inst_pin_coord(icdacr.name, "O"+str(outcnt-1-i), rg_m5m6, index=np.array([0, 0]), sort=True)[1]
        r = laygen.route(None, laygen.layers['metal'][6], xy0=xy0, xy1=np.array([x1, xy0[1]]), gridname0=rg_m5m6)
        rinm.append(r)

    nrin_sa = 4  # number of M6 horizontal route stacks for cdac to sa
    for i in range(nrin_sa):
        xy0=laygen.get_inst_pin_coord(icdacl.name, "O"+str(i), rg_m5m6, index=np.array([0, 0]), sort=True)[0]
        laygen.route(None, laygen.layers['metal'][6], xy0=xy0, xy1=np.array([x0, xy0[1]]), gridname0=rg_m5m6)
        for j in range(4):
            laygen.via(None, [x0-2*j, xy0[1]], rg_m5m6)
        xy0=laygen.get_inst_pin_coord(icdacr.name, "O"+str(i), rg_m5m6, index=np.array([0, 0]), sort=True)[1]
        laygen.route(None, laygen.layers['metal'][6], xy0=xy0, xy1=np.array([x1, xy0[1]]), gridname0=rg_m5m6)
        for j in range(4):
            laygen.via(None, [x1+2*j, xy0[1]], rg_m5m6)
    xy0 = laygen.get_inst_pin_coord(isa.name, "INP", rg_m3m4, index=np.array([0, 0]), sort=True)[0]
    xy1 = laygen.get_inst_pin_coord(icdacl.name, "O"+str(nrin_sa-1), rg_m5m6, index=np.array([0, 0]), sort=True)[0]
    for j in range(4):
        laygen.route(None, laygen.layers['metal'][5], xy0=np.array([x0-2*j, xy0[1]]), xy1=np.array([x0-2*j, xy1[1]]), gridname0=rg_m5m6)
    xy0 = laygen.get_inst_pin_coord(isa.name, "INM", rg_m4m5, index=np.array([0, 0]), sort=True)[0]
    xy1 = laygen.get_inst_pin_coord(icdacr.name, "O"+str(nrin_sa-1), rg_m5m6, index=np.array([0, 0]), sort=True)[0]
    for j in range(4):
        laygen.route(None, laygen.layers['metal'][5], xy0=np.array([x1+2*j, xy0[1]]), xy1=np.array([x1+2*j, xy1[1]]), gridname0=rg_m5m6)
    #inp/inm - sa to capdac
    xy0 = laygen.get_inst_pin_coord(isa.name, "INP", rg_m4m5, index=np.array([0, 0]), sort=True)[0]
    xy1 = laygen.get_inst_pin_coord(isa.name, "INM", rg_m4m5, index=np.array([0, 0]), sort=True)[0]
    rsainp=laygen.route(None, laygen.layers['metal'][4], xy0=np.array([x0-8, xy0[1]]), xy1=xy0, gridname0=rg_m4m5)
    rsainm=laygen.route(None, laygen.layers['metal'][4], xy0=np.array([x1+8, xy1[1]]), xy1=xy1, gridname0=rg_m4m5)
    for j in range(4):
        laygen.via(None, [x0 - 2 * j, xy0[1]], rg_m4m5)
        laygen.via(None, [x1 + 2 * j, xy1[1]], rg_m4m5)
    x0 = laygen.get_inst_xy(icdacl.name, rg_m3m4)[0] - 1
    x1 = laygen.get_inst_xy(icdacr.name, rg_m3m4)[0] + 1
    xy0 = laygen.get_inst_pin_coord(isa.name, "INP", rg_m3m4, index=np.array([0, 0]), sort=True)[0]
    xy1 = laygen.get_inst_pin_coord(isa.name, "INM", rg_m3m4, index=np.array([0, 0]), sort=True)[0]
    laygen.route(None, laygen.layers['metal'][4], xy0=np.array([x0, xy0[1]]), xy1=xy0, gridname0=rg_m3m4, addvia1=True)
    laygen.route(None, laygen.layers['metal'][4], xy0=np.array([x1, xy1[1]]), xy1=xy1, gridname0=rg_m3m4, addvia1=True)

    #vdd/vss - route
    #cdrv_left_m4
    rvdd_cdrvl_m3=[]
    rvss_cdrvl_m3=[]
    for pn, p in pdict_m3m4_thick[icdrvl.name].items():
        if pn.startswith('VDDR'):
            rvdd_cdrvl_m3.append(p)
        if pn.startswith('VSSR'):
            rvss_cdrvl_m3.append(p)
    input_rails_xy = [rvdd_cdrvl_m3, rvss_cdrvl_m3]
    rvdd_cdrvl_m4, rvss_cdrvl_m4 = laygenhelper.generate_power_rails_from_rails_xy(laygen, routename_tag='_CDRVL_M4_', 
                layer=laygen.layers['metal'][4], gridname=rg_m3m4_thick, netnames=['VDD', 'VSS'], direction='x', 
                input_rails_xy=input_rails_xy, generate_pin=False, overwrite_start_coord=0, overwrite_end_coord=None,
                offset_start_index=0, offset_end_index=0)
    #cdrv_right_m4
    x1 = laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m3m4_thick)[0]\
         +laygen.get_template_size(name=icdrvr.cellname, gridname=rg_m3m4_thick, libname=workinglib)[0]
    rvdd_cdrvr_m3=[]
    rvss_cdrvr_m3=[]
    for pn, p in pdict_m3m4_thick[icdrvr.name].items():
        if pn.startswith('VDDR'):
            rvdd_cdrvr_m3.append(p)
        if pn.startswith('VSSR'):
            rvss_cdrvr_m3.append(p)
    input_rails_xy = [rvdd_cdrvr_m3, rvss_cdrvr_m3]
    rvdd_cdrvr_m4, rvss_cdrvr_m4 = laygenhelper.generate_power_rails_from_rails_xy(laygen, routename_tag='_CDRVR_M4_', 
                layer=laygen.layers['metal'][4], gridname=rg_m3m4_thick, netnames=['VDD', 'VSS'], direction='x', 
                input_rails_xy=input_rails_xy, generate_pin=False, overwrite_start_coord=None, overwrite_end_coord=x1, 
                offset_start_index=0, offset_end_index=0)
    #sa_left_m4_m5
    rvdd_sal_m3=[]
    rvss_sal_m3=[]
    for pn, p in pdict_m3m4_thick[isa.name].items():
        if pn.startswith('VDDL'):
            rvdd_sal_m3.append(p)
        if pn.startswith('VSSL'):
            rvss_sal_m3.append(p)
    input_rails_xy = [rvdd_sal_m3, rvss_sal_m3]
    rvdd_sal_m4, rvss_sal_m4 = laygenhelper.generate_power_rails_from_rails_xy(laygen, routename_tag='_SAL_M4_', 
                layer=laygen.layers['metal'][4], gridname=rg_m3m4_thick, netnames=['VDD', 'VSS'], direction='x', 
                input_rails_xy=input_rails_xy, generate_pin=False, overwrite_start_coord=0, overwrite_end_coord=None,
                offset_start_index=0, offset_end_index=0)
    input_rails_rect = [rvdd_sal_m4+rvdd_cdrvl_m4, rvss_sal_m4+rvss_cdrvl_m4]
    rvdd_sal_m5, rvss_sal_m5 = laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_SAL_M5_', 
                layer=laygen.layers['metal'][5], gridname=rg_m4m5_thick, netnames=['VDD', 'VSS'], direction='y', 
                input_rails_rect=input_rails_rect, generate_pin=False, overwrite_start_coord=0, overwrite_end_coord=None,
                offset_start_index=1, offset_end_index=-5)
    #sa_right_m4_m5
    x1 = laygen.get_inst_xy(name=isa.name, gridname=rg_m3m4_thick)[0]\
         +laygen.get_template_size(name=isa.cellname, gridname=rg_m3m4_thick, libname=workinglib)[0]
    rvdd_sar_m3=[]
    rvss_sar_m3=[]
    for pn, p in pdict_m3m4_thick[isa.name].items():
        if pn.startswith('VDDR'):
            rvdd_sar_m3.append(p)
        if pn.startswith('VSSR'):
            rvss_sar_m3.append(p)
    input_rails_xy = [rvdd_sar_m3, rvss_sar_m3]
    rvdd_sar_m4, rvss_sar_m4 = laygenhelper.generate_power_rails_from_rails_xy(laygen, routename_tag='_SAR_M4_', 
                layer=laygen.layers['metal'][4], gridname=rg_m3m4_thick, netnames=['VDD', 'VSS'], direction='x', 
                input_rails_xy=input_rails_xy, generate_pin=False, overwrite_start_coord=None, overwrite_end_coord=x1,
                offset_start_index=0, offset_end_index=0)
    input_rails_rect = [rvdd_sar_m4+rvdd_cdrvr_m4, rvss_sar_m4+rvss_cdrvr_m4]
    rvdd_sar_m5, rvss_sar_m5 = laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_SAR_M5_', 
                layer=laygen.layers['metal'][5], gridname=rg_m4m5_thick, netnames=['VDD', 'VSS'], direction='y', 
                input_rails_rect=input_rails_rect, generate_pin=False, overwrite_start_coord=0, overwrite_end_coord=None,
                offset_start_index=5, offset_end_index=0)
    #sa_m6
    x1 = laygen.get_inst_xy(name=isa.name, gridname=rg_m5m6_thick)[0]\
         +laygen.get_template_size(name=isa.cellname, gridname=rg_m5m6_thick, libname=workinglib)[0]
    y1 = laygen.get_inst_xy(name=isa.name, gridname=rg_m5m6_thick)[1]
    input_rails_rect = [rvdd_sal_m5+rvdd_sar_m5, rvss_sal_m5+rvss_sar_m5]
    rvdd_sa_m6, rvss_sa_m6 = laygenhelper.generate_power_rails_from_rails_rect(laygen, routename_tag='_M6_', 
                layer=laygen.layers['pin'][6], gridname=rg_m5m6_thick, netnames=['VDD', 'VSS'], direction='x', 
                input_rails_rect=input_rails_rect, generate_pin=True, overwrite_start_coord=0, overwrite_end_coord=x1,
                offset_start_index=int(y1/2)+4, offset_end_index=-2+2)

    #pins
    laygen.pin(name='VREF<0>', layer=laygen.layers['pin'][4], xy=laygen.get_rect_xy(rvref0.name, rg_m4m5), gridname=rg_m4m5)
    laygen.pin(name='VREF<1>', layer=laygen.layers['pin'][4], xy=laygen.get_rect_xy(rvref1.name, rg_m4m5), gridname=rg_m4m5)
    laygen.pin(name='VREF<2>', layer=laygen.layers['pin'][4], xy=laygen.get_rect_xy(rvref2.name, rg_m4m5), gridname=rg_m4m5)
    t = laygen.templates.get_template(icdrvl.cellname, libname=workinglib)
    vref0vl_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvl.name, gridname=rg_m4m5), (2,1))\
                   + np.array([-1, 1]) * laygen.get_template_pin_coord(t.name, 'VREF_M5<0>', rg_m4m5, libname=workinglib)
    vref1vl_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvl.name, gridname=rg_m4m5), (2,1))\
                   + np.array([-1, 1]) * laygen.get_template_pin_coord(t.name, 'VREF_M5<1>', rg_m4m5, libname=workinglib)
    vref2vl_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvl.name, gridname=rg_m4m5), (2,1))\
                   + np.array([-1, 1]) * laygen.get_template_pin_coord(t.name, 'VREF_M5<2>', rg_m4m5, libname=workinglib)
    vref0vr_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m4m5), (2,1))\
                   + np.array([1, 1]) * laygen.get_template_pin_coord(t.name, 'VREF_M5<0>', rg_m4m5, libname=workinglib)
    vref1vr_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m4m5), (2,1))\
                   + np.array([1, 1]) * laygen.get_template_pin_coord(t.name, 'VREF_M5<1>', rg_m4m5, libname=workinglib)
    vref2vr_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m4m5), (2,1))\
                   + np.array([1, 1]) * laygen.get_template_pin_coord(t.name, 'VREF_M5<2>', rg_m4m5, libname=workinglib)
    laygen.pin(name='VREF_M5L<0>', layer=laygen.layers['pin'][5], xy=vref0vl_pin_xy, gridname=rg_m4m5, netname='VREF<0>')
    laygen.pin(name='VREF_M5L<1>', layer=laygen.layers['pin'][5], xy=vref1vl_pin_xy, gridname=rg_m4m5, netname='VREF<1>')
    laygen.pin(name='VREF_M5L<2>', layer=laygen.layers['pin'][5], xy=vref2vl_pin_xy, gridname=rg_m4m5, netname='VREF<2>')
    laygen.pin(name='VREF_M5R<0>', layer=laygen.layers['pin'][5], xy=vref0vr_pin_xy, gridname=rg_m4m5, netname='VREF<0>')
    laygen.pin(name='VREF_M5R<1>', layer=laygen.layers['pin'][5], xy=vref1vr_pin_xy, gridname=rg_m4m5, netname='VREF<1>')
    laygen.pin(name='VREF_M5R<2>', layer=laygen.layers['pin'][5], xy=vref2vr_pin_xy, gridname=rg_m4m5, netname='VREF<2>')

    laygen.create_boundary_pin_form_rect(rclkb, rg_m4m5, "CLKB", laygen.layers['pin'][5], size=4, direction='bottom')
    laygen.create_boundary_pin_form_rect(routp, rg_m4m5, "OUTP", laygen.layers['pin'][5], size=4, direction='bottom')
    laygen.create_boundary_pin_form_rect(routm, rg_m4m5, "OUTM", laygen.layers['pin'][5], size=4, direction='bottom')
    laygen.create_boundary_pin_form_rect(rosp, rg_m2m3, "OSP", laygen.layers['pin'][3], size=4, direction='bottom')
    laygen.create_boundary_pin_form_rect(rosm, rg_m2m3, "OSM", laygen.layers['pin'][3], size=4, direction='bottom')
    for i in range(num_bits):
        laygen.create_boundary_pin_form_rect(renl0[i], rg_m5m6, "ENL"+str(i)+"<0>", laygen.layers['pin'][5], size=4, direction='bottom')
        laygen.create_boundary_pin_form_rect(renl1[i], rg_m5m6, "ENL"+str(i)+"<1>", laygen.layers['pin'][5], size=4, direction='bottom')
        laygen.create_boundary_pin_form_rect(renl2[i], rg_m5m6, "ENL"+str(i)+"<2>", laygen.layers['pin'][5], size=4, direction='bottom')
        laygen.create_boundary_pin_form_rect(renr0[i], rg_m5m6, "ENR"+str(i)+"<0>", laygen.layers['pin'][5], size=4, direction='bottom')
        laygen.create_boundary_pin_form_rect(renr1[i], rg_m5m6, "ENR"+str(i)+"<1>", laygen.layers['pin'][5], size=4, direction='bottom')
        laygen.create_boundary_pin_form_rect(renr2[i], rg_m5m6, "ENR"+str(i)+"<2>", laygen.layers['pin'][5], size=4, direction='bottom')

    laygen.pin_from_rect(name='SAINP', layer=laygen.layers['pin'][4], rect=rsainp, gridname=rg_m4m5, netname='INP')
    laygen.pin_from_rect(name='SAINM', layer=laygen.layers['pin'][4], rect=rsainm, gridname=rg_m4m5, netname='INM')
    for i, r in enumerate(rinp):
        laygen.create_boundary_pin_form_rect(r, rg_m5m6, "INP"+str(i), laygen.layers['pin'][6], size=8, direction='right', netname="INP")
    for i, r in enumerate(rinm):
        laygen.create_boundary_pin_form_rect(r, rg_m5m6, "INM"+str(i), laygen.layers['pin'][6], size=8, direction='left', netname="INM")
def generate_sarafe_nsw(laygen,
                        objectname_pfix,
                        workinglib,
                        placement_grid,
                        routing_grid_m2m3_thick,
                        routing_grid_m3m4_thick,
                        routing_grid_m4m5_thick,
                        routing_grid_m5m6,
                        routing_grid_m5m6_thick,
                        routing_grid_m5m6_basic_thick,
                        routing_grid_m6m7,
                        num_bits=8,
                        num_bits_vertical=6,
                        num_cdrv_output_routes=2,
                        m_sa=8,
                        origin=np.array([0, 0])):
    """generate sar analog frontend """
    pg = placement_grid

    rg_m3m4_thick = routing_grid_m3m4_thick
    rg_m5m6 = routing_grid_m5m6
    rg_m6m7 = routing_grid_m6m7

    tap_name = 'tap'
    cdrv_name = 'capdrv_nsw_array'
    #cdac_name='capdac_'+str(num_bits)+'b'
    cdac_name = 'capdac'
    sa_name = 'doubleSA_pmos'

    # placement
    xy0 = origin + (laygen.get_template_size(
        cdrv_name, gridname=pg, libname=workinglib) * np.array([1, 0]))
    icdrvl = laygen.place(name="I" + objectname_pfix + 'CDRVL0',
                          templatename=cdrv_name,
                          gridname=pg,
                          xy=xy0,
                          template_libname=workinglib,
                          transform='MY')
    icdrvr = laygen.place(name="I" + objectname_pfix + 'CDRVR0',
                          templatename=cdrv_name,
                          gridname=pg,
                          xy=xy0,
                          template_libname=workinglib)
    xy0 = origin + laygen.get_template_size(cdrv_name, gridname=pg, libname=workinglib)*np.array([0, 1]) \
                 + laygen.get_template_size(sa_name, gridname=pg, libname=workinglib) * np.array([0, 1])
    isa = laygen.place(name="I" + objectname_pfix + 'SA0',
                       templatename=sa_name,
                       gridname=pg,
                       xy=xy0,
                       template_libname=workinglib,
                       transform='MX')
    xy0 = origin + laygen.get_template_size(cdrv_name, gridname=pg, libname=workinglib)*np.array([0, 1]) \
                 + laygen.get_template_size(sa_name, gridname=pg, libname=workinglib) * np.array([0, 1]) \
                 + laygen.get_template_size(cdac_name, gridname=pg, libname=workinglib)*np.array([1, 0])
    icdacl = laygen.place(name="I" + objectname_pfix + 'CDACL0',
                          templatename=cdac_name,
                          gridname=pg,
                          xy=xy0,
                          template_libname=workinglib,
                          transform='MY')
    xy0 = origin + laygen.get_template_size(cdrv_name, gridname=pg, libname=workinglib)*np.array([2, 1]) \
                 + laygen.get_template_size(sa_name, gridname=pg, libname=workinglib) * np.array([0, 1]) \
                 - laygen.get_template_size(cdac_name, gridname=pg, libname=workinglib)*np.array([1, 0])
    icdacr = laygen.place(name="I" + objectname_pfix + 'CDACR0',
                          templatename=cdac_name,
                          gridname=pg,
                          xy=xy0,
                          template_libname=workinglib)

    # pin informations
    pdict_m3m4_thick = laygen.get_inst_pin_xy(None, None, rg_m3m4_thick)

    # internal pins
    icdrvl_vo_xy = []
    icdacl_i_xy = []
    icdacl_i2_xy = []
    icdrvr_vo_xy = []
    icdacr_i_xy = []
    icdacr_i2_xy = []

    icdrvl_vo_c0_xy = laygen.get_inst_pin_xy(icdrvl.name, 'VO_C0', rg_m5m6)
    icdacl_i_c0_xy = laygen.get_inst_pin_xy(icdacl.name, 'I_C0', rg_m5m6)
    icdrvr_vo_c0_xy = laygen.get_inst_pin_xy(icdrvr.name, 'VO_C0', rg_m5m6)
    icdacr_i_c0_xy = laygen.get_inst_pin_xy(icdacr.name, 'I_C0', rg_m5m6)
    for i in range(num_bits):
        icdacl_i_xy.append(
            laygen.get_inst_pin_xy(icdacl.name, 'I<' + str(i) + '>', rg_m4m5))
        icdacr_i_xy.append(
            laygen.get_inst_pin_xy(icdacr.name, 'I<' + str(i) + '>', rg_m4m5))
        if i >= num_bits_vertical:
            icdacl_i2_xy.append(
                laygen.get_inst_pin_xy(icdacl.name, 'I2<' + str(i) + '>',
                                       rg_m4m5))
            icdacr_i2_xy.append(
                laygen.get_inst_pin_xy(icdacr.name, 'I2<' + str(i) + '>',
                                       rg_m4m5))

    for j in range(num_cdrv_output_routes):
        for i in range(num_bits):
            icdrvl_vo_xy.append(
                laygen.get_inst_pin_xy(icdrvl.name,
                                       'VO' + str(j) + '<' + str(i) + '>',
                                       rg_m5m6))
            icdrvr_vo_xy.append(
                laygen.get_inst_pin_xy(icdrvr.name,
                                       'VO' + str(j) + '<' + str(i) + '>',
                                       rg_m5m6))

    #route
    #capdrv to capdac
    #y0 = origin[1] + laygen.get_template_size(cdrv_name, gridname=rg_m5m6, libname=workinglib)[1]-2 #refer to capdrv
    y0 = origin[1] + laygen.get_template_size(cdrv_name, gridname=rg_m4m5, libname=workinglib)[1] \
         + laygen.get_template_size(sa_name, gridname=rg_m4m5, libname=workinglib)[1]-4 #refer to sa
    [rv0, rh0, rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                       laygen.layers['metal'][6],
                                       icdrvl_vo_c0_xy[0],
                                       icdacl_i_c0_xy[0],
                                       y0 + 1,
                                       rg_m5m6,
                                       layerv1=laygen.layers['metal'][5],
                                       gridname1=rg_m5m6)
    laygen.boundary_pin_from_rect(rv0,
                                  rg_m4m5,
                                  "VOL_C0",
                                  laygen.layers['pin'][5],
                                  size=4,
                                  direction='bottom',
                                  netname='VREF<1>')
    [rv0, rh0, rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                       laygen.layers['metal'][6],
                                       icdrvr_vo_c0_xy[0],
                                       icdacr_i_c0_xy[0],
                                       y0 + 1,
                                       rg_m5m6,
                                       layerv1=laygen.layers['metal'][5],
                                       gridname1=rg_m5m6)
    laygen.boundary_pin_from_rect(rv0,
                                  rg_m4m5,
                                  "VOR_C0",
                                  laygen.layers['pin'][5],
                                  size=4,
                                  direction='bottom',
                                  netname='VREF<1>')

    for j in range(num_cdrv_output_routes):
        for i in range(num_bits):
            if i in range(num_bits_vertical) and i % 2 == (num_bits_vertical +
                                                           1) % 2:
                #if not i%2==num_bits_vertical%2:
                [rv0l, rh0,
                 rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                         laygen.layers['metal'][4],
                                         icdrvl_vo_xy[i + j * num_bits][0],
                                         icdacl_i_xy[i][0],
                                         y0 - i - 1,
                                         rg_m4m5,
                                         layerv1=laygen.layers['metal'][3],
                                         gridname1=rg_m3m4)
                [rv0r, rh0,
                 rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                         laygen.layers['metal'][4],
                                         icdrvr_vo_xy[i + j * num_bits][0],
                                         icdacr_i_xy[i][0],
                                         y0 - i - 1,
                                         rg_m4m5,
                                         layerv1=laygen.layers['metal'][3],
                                         gridname1=rg_m3m4)
            else:
                [rv0l, rh0,
                 rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                         laygen.layers['metal'][6],
                                         icdrvl_vo_xy[i + j * num_bits][0],
                                         icdacl_i_xy[i][0],
                                         y0 - i - 1,
                                         rg_m5m6,
                                         layerv1=laygen.layers['metal'][5],
                                         gridname1=rg_m5m6)
                [rv0r, rh0,
                 rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                         laygen.layers['metal'][6],
                                         icdrvr_vo_xy[i + j * num_bits][0],
                                         icdacr_i_xy[i][0],
                                         y0 - i - 1,
                                         rg_m5m6,
                                         layerv1=laygen.layers['metal'][5],
                                         gridname1=rg_m5m6)
            laygen.boundary_pin_from_rect(rv0l,
                                          rg_m4m5,
                                          "VOL<" + str(i) + ">",
                                          laygen.layers['pin'][5],
                                          size=4,
                                          direction='bottom')
            laygen.boundary_pin_from_rect(rv0r,
                                          rg_m4m5,
                                          "VOR<" + str(i) + ">",
                                          laygen.layers['pin'][5],
                                          size=4,
                                          direction='bottom')
            #more routes for horizontal dacs
            if i >= num_bits_vertical:
                [rv0, rh0,
                 rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                         laygen.layers['metal'][4],
                                         icdrvl_vo_xy[i + j * num_bits][0],
                                         icdacl_i_xy[i][0],
                                         y0 + 2 + i - num_bits_vertical,
                                         rg_m4m5,
                                         layerv1=laygen.layers['metal'][5],
                                         gridname1=rg_m4m5)
                [rv0, rh0,
                 rv1] = laygen.route_vhv(laygen.layers['metal'][5],
                                         laygen.layers['metal'][4],
                                         icdrvr_vo_xy[i + j * num_bits][0],
                                         icdacr_i_xy[i][0],
                                         y0 + 2 + i - num_bits_vertical,
                                         rg_m4m5,
                                         layerv1=laygen.layers['metal'][5],
                                         gridname1=rg_m4m5)
                [rv0, rh0, rv1
                 ] = laygen.route_vhv(laygen.layers['metal'][5],
                                      laygen.layers['metal'][4],
                                      icdrvl_vo_xy[i + j * num_bits][0],
                                      icdacl_i2_xy[i - num_bits_vertical][0],
                                      y0 + 2 + i - num_bits_vertical,
                                      rg_m4m5,
                                      layerv1=laygen.layers['metal'][5],
                                      gridname1=rg_m4m5)
                [rv0, rh0, rv1
                 ] = laygen.route_vhv(laygen.layers['metal'][5],
                                      laygen.layers['metal'][4],
                                      icdrvr_vo_xy[i + j * num_bits][0],
                                      icdacr_i2_xy[i - num_bits_vertical][0],
                                      y0 + 2 + i - num_bits_vertical,
                                      rg_m4m5,
                                      layerv1=laygen.layers['metal'][5],
                                      gridname1=rg_m4m5)

    #vref
    rvref0 = laygen.route(None,
                          laygen.layers['metal'][4],
                          xy0=np.array([0, 0]),
                          xy1=np.array([0, 0]),
                          refinstname0=icdrvl.name,
                          refpinname0='VREF<0>',
                          gridname0=rg_m4m5,
                          refinstname1=icdrvr.name,
                          refpinname1='VREF<0>')
    rvref1 = laygen.route(None,
                          laygen.layers['metal'][4],
                          xy0=np.array([0, 0]),
                          xy1=np.array([0, 0]),
                          refinstname0=icdrvl.name,
                          refpinname0='VREF<1>',
                          gridname0=rg_m4m5,
                          refinstname1=icdrvr.name,
                          refpinname1='VREF<1>')
    rvref2 = laygen.route(None,
                          laygen.layers['metal'][4],
                          xy0=np.array([0, 0]),
                          xy1=np.array([0, 0]),
                          refinstname0=icdrvl.name,
                          refpinname0='VREF<2>',
                          gridname0=rg_m4m5,
                          refinstname1=icdrvr.name,
                          refpinname1='VREF<2>')
    #input pins
    #y0 = laygen.get_inst_pin_xy(icdrvl.name, 'EN0<0>', rg_m4m5, index=np.array([0, 0]), sort=True)[0][1]
    y0 = 0
    rclkb = laygen.route(None,
                         laygen.layers['metal'][5],
                         xy0=np.array([0, 0]),
                         xy1=np.array([0, 0]),
                         refinstname0=isa.name,
                         refpinname0='CLKB',
                         gridname0=rg_m4m5,
                         direction='y')
    routp = laygen.route(None,
                         laygen.layers['metal'][5],
                         xy0=np.array([0, 0]),
                         xy1=np.array([0, 0]),
                         refinstname0=isa.name,
                         refpinname0='OUTP',
                         gridname0=rg_m4m5,
                         direction='y')
    routm = laygen.route(None,
                         laygen.layers['metal'][5],
                         xy0=np.array([0, 0]),
                         xy1=np.array([0, 0]),
                         refinstname0=isa.name,
                         refpinname0='OUTM',
                         gridname0=rg_m4m5,
                         direction='y')
    #rosp=laygen.route(None, laygen.layers['metal'][3], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
    #                  refinstname0=isa.name, refpinname0='OSP', gridname0=rg_m2m3, direction='y')
    #rosm=laygen.route(None, laygen.layers['metal'][3], xy0=np.array([0, 0]), xy1=np.array([0, 0]),
    #                  refinstname0=isa.name, refpinname0='OSM', gridname0=rg_m2m3, direction='y')
    pdict_m3m4 = laygen.get_inst_pin_xy(None, None, rg_m3m4)
    yos=laygen.get_inst_xy(isa.name, rg_m3m4)[1] \
        - laygen.get_template_size(name=isa.cellname, gridname=rg_m3m4, libname=workinglib)[1]
    [rv0, rh0, rosp] = laygen.route_vhv(
        laygen.layers['metal'][3], laygen.layers['metal'][4],
        pdict_m3m4[isa.name]['OSP'][0],
        np.array([pdict_m3m4[isa.name]['OSP'][0][0] + m_sa, 0]), yos, rg_m3m4)
    [rv0, rh0, rosm] = laygen.route_vhv(
        laygen.layers['metal'][3], laygen.layers['metal'][4],
        pdict_m3m4[isa.name]['OSM'][0],
        np.array([pdict_m3m4[isa.name]['OSM'][0][0] - m_sa, 0]), yos, rg_m3m4)
    renl0 = []
    renl1 = []
    renl2 = []
    renr0 = []
    renr1 = []
    renr2 = []
    for i in range(num_bits):
        renl0.append(
            laygen.route(None,
                         laygen.layers['metal'][5],
                         xy0=np.array([0, 0]),
                         xy1=np.array([0, 0]),
                         refinstname0=icdrvl.name,
                         refpinname0='EN' + str(i) + '<0>',
                         gridname0=rg_m5m6,
                         direction='y'))
        renl1.append(
            laygen.route(None,
                         laygen.layers['metal'][5],
                         xy0=np.array([0, 0]),
                         xy1=np.array([0, 0]),
                         refinstname0=icdrvl.name,
                         refpinname0='EN' + str(i) + '<1>',
                         gridname0=rg_m5m6,
                         direction='y'))
        renl2.append(
            laygen.route(None,
                         laygen.layers['metal'][5],
                         xy0=np.array([0, 0]),
                         xy1=np.array([0, 0]),
                         refinstname0=icdrvl.name,
                         refpinname0='EN' + str(i) + '<2>',
                         gridname0=rg_m5m6,
                         direction='y'))
        renr0.append(
            laygen.route(None,
                         laygen.layers['metal'][5],
                         xy0=np.array([0, 0]),
                         xy1=np.array([0, 0]),
                         refinstname0=icdrvr.name,
                         refpinname0='EN' + str(i) + '<0>',
                         gridname0=rg_m5m6,
                         direction='y'))
        renr1.append(
            laygen.route(None,
                         laygen.layers['metal'][5],
                         xy0=np.array([0, 0]),
                         xy1=np.array([0, 0]),
                         refinstname0=icdrvr.name,
                         refpinname0='EN' + str(i) + '<1>',
                         gridname0=rg_m5m6,
                         direction='y'))
        renr2.append(
            laygen.route(None,
                         laygen.layers['metal'][5],
                         xy0=np.array([0, 0]),
                         xy1=np.array([0, 0]),
                         refinstname0=icdrvr.name,
                         refpinname0='EN' + str(i) + '<2>',
                         gridname0=rg_m5m6,
                         direction='y'))
    #inp/inm
    pdict_m3m4 = laygen.get_inst_pin_xy(None, None, rg_m3m4)
    outcnt = 0
    for pn in pdict_m3m4[icdacl.name]:
        if pn.startswith('O'):  #out pin
            outcnt += 1
    x0 = laygen.get_inst_xy(icdacl.name, rg_m3m4)[0] - 8
    x1 = laygen.get_inst_xy(icdacr.name, rg_m3m4)[0] + 8
    nrin_sa = 4  # number of M6 horizontal route stacks for cdac to sa
    nrin = 2**num_bits_vertical - 2 * nrin_sa  # number of M6 horizontal route stacks
    if nrin < 1:
        nrin = 2**num_bits_vertical - nrin_sa
    rinp = []
    rinm = []
    for i in range(nrin):
        xy0 = laygen.get_inst_pin_xy(icdacl.name,
                                     "O" + str(outcnt - 1 - i),
                                     rg_m3m4,
                                     index=np.array([0, 0]),
                                     sort=True)[0]
        r = laygen.route(None,
                         laygen.layers['metal'][4],
                         xy0=xy0,
                         xy1=np.array([x0, xy0[1]]),
                         gridname0=rg_m3m4)
        rinp.append(r)
        '''
        #additional routes for dummy for density rules; may not process portable
        for j in range(3):
            laygen.route(None, laygen.layers['metal'][6], xy0=xy0+np.array([0, 2*j+2]), xy1=np.array([x0, xy0[1]+2*j+2]), gridname0=rg_m5m6)
        '''
        xy0 = laygen.get_inst_pin_xy(icdacr.name,
                                     "O" + str(outcnt - 1 - i),
                                     rg_m3m4,
                                     index=np.array([0, 0]),
                                     sort=True)[1]
        r = laygen.route(None,
                         laygen.layers['metal'][4],
                         xy0=xy0,
                         xy1=np.array([x1, xy0[1]]),
                         gridname0=rg_m3m4)
        rinm.append(r)
        '''
        #additional routes for dummy for density rules; may not process portable
        for j in range(3):
            laygen.route(None, laygen.layers['metal'][6], xy0=xy0+np.array([0, 2*j+2]), xy1=np.array([x1, xy0[1]+2*j+2]), gridname0=rg_m5m6)
        '''

    for i in range(nrin_sa):
        xy0 = laygen.get_inst_pin_xy(icdacl.name,
                                     "O" + str(i),
                                     rg_m3m4,
                                     index=np.array([0, 0]),
                                     sort=True)[0]
        laygen.route(None,
                     laygen.layers['metal'][4],
                     xy0=xy0,
                     xy1=np.array([x0, xy0[1]]),
                     gridname0=rg_m4m5)
        for j in range(4):
            laygen.via(None, [x0 - 2 * j, xy0[1]], rg_m4m5)
        xy0 = laygen.get_inst_pin_xy(icdacr.name,
                                     "O" + str(i),
                                     rg_m3m4,
                                     index=np.array([0, 0]),
                                     sort=True)[1]
        laygen.route(None,
                     laygen.layers['metal'][4],
                     xy0=xy0,
                     xy1=np.array([x1, xy0[1]]),
                     gridname0=rg_m4m5)
        for j in range(4):
            laygen.via(None, [x1 + 2 * j, xy0[1]], rg_m4m5)
    xy0 = laygen.get_inst_pin_xy(isa.name,
                                 "INP",
                                 rg_m3m4,
                                 index=np.array([0, 0]),
                                 sort=True)[0]
    xy1 = laygen.get_inst_pin_xy(icdacl.name,
                                 "O" + str(nrin_sa - 1),
                                 rg_m4m5,
                                 index=np.array([0, 0]),
                                 sort=True)[0]
    for j in range(4):
        laygen.route(None,
                     laygen.layers['metal'][5],
                     xy0=np.array([x0 - 2 * j, xy0[1]]),
                     xy1=np.array([x0 - 2 * j, xy1[1]]),
                     gridname0=rg_m5m6)
    xy0 = laygen.get_inst_pin_xy(isa.name,
                                 "INM",
                                 rg_m4m5,
                                 index=np.array([0, 0]),
                                 sort=True)[0]
    xy1 = laygen.get_inst_pin_xy(icdacr.name,
                                 "O" + str(nrin_sa - 1),
                                 rg_m5m6,
                                 index=np.array([0, 0]),
                                 sort=True)[0]
    for j in range(4):
        laygen.route(None,
                     laygen.layers['metal'][5],
                     xy0=np.array([x1 + 2 * j, xy0[1]]),
                     xy1=np.array([x1 + 2 * j, xy1[1]]),
                     gridname0=rg_m5m6)
    #inp/inm - sa to capdac
    xy0 = laygen.get_inst_pin_xy(isa.name,
                                 "INP",
                                 rg_m4m5,
                                 index=np.array([0, 0]),
                                 sort=True)[0]
    xy1 = laygen.get_inst_pin_xy(isa.name,
                                 "INM",
                                 rg_m4m5,
                                 index=np.array([0, 0]),
                                 sort=True)[0]
    rsainp = laygen.route(None,
                          laygen.layers['metal'][4],
                          xy0=np.array([x0 - 8, xy0[1]]),
                          xy1=xy0,
                          gridname0=rg_m4m5)
    rsainm = laygen.route(None,
                          laygen.layers['metal'][4],
                          xy0=np.array([x1 + 8, xy1[1]]),
                          xy1=xy1,
                          gridname0=rg_m4m5)
    for j in range(4):
        laygen.via(None, [x0 - 2 * j, xy0[1]], rg_m4m5)
        laygen.via(None, [x1 + 2 * j, xy1[1]], rg_m4m5)
    x0 = laygen.get_inst_xy(icdacl.name, rg_m3m4)[0] - 1
    x1 = laygen.get_inst_xy(icdacr.name, rg_m3m4)[0] + 1
    xy0 = laygen.get_inst_pin_xy(isa.name,
                                 "INP",
                                 rg_m3m4,
                                 index=np.array([0, 0]),
                                 sort=True)[0]
    xy1 = laygen.get_inst_pin_xy(isa.name,
                                 "INM",
                                 rg_m3m4,
                                 index=np.array([0, 0]),
                                 sort=True)[0]
    laygen.route(None,
                 laygen.layers['metal'][4],
                 xy0=np.array([x0, xy0[1]]),
                 xy1=xy0,
                 gridname0=rg_m3m4,
                 via1=[[0, 0]])
    laygen.route(None,
                 laygen.layers['metal'][4],
                 xy0=np.array([x1, xy1[1]]),
                 xy1=xy1,
                 gridname0=rg_m3m4,
                 via1=[[0, 0]])

    #vdd/vss - route
    #cdrv_left_m4
    rvdd_cdrvl_m3 = []
    rvss_cdrvl_m3 = []
    for pn, p in pdict_m3m4_thick[icdrvl.name].items():
        if pn.startswith('VSSR'):
            rvss_cdrvl_m3.append(p)
    input_rails_xy = [rvss_cdrvl_m3]
    rvss_cdrvl_m4 = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_CDRVL_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VSS'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=0)
    rvss_cdrvl_m4 = rvss_cdrvl_m4[0]
    #cdrv_right_m4
    x1 = laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m3m4_thick)[0]\
         +laygen.get_template_size(name=icdrvr.cellname, gridname=rg_m3m4_thick, libname=workinglib)[0]
    rvdd_cdrvr_m3 = []
    rvss_cdrvr_m3 = []
    for pn, p in pdict_m3m4_thick[icdrvr.name].items():
        if pn.startswith('VSSR'):
            rvss_cdrvr_m3.append(p)
    input_rails_xy = [rvss_cdrvr_m3]
    rvss_cdrvr_m4 = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_CDRVR_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VSS'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=x1,
        offset_start_index=0,
        offset_end_index=0)
    rvss_cdrvr_m4 = rvss_cdrvr_m4[0]
    #sa_left_m4_m5
    rvdd_sal_m3 = []
    rvss_sal_m3 = []
    for pn, p in pdict_m3m4_thick[isa.name].items():
        if pn.startswith('VDDL'):
            rvdd_sal_m3.append(p)
        if pn.startswith('VSSL'):
            rvss_sal_m3.append(p)
    input_rails_xy = [rvdd_sal_m3, rvss_sal_m3]
    rvdd_sal_m4, rvss_sal_m4 = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_SAL_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        offset_start_index=0,
        offset_end_index=-4)
    #input_rails_rect = [rvdd_sal_m4+rvdd_cdrvl_m4, rvss_sal_m4+rvss_cdrvl_m4]
    input_rails_rect = [rvdd_sal_m4, rvss_sal_m4 + rvss_cdrvl_m4]
    rvdd_sal_m5, rvss_sal_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_SAL_M5_',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        offset_start_index=1,
        offset_end_index=-6)
    #sa_right_m4_m5
    x1 = laygen.get_inst_xy(name=isa.name, gridname=rg_m3m4_thick)[0]\
         +laygen.get_template_size(name=isa.cellname, gridname=rg_m3m4_thick, libname=workinglib)[0]
    rvdd_sar_m3 = []
    rvss_sar_m3 = []
    for pn, p in pdict_m3m4_thick[isa.name].items():
        if pn.startswith('VDDR'):
            rvdd_sar_m3.append(p)
        if pn.startswith('VSSR'):
            rvss_sar_m3.append(p)
    input_rails_xy = [rvdd_sar_m3, rvss_sar_m3]
    rvdd_sar_m4, rvss_sar_m4 = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_SAR_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=None,
        overwrite_end_coord=x1,
        offset_start_index=0,
        offset_end_index=-4)
    #input_rails_rect = [rvdd_sar_m4+rvdd_cdrvr_m4, rvss_sar_m4+rvss_cdrvr_m4]
    input_rails_rect = [rvdd_sar_m4, rvss_sar_m4 + rvss_cdrvr_m4]
    rvdd_sar_m5, rvss_sar_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_SAR_M5_',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=None,
        offset_start_index=8,
        offset_end_index=0)
    #sa_m6
    num_vref_routes_m6 = 4
    x1 = laygen.get_inst_xy(name=isa.name, gridname=rg_m5m6_thick)[0]\
         +laygen.get_template_size(name=isa.cellname, gridname=rg_m5m6_thick, libname=workinglib)[0]
    x1_phy = laygen.get_inst_xy(name=isa.name)[0]\
         +laygen.get_template_size(name=isa.cellname, libname=workinglib)[0]
    y1 = laygen.get_inst_xy(name=isa.name, gridname=rg_m5m6_thick)[1]
    input_rails_rect = [rvdd_sal_m5 + rvdd_sar_m5, rvss_sal_m5 + rvss_sar_m5]
    #sa_m6_bottom_shield
    rvdd_sa_m6, rvss_sa_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_0_',
        layer=laygen.layers['metal'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=x1,
        overwrite_start_index=2,
        overwrite_end_index=4)
    #trimming
    for r in rvdd_sa_m6:
        r.xy1[0] = x1_phy
    for r in rvss_sa_m6:
        r.xy1[0] = x1_phy
    #sa_m6_mid and top_shield
    for i in range(3):
        rvss_sa_m6 = laygenhelper.generate_power_rails_from_rails_rect(
            laygen,
            routename_tag='_M6_' + str(i + 1) + '_',
            layer=laygen.layers['metal'][6],
            gridname=rg_m5m6_thick,
            netnames=['VSS'],
            direction='x',
            input_rails_rect=[rvss_sal_m5 + rvss_sar_m5],
            generate_pin=False,
            overwrite_start_coord=0,
            overwrite_end_coord=x1,
            overwrite_start_index=4 + (1 + num_vref_routes_m6) * (i + 1) - 1,
            overwrite_num_routes=0
        )  #overwrite_end_index=4+(1+num_vref_routes_m6)*(i+1))
        #trimming
        for r in rvss_sa_m6[0]:
            r.xy1[0] = x1_phy
    #sa_m6_top_main_route
    rvdd_sa_m6, rvss_sa_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_',
        layer=laygen.layers['metal'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=x1,
        offset_start_index=4 + (1 + num_vref_routes_m6) * 3,
        offset_end_index=0)
    #trimming and pinning
    for r in rvdd_sa_m6:
        r.xy1[0] = x1_phy
        p = laygen.pin_from_rect(name='VDD_M6_' + r.name,
                                 layer=laygen.layers['pin'][6],
                                 rect=r,
                                 gridname=rg_m5m6_thick,
                                 netname='VDD')
        p.xy1[0] = x1_phy
    for r in rvss_sa_m6:
        r.xy1[0] = x1_phy
        p = laygen.pin_from_rect(name='VSS_M6_' + r.name,
                                 layer=laygen.layers['pin'][6],
                                 rect=r,
                                 gridname=rg_m5m6_thick,
                                 netname='VSS')
        p.xy1[0] = x1_phy
    #pins
    #laygen.pin(name='VREF<0>', layer=laygen.layers['pin'][4], xy=laygen.get_rect_xy(rvref0.name, rg_m4m5), gridname=rg_m4m5)
    #laygen.pin(name='VREF<1>', layer=laygen.layers['pin'][4], xy=laygen.get_rect_xy(rvref1.name, rg_m4m5), gridname=rg_m4m5)
    #laygen.pin(name='VREF<2>', layer=laygen.layers['pin'][4], xy=laygen.get_rect_xy(rvref2.name, rg_m4m5), gridname=rg_m4m5)
    '''
    t = laygen.templates.get_template(icdrvl.cellname, libname=workinglib)
    vref0vl_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvl.name, gridname=rg_m4m5), (2,1))\
                   + np.array([-1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5<0>', rg_m4m5, libname=workinglib)
    vref1vl_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvl.name, gridname=rg_m4m5), (2,1))\
                   + np.array([-1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5<1>', rg_m4m5, libname=workinglib)
    vref2vl_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvl.name, gridname=rg_m4m5), (2,1))\
                   + np.array([-1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5<2>', rg_m4m5, libname=workinglib)
    vref0vr_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m4m5), (2,1))\
                   + np.array([1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5<0>', rg_m4m5, libname=workinglib)
    vref1vr_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m4m5), (2,1))\
                   + np.array([1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5<1>', rg_m4m5, libname=workinglib)
    vref2vr_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m4m5), (2,1))\
                   + np.array([1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5<2>', rg_m4m5, libname=workinglib)
    laygen.pin(name='VREF_M5L<0>', layer=laygen.layers['pin'][5], xy=vref0vl_pin_xy, gridname=rg_m4m5, netname='VREF<0>')
    laygen.pin(name='VREF_M5L<1>', layer=laygen.layers['pin'][5], xy=vref1vl_pin_xy, gridname=rg_m4m5, netname='VREF<1>')
    laygen.pin(name='VREF_M5L<2>', layer=laygen.layers['pin'][5], xy=vref2vl_pin_xy, gridname=rg_m4m5, netname='VREF<2>')
    laygen.pin(name='VREF_M5R<0>', layer=laygen.layers['pin'][5], xy=vref0vr_pin_xy, gridname=rg_m4m5, netname='VREF<0>')
    laygen.pin(name='VREF_M5R<1>', layer=laygen.layers['pin'][5], xy=vref1vr_pin_xy, gridname=rg_m4m5, netname='VREF<1>')
    laygen.pin(name='VREF_M5R<2>', layer=laygen.layers['pin'][5], xy=vref2vr_pin_xy, gridname=rg_m4m5, netname='VREF<2>')
    vref0vl_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvl.name, gridname=rg_m4m5), (2,1))\
                   + np.array([-1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5_2<0>', rg_m4m5, libname=workinglib)
    vref1vl_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvl.name, gridname=rg_m4m5), (2,1))\
                   + np.array([-1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5_2<1>', rg_m4m5, libname=workinglib)
    vref2vl_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvl.name, gridname=rg_m4m5), (2,1))\
                   + np.array([-1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5_2<2>', rg_m4m5, libname=workinglib)
    vref0vr_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m4m5), (2,1))\
                   + np.array([1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5_2<0>', rg_m4m5, libname=workinglib)
    vref1vr_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m4m5), (2,1))\
                   + np.array([1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5_2<1>', rg_m4m5, libname=workinglib)
    vref2vr_pin_xy=np.tile(laygen.get_inst_xy(name=icdrvr.name, gridname=rg_m4m5), (2,1))\
                   + np.array([1, 1]) * laygen.get_template_pin_xy(t.name, 'VREF_M5_2<2>', rg_m4m5, libname=workinglib)
    laygen.pin(name='VREF_M5L_2<0>', layer=laygen.layers['pin'][5], xy=vref0vl_pin_xy, gridname=rg_m4m5, netname='VREF<0>')
    laygen.pin(name='VREF_M5L_2<1>', layer=laygen.layers['pin'][5], xy=vref1vl_pin_xy, gridname=rg_m4m5, netname='VREF<1>')
    laygen.pin(name='VREF_M5L_2<2>', layer=laygen.layers['pin'][5], xy=vref2vl_pin_xy, gridname=rg_m4m5, netname='VREF<2>')
    laygen.pin(name='VREF_M5R_2<0>', layer=laygen.layers['pin'][5], xy=vref0vr_pin_xy, gridname=rg_m4m5, netname='VREF<0>')
    laygen.pin(name='VREF_M5R_2<1>', layer=laygen.layers['pin'][5], xy=vref1vr_pin_xy, gridname=rg_m4m5, netname='VREF<1>')
    laygen.pin(name='VREF_M5R_2<2>', layer=laygen.layers['pin'][5], xy=vref2vr_pin_xy, gridname=rg_m4m5, netname='VREF<2>')
    '''
    pdict_vref = laygen.get_inst_pin_xy(None, None, rg_m5m6_basic_thick)
    x1 = laygen.get_inst_xy(name=isa.name, gridname=rg_m5m6_basic_thick)[0]\
         +laygen.get_template_size(name=isa.cellname, gridname=rg_m5m6_basic_thick, libname=workinglib)[0]
    for i in range(3):
        laygen.route(
            None,
            laygen.layers['metal'][5],
            gridname0=rg_m5m6_basic_thick,
            xy0=pdict_vref[icdrvl.name]['VREF_M5<' + str(i) + '>'][0],
            xy1=pdict_vref[icdrvl.name]['VREF_M5<' + str(i) + '>'][0] +
            np.array([0, (num_vref_routes_m6 + 1) * 3 + 4]))
        laygen.route(
            None,
            laygen.layers['metal'][5],
            gridname0=rg_m5m6_basic_thick,
            xy0=pdict_vref[icdrvr.name]['VREF_M5<' + str(i) + '>'][0],
            xy1=pdict_vref[icdrvr.name]['VREF_M5<' + str(i) + '>'][0] +
            np.array([0, (num_vref_routes_m6 + 1) * 3 + 4]))
        laygen.route(
            None,
            laygen.layers['metal'][5],
            gridname0=rg_m5m6_basic_thick,
            xy0=pdict_vref[icdrvl.name]['VREF_M5_2<' + str(i) + '>'][0],
            xy1=pdict_vref[icdrvl.name]['VREF_M5_2<' + str(i) + '>'][0] +
            np.array([0, (num_vref_routes_m6 + 1) * 3 + 4]))
        laygen.route(
            None,
            laygen.layers['metal'][5],
            gridname0=rg_m5m6_basic_thick,
            xy0=pdict_vref[icdrvr.name]['VREF_M5_2<' + str(i) + '>'][0],
            xy1=pdict_vref[icdrvr.name]['VREF_M5_2<' + str(i) + '>'][0] +
            np.array([0, (num_vref_routes_m6 + 1) * 3 + 4]))
        input_rails_xy = [[
            pdict_vref[icdrvl.name]['VREF_M5<' + str(i) + '>'],
            pdict_vref[icdrvl.name]['VREF_M5_2<' + str(i) + '>'],
            pdict_vref[icdrvr.name]['VREF_M5<' + str(i) + '>'],
            pdict_vref[icdrvr.name]['VREF_M5_2<' + str(i) + '>']
        ]]
        laygenhelper.generate_power_rails_from_rails_xy(
            laygen,
            routename_tag='_M6_',
            layer=laygen.layers['pin'][6],
            gridname=rg_m5m6_basic_thick,
            netnames=['VREF<' + str(i) + '>'],
            direction='x',
            input_rails_xy=input_rails_xy,
            generate_pin=True,
            overwrite_start_coord=0,
            overwrite_end_coord=x1,
            overwrite_start_index=4 + (num_vref_routes_m6 + 1) * i,
            overwrite_end_index=4 + (num_vref_routes_m6 + 1) * i +
            num_vref_routes_m6 - 1)

    laygen.boundary_pin_from_rect(rclkb,
                                  rg_m4m5,
                                  "CLKB",
                                  laygen.layers['pin'][5],
                                  size=4,
                                  direction='bottom')
    laygen.boundary_pin_from_rect(routp,
                                  rg_m4m5,
                                  "OUTP",
                                  laygen.layers['pin'][5],
                                  size=4,
                                  direction='bottom')
    laygen.boundary_pin_from_rect(routm,
                                  rg_m4m5,
                                  "OUTM",
                                  laygen.layers['pin'][5],
                                  size=4,
                                  direction='bottom')
    laygen.boundary_pin_from_rect(rosp,
                                  rg_m2m3,
                                  "OSP",
                                  laygen.layers['pin'][3],
                                  size=4,
                                  direction='bottom')
    laygen.boundary_pin_from_rect(rosm,
                                  rg_m2m3,
                                  "OSM",
                                  laygen.layers['pin'][3],
                                  size=4,
                                  direction='bottom')
    for i in range(num_bits):
        laygen.boundary_pin_from_rect(renl0[i],
                                      rg_m5m6,
                                      "ENL" + str(i) + "<0>",
                                      laygen.layers['pin'][5],
                                      size=4,
                                      direction='bottom')
        laygen.boundary_pin_from_rect(renl1[i],
                                      rg_m5m6,
                                      "ENL" + str(i) + "<1>",
                                      laygen.layers['pin'][5],
                                      size=4,
                                      direction='bottom')
        laygen.boundary_pin_from_rect(renl2[i],
                                      rg_m5m6,
                                      "ENL" + str(i) + "<2>",
                                      laygen.layers['pin'][5],
                                      size=4,
                                      direction='bottom')
        laygen.boundary_pin_from_rect(renr0[i],
                                      rg_m5m6,
                                      "ENR" + str(i) + "<0>",
                                      laygen.layers['pin'][5],
                                      size=4,
                                      direction='bottom')
        laygen.boundary_pin_from_rect(renr1[i],
                                      rg_m5m6,
                                      "ENR" + str(i) + "<1>",
                                      laygen.layers['pin'][5],
                                      size=4,
                                      direction='bottom')
        laygen.boundary_pin_from_rect(renr2[i],
                                      rg_m5m6,
                                      "ENR" + str(i) + "<2>",
                                      laygen.layers['pin'][5],
                                      size=4,
                                      direction='bottom')

    laygen.pin_from_rect(name='SAINP',
                         layer=laygen.layers['pin'][4],
                         rect=rsainp,
                         gridname=rg_m4m5,
                         netname='INP')
    laygen.pin_from_rect(name='SAINM',
                         layer=laygen.layers['pin'][4],
                         rect=rsainm,
                         gridname=rg_m4m5,
                         netname='INM')
    for i, r in enumerate(rinp):
        laygen.boundary_pin_from_rect(r,
                                      rg_m4m5,
                                      "INP" + str(i),
                                      laygen.layers['pin'][4],
                                      size=8,
                                      direction='right',
                                      netname="INP")
    for i, r in enumerate(rinm):
        laygen.boundary_pin_from_rect(r,
                                      rg_m4m5,
                                      "INM" + str(i),
                                      laygen.layers['pin'][4],
                                      size=8,
                                      direction='left',
                                      netname="INM")
def generate_sar_wsamp(laygen,
                       objectname_pfix,
                       workinglib,
                       samp_lib,
                       space_1x_lib,
                       sar_name,
                       samp_name,
                       space_1x_name,
                       placement_grid,
                       routing_grid_m5m6,
                       routing_grid_m3m4_basic_thick,
                       routing_grid_m5m6_thick,
                       routing_grid_m5m6_thick_basic,
                       num_inv_bb=0,
                       num_bits=9,
                       use_sf=False,
                       vref_sf=False,
                       origin=np.array([0, 0])):
    """generate sar with sampling frontend """
    pg = placement_grid

    rg_m5m6 = routing_grid_m5m6
    rg_m5m6_thick = routing_grid_m5m6_thick
    rg_m5m6_thick_basic = routing_grid_m5m6_thick_basic  #for clock routing

    # placement
    # sar
    isar = laygen.place(name="I" + objectname_pfix + 'SAR0',
                        templatename=sar_name,
                        gridname=pg,
                        xy=origin,
                        template_libname=workinglib)
    # samp
    isamp = laygen.relplace(name="I" + objectname_pfix + 'SAMP0',
                            templatename=samp_name,
                            gridname=pg,
                            refinstname=isar.name,
                            direction='top',
                            template_libname=samp_lib)

    # source follower
    sf_name = 'sourceFollower_diff'
    if use_sf == True:
        isf = laygen.relplace(name="I" + objectname_pfix + 'SF0',
                              templatename=sf_name,
                              gridname=pg,
                              refinstname=isamp.name,
                              direction='top',
                              template_libname=workinglib)
        sf_xy = isf.xy

    #prboundary
    sar_size = laygen.templates.get_template(sar_name, libname=workinglib).size
    samp_size = laygen.templates.get_template(samp_name, libname=samp_lib).size
    sf_size = laygen.templates.get_template(sf_name, libname=workinglib).size
    space_size = laygen.templates.get_template(space_1x_name,
                                               libname=space_1x_lib).size
    size_x = sar_size[0]
    size_y = int((sar_size[1] + samp_size[1]) / space_size[1] +
                 1) * space_size[1]
    if use_sf == True:
        size_y = int((sar_size[1] + samp_size[1] + sf_size[1]) /
                     space_size[1] + 1) * space_size[1]
    laygen.add_rect(None,
                    np.array([origin, origin + np.array([size_x, size_y])]),
                    laygen.layers['prbnd'])

    # template handles
    sar_template = laygen.templates.get_template(sar_name, workinglib)
    samp_template = laygen.templates.get_template(samp_name, samp_lib)
    sf_template = laygen.templates.get_template(sf_name, workinglib)

    #reference coordinates
    pdict_m5m6 = laygen.get_inst_pin_xy(None, None, rg_m5m6)
    pdict_m5m6_thick = laygen.get_inst_pin_xy(None, None, rg_m5m6_thick)
    pdict_m5m6_thick_basic = laygen.get_inst_pin_xy(None, None,
                                                    rg_m5m6_thick_basic)
    sar_pins = sar_template.pins
    samp_pins = samp_template.pins
    sf_pins = sf_template.pins
    #sar_xy=isar.xy[0]
    #samp_xy=isamp.xy[0]
    sar_xy = isar.xy
    samp_xy = isamp.xy

    #signal route (clk/inp/inm)
    #make virtual grids and route on the grids (assuming drc clearance of each block)
    rg_m5m6_thick_basic_temp_sig = 'route_M5_M6_thick_basic_temp_sig'
    laygenhelper.generate_grids_from_inst(
        laygen,
        gridname_input=rg_m5m6_thick_basic,
        gridname_output=rg_m5m6_thick_basic_temp_sig,
        instname=isamp.name,
        inst_pin_prefix=['ckout'],
        xy_grid_type='xgrid')
    pdict_m5m6_thick_basic_temp_sig = laygen.get_inst_pin_xy(
        None, None, rg_m5m6_thick_basic_temp_sig)
    rg_m4m5_basic_thick_temp_sig = 'route_M4_M5_basic_thick_temp_sig'
    laygenhelper.generate_grids_from_inst(
        laygen,
        gridname_input=rg_m4m5_basic_thick,
        gridname_output=rg_m4m5_basic_thick_temp_sig,
        instname=isamp.name,
        inst_pin_prefix=['outp', 'outn'],
        xy_grid_type='xgrid')
    pdict_m4m5_basic_thick_temp_sig = laygen.get_inst_pin_xy(
        None, None, rg_m4m5_basic_thick_temp_sig)
    #clock
    rclk0 = laygen.route(
        None,
        laygen.layers['metal'][5],
        xy0=pdict_m5m6_thick_basic_temp_sig[isamp.name]['ckout'][0],
        xy1=pdict_m5m6_thick_basic_temp_sig[isar.name]['CLK0'][1] -
        np.array([0, 1]),
        gridname0=rg_m5m6_thick_basic_temp_sig)
    laygen.via(None, pdict_m5m6_thick_basic_temp_sig[isar.name]['CLK0'][1],
               rg_m5m6_thick_basic_temp_sig)
    laygen.via(None, pdict_m5m6_thick_basic_temp_sig[isar.name]['CLK1'][1],
               rg_m5m6_thick_basic_temp_sig)
    #laygen.via(None,pdict_m5m6_thick_basic_temp_sig[isar.name]['CLK2'][1], rg_m5m6_thick_basic_temp_sig)
    #rclk0 = laygen.route(None, laygen.layers['metal'][5],
    #                     xy0=pdict_m5m6_thick_basic[isamp.name]['ckout'][0],
    #                     xy1=pdict_m5m6_thick_basic[isar.name]['CLK'][1]-np.array([0,1]), gridname0=rg_m5m6_thick_basic)
    #laygen.via(None,pdict_m5m6_thick_basic[isar.name]['CLK'][1], rg_m5m6_thick_basic)

    #frontend sig
    inp_y_list = []
    inm_y_list = []
    for pn, p in pdict_m4m5_basic_thick_temp_sig[isar.name].items():
        if pn.startswith('INP'):
            inp_y_list.append(p[0][1])
            pv = np.array([
                pdict_m4m5_basic_thick_temp_sig[isamp.name]['outp'][0][0],
                p[0][1]
            ])
            laygen.via(None, pv, rg_m4m5_basic_thick_temp_sig)
            #laygen.via(None,p[0], rg_m5m6_thick_basic_temp_sig)
        if pn.startswith('INM'):
            inm_y_list.append(p[0][1])
            pv = np.array([
                pdict_m4m5_basic_thick_temp_sig[isamp.name]['outn'][0][0],
                p[0][1]
            ])
            laygen.via(None, pv, rg_m4m5_basic_thick_temp_sig)
            #laygen.via(None,p[0], rg_m5m6_thick_basic_temp_sig)
    inp_y = min(inp_y_list)
    inm_y = min(inm_y_list)
    rinp0 = laygen.route(
        None,
        laygen.layers['metal'][5],
        xy0=pdict_m4m5_basic_thick_temp_sig[isamp.name]['outp'][0],
        xy1=np.array([
            pdict_m4m5_basic_thick_temp_sig[isamp.name]['outp'][0][0],
            inp_y - 1
        ]),
        gridname0=rg_m4m5_basic_thick_temp_sig)
    rinm0 = laygen.route(
        None,
        laygen.layers['metal'][5],
        xy0=pdict_m4m5_basic_thick_temp_sig[isamp.name]['outn'][0],
        xy1=np.array([
            pdict_m4m5_basic_thick_temp_sig[isamp.name]['outn'][0][0],
            inm_y - 1
        ]),
        gridname0=rg_m4m5_basic_thick_temp_sig)
    #rinp0 = laygen.route(None, laygen.layers['metal'][5],
    #                     xy0=pdict_m5m6_thick_basic_temp_sig[isamp.name]['outp'][0],
    #                     xy1=np.array([pdict_m5m6_thick_basic_temp_sig[isar.name]['INP0'][0][0],inp_y-1]),
    #                     gridname0=rg_m5m6_thick_basic_temp_sig)
    #rinm0 = laygen.route(None, laygen.layers['metal'][5],
    #                     xy0=pdict_m5m6_thick_basic_temp_sig[isamp.name]['outn'][0],
    #                     xy1=np.array([pdict_m5m6_thick_basic_temp_sig[isar.name]['INM0'][0][0],inm_y-1]),
    #                     gridname0=rg_m5m6_thick_basic_temp_sig)

    # source follower routing
    pdict_m4m5_thick = laygen.get_inst_pin_xy(None, None, rg_m4m5_thick)
    if use_sf == True:
        [rh0, rv0] = laygen.route_hv(laygen.layers['metal'][4],
                                     laygen.layers['metal'][5],
                                     pdict_m4m5_thick[isf.name]['outp'][0],
                                     pdict_m4m5_thick[isamp.name]['inp'][0],
                                     rg_m4m5_thick)
        [rh0, rv0] = laygen.route_hv(laygen.layers['metal'][4],
                                     laygen.layers['metal'][5],
                                     pdict_m4m5_thick[isf.name]['outn'][0],
                                     pdict_m4m5_thick[isamp.name]['inn'][0],
                                     rg_m4m5_thick)
        # VDD/VSS for source follower
        for pn, p in samp_pins.items():
            if pn.startswith('LVDD'):
                laygen.route_vh(laygen.layers['metal'][5],
                                laygen.layers['metal'][4],
                                pdict_m4m5_thick[isamp.name][pn][0],
                                pdict_m4m5_thick[isf.name]['VDD0'][0],
                                rg_m4m5_thick)
                laygen.route_vh(laygen.layers['metal'][5],
                                laygen.layers['metal'][4],
                                pdict_m4m5_thick[isamp.name][pn][0],
                                pdict_m4m5_thick[isf.name]['VDD1'][0],
                                rg_m4m5_thick)
            if pn.startswith('RVDD'):
                laygen.route_vh(laygen.layers['metal'][5],
                                laygen.layers['metal'][4],
                                pdict_m4m5_thick[isamp.name][pn][0],
                                pdict_m4m5_thick[isf.name]['VDD0'][0],
                                rg_m4m5_thick)
                laygen.route_vh(laygen.layers['metal'][5],
                                laygen.layers['metal'][4],
                                pdict_m4m5_thick[isamp.name][pn][0],
                                pdict_m4m5_thick[isf.name]['VDD1'][0],
                                rg_m4m5_thick)
            if pn.startswith('LVSS'):
                laygen.route_vh(laygen.layers['metal'][5],
                                laygen.layers['metal'][4],
                                pdict_m4m5_thick[isamp.name][pn][0],
                                pdict_m4m5_thick[isf.name]['VSS0'][0],
                                rg_m4m5_thick)
                laygen.route_vh(laygen.layers['metal'][5],
                                laygen.layers['metal'][4],
                                pdict_m4m5_thick[isamp.name][pn][0],
                                pdict_m4m5_thick[isf.name]['VSS1'][0],
                                rg_m4m5_thick)
            if pn.startswith('RVSS'):
                laygen.route_vh(laygen.layers['metal'][5],
                                laygen.layers['metal'][4],
                                pdict_m4m5_thick[isamp.name][pn][0],
                                pdict_m4m5_thick[isf.name]['VSS0'][0],
                                rg_m4m5_thick)
                laygen.route_vh(laygen.layers['metal'][5],
                                laygen.layers['metal'][4],
                                pdict_m4m5_thick[isamp.name][pn][0],
                                pdict_m4m5_thick[isf.name]['VSS1'][0],
                                rg_m4m5_thick)

    #input pins (just duplicate from lower hierarchy cells)
    laygen.add_pin('CLK', 'CLK', samp_xy + samp_pins['ckin']['xy'],
                   samp_pins['ckin']['layer'])
    if use_sf == False:
        laygen.add_pin('INP', 'INP', samp_xy + samp_pins['inp']['xy'],
                       samp_pins['inp']['layer'])
        laygen.add_pin('INM', 'INM', samp_xy + samp_pins['inn']['xy'],
                       samp_pins['inn']['layer'])
    else:
        laygen.add_pin('INP', 'INP', sf_xy + sf_pins['inp']['xy'],
                       sf_pins['inp']['layer'])
        laygen.add_pin('INM', 'INM', sf_xy + sf_pins['inn']['xy'],
                       sf_pins['inn']['layer'])
        for pn, p in sf_pins.items():
            if pn.startswith('Voffp'):
                pxy = sf_xy + sf_pins[pn]['xy']
                laygen.add_pin('SF_' + pn, 'SF_Voffp', pxy,
                               sf_pins[pn]['layer'])
            if pn.startswith('Voffn'):
                pxy = sf_xy + sf_pins[pn]['xy']
                laygen.add_pin('SF_' + pn, 'SF_Voffn', pxy,
                               sf_pins[pn]['layer'])
            if pn.startswith('bypass'):
                pxy = sf_xy + sf_pins[pn]['xy']
                laygen.add_pin('SF_' + pn, 'SF_bypass', pxy,
                               sf_pins[pn]['layer'])
            if pn.startswith('VBIAS'):
                pxy = sf_xy + sf_pins[pn]['xy']
                laygen.add_pin('SF_BIAS', 'SF_BIAS', pxy, sf_pins[pn]['layer'])
            # if pn.startswith('VBIASn'):
            #     pxy = sf_xy + sf_pins[pn]['xy']
            #     laygen.add_pin('SF_BIASn', 'SF_BIASn', pxy, sf_pins[pn]['layer'])

    if vref_sf == True:
        laygen.add_pin('VREF_SF_BIAS', 'VREF_SF_BIAS',
                       sar_xy + sar_pins['SF_VBIAS']['xy'],
                       sar_pins['SF_VBIAS']['layer'])
        laygen.add_pin('VREF_SF_bypass', 'VREF_SF_bypass',
                       sar_xy + sar_pins['SF_bypass']['xy'],
                       sar_pins['SF_bypass']['layer'])

    laygen.add_pin('OSP', 'OSP', sar_xy + sar_pins['OSP']['xy'],
                   sar_pins['OSP']['layer'])
    laygen.add_pin('OSM', 'OSM', sar_xy + sar_pins['OSM']['xy'],
                   sar_pins['OSM']['layer'])
    for pn, p in sar_pins.items():
        if pn.startswith('VREF<0>'):
            pxy = sar_xy + sar_pins[pn]['xy']
            laygen.add_pin(pn, 'VREF<0>', pxy, sar_pins[pn]['layer'])
        if pn.startswith('VREF<1>'):
            pxy = sar_xy + sar_pins[pn]['xy']
            laygen.add_pin(pn, 'VREF<1>', pxy, sar_pins[pn]['layer'])
        if pn.startswith('VREF<2>'):
            pxy = sar_xy + sar_pins[pn]['xy']
            laygen.add_pin(pn, 'VREF<2>', pxy, sar_pins[pn]['layer'])
    #laygen.add_pin('VREF_M5R<2>', 'VREF<2>', sar_xy+sar_pins['VREF_M5R<2>']['xy'], sar_pins['VREF_M5R<2>']['layer'])
    #laygen.add_pin('VREF_M5R<1>', 'VREF<1>', sar_xy+sar_pins['VREF_M5R<1>']['xy'], sar_pins['VREF_M5R<1>']['layer'])
    #laygen.add_pin('VREF_M5R<0>', 'VREF<0>', sar_xy+sar_pins['VREF_M5R<0>']['xy'], sar_pins['VREF_M5R<0>']['layer'])
    #laygen.add_pin('VREF_M5L<2>', 'VREF<2>', sar_xy+sar_pins['VREF_M5L<2>']['xy'], sar_pins['VREF_M5L<2>']['layer'])
    #laygen.add_pin('VREF_M5L<1>', 'VREF<1>', sar_xy+sar_pins['VREF_M5L<1>']['xy'], sar_pins['VREF_M5L<1>']['layer'])
    #laygen.add_pin('VREF_M5L<0>', 'VREF<0>', sar_xy+sar_pins['VREF_M5L<0>']['xy'], sar_pins['VREF_M5L<0>']['layer'])
    laygen.add_pin('CKDSEL0<1>', 'CKDSEL0<1>',
                   sar_xy + sar_pins['CKDSEL0<1>']['xy'],
                   sar_pins['CKDSEL0<1>']['layer'])
    laygen.add_pin('CKDSEL0<0>', 'CKDSEL0<0>',
                   sar_xy + sar_pins['CKDSEL0<0>']['xy'],
                   sar_pins['CKDSEL0<0>']['layer'])
    laygen.add_pin('CKDSEL1<1>', 'CKDSEL1<1>',
                   sar_xy + sar_pins['CKDSEL1<1>']['xy'],
                   sar_pins['CKDSEL1<1>']['layer'])
    laygen.add_pin('CKDSEL1<0>', 'CKDSEL1<0>',
                   sar_xy + sar_pins['CKDSEL1<0>']['xy'],
                   sar_pins['CKDSEL1<0>']['layer'])
    #laygen.add_pin('EXTCLK', 'EXTCLK', sar_xy+sar_pins['EXTCLK']['xy'], sar_pins['EXTCLK']['layer'])
    laygen.add_pin('EXTSEL_CLK', 'EXTSEL_CLK',
                   sar_xy + sar_pins['EXTSEL_CLK']['xy'],
                   sar_pins['EXTSEL_CLK']['layer'])
    #output pins (just duplicate from lower hierarchy cells)
    for i in range(num_bits):
        pn = 'ADCOUT' + '<' + str(i) + '>'
        laygen.add_pin(pn, pn, sar_xy + sar_pins[pn]['xy'],
                       sar_pins[pn]['layer'])
    laygen.add_pin('CLKO0', 'CLKO', sar_xy + sar_pins['CLKOUT0']['xy'],
                   sar_pins['CLKOUT0']['layer'])
    laygen.add_pin('CLKO1', 'CLKO', sar_xy + sar_pins['CLKOUT1']['xy'],
                   sar_pins['CLKOUT1']['layer'])
    #laygen.add_pin('CLKO2', 'CLKO', sar_xy+sar_pins['CLKOUT2']['xy'], sar_pins['CLKOUT2']['layer'])

    #probe pins
    laygen.add_pin('CLK0', 'ICLK', sar_xy + sar_pins['CLK0']['xy'],
                   sar_pins['CLK0']['layer'])
    laygen.add_pin('CLK1', 'ICLK', sar_xy + sar_pins['CLK1']['xy'],
                   sar_pins['CLK1']['layer'])
    #laygen.add_pin('CLK2', 'ICLK', sar_xy+sar_pins['CLK2']['xy'], sar_pins['CLK2']['layer'])
    laygen.add_pin('CLKPRB_SAMP', 'CLKPRB_SAMP',
                   samp_xy + samp_pins['ckpg']['xy'],
                   samp_pins['ckpg']['layer'])
    #laygen.add_pin('CLKPRB_SAR', 'CLKPRB_SAR', sar_xy+sar_pins['CLKPRB']['xy'], sar_pins['CLKPRB']['layer'])
    laygen.add_pin('SAMPP', 'SAMPP', sar_xy + sar_pins['SAINP']['xy'],
                   sar_pins['SAINP']['layer'])
    laygen.add_pin('SAMPM', 'SAMPM', sar_xy + sar_pins['SAINM']['xy'],
                   sar_pins['SAINM']['layer'])
    laygen.add_pin('SAOP', 'SAOP', sar_xy + sar_pins['SAOP']['xy'],
                   sar_pins['SAOP']['layer'])
    laygen.add_pin('SAOM', 'SAOM', sar_xy + sar_pins['SAOM']['xy'],
                   sar_pins['SAOM']['layer'])
    laygen.add_pin('SARCLK', 'SARCLK', sar_xy + sar_pins['SARCLK']['xy'],
                   sar_pins['SARCLK']['layer'])
    laygen.add_pin('SARCLKB', 'SARCLKB', sar_xy + sar_pins['SARCLKB']['xy'],
                   sar_pins['SARCLKB']['layer'])
    #laygen.add_pin('COMPOUT', 'COMPOUT', sar_xy+sar_pins['COMPOUT']['xy'], sar_pins['COMPOUT']['layer'])
    laygen.add_pin('DONE', 'DONE', sar_xy + sar_pins['DONE']['xy'],
                   sar_pins['DONE']['layer'])
    laygen.add_pin('UP', 'UP', sar_xy + sar_pins['UP']['xy'],
                   sar_pins['UP']['layer'])
    laygen.add_pin('PHI0', 'PHI0', sar_xy + sar_pins['PHI0']['xy'],
                   sar_pins['PHI0']['layer'])
    for i in range(num_bits):
        pn = 'ZP' + '<' + str(i) + '>'
        laygen.add_pin(pn, pn, sar_xy + sar_pins[pn]['xy'],
                       sar_pins[pn]['layer'])
        pn = 'ZMID' + '<' + str(i) + '>'
        laygen.add_pin(pn, pn, sar_xy + sar_pins[pn]['xy'],
                       sar_pins[pn]['layer'])
        pn = 'ZM' + '<' + str(i) + '>'
        laygen.add_pin(pn, pn, sar_xy + sar_pins[pn]['xy'],
                       sar_pins[pn]['layer'])
        pn = 'SB' + '<' + str(i) + '>'
        laygen.add_pin(pn, pn, sar_xy + sar_pins[pn]['xy'],
                       sar_pins[pn]['layer'])
    for i in range(num_bits - 1):
        pn = 'VOL' + '<' + str(i) + '>'
        laygen.add_pin(pn, pn, sar_xy + sar_pins[pn]['xy'],
                       sar_pins[pn]['layer'])
        pn = 'VOR' + '<' + str(i) + '>'
        laygen.add_pin(pn, pn, sar_xy + sar_pins[pn]['xy'],
                       sar_pins[pn]['layer'])

    #VDD/VSS pin
    vddcnt = 0
    vsscnt = 0
    for p in pdict_m5m6[isar.name]:
        if p.startswith('VDD'):
            xy0 = pdict_m5m6_thick[isar.name][p]
            laygen.pin(name='VDDSAR' + str(vddcnt),
                       layer=laygen.layers['pin'][6],
                       xy=xy0,
                       gridname=rg_m5m6_thick,
                       netname='VDDSAR')
            vddcnt += 1
        if p.startswith('VSS'):
            xy0 = pdict_m5m6_thick[isar.name][p]
            laygen.pin(name='VSSSAR' + str(vsscnt),
                       layer=laygen.layers['pin'][6],
                       xy=xy0,
                       gridname=rg_m5m6_thick,
                       netname='VSS:')
            #laygen.pin(name='VSSSAR' + str(vsscnt), layer=laygen.layers['pin'][6], xy=xy0, gridname=rg_m5m6_thick, netname='VSS')
            vsscnt += 1
    #extract VDD/VSS grid from samp and make power pins
    rg_m5m6_thick_temp_samp = 'route_M5_M6_thick_temp_samp'
    laygenhelper.generate_grids_from_inst(
        laygen,
        gridname_input=rg_m5m6_thick,
        gridname_output=rg_m5m6_thick_temp_samp,
        instname=isamp.name,
        inst_pin_prefix=['VDD', 'VSS', 'samp_body'],
        xy_grid_type='ygrid')
    pdict_m5m6_thick_temp_samp = laygen.get_inst_pin_xy(
        None, None, rg_m5m6_thick_temp_samp)
    vddcnt = 0
    vsscnt = 0
    bodycnt = 0
    for p in pdict_m5m6_thick_temp_samp[isamp.name]:
        if p.startswith('VDD'):
            xy0 = pdict_m5m6_thick_temp_samp[isamp.name][p]
            laygen.pin(name='VDDSAMP' + str(vddcnt),
                       layer=laygen.layers['pin'][6],
                       xy=xy0,
                       gridname=rg_m5m6_thick_temp_samp,
                       netname='VDDSAMP')
            vddcnt += 1
        if p.startswith('VSS'):
            xy0 = pdict_m5m6_thick_temp_samp[isamp.name][p]
            laygen.pin(name='VSSSAMP' + str(vsscnt),
                       layer=laygen.layers['pin'][6],
                       xy=xy0,
                       gridname=rg_m5m6_thick_temp_samp,
                       netname='VSS:')
            #laygen.pin(name='VSSSAMP' + str(vsscnt), layer=laygen.layers['pin'][6], xy=xy0, gridname=rg_m5m6_thick_temp_samp, netname='VSS')
            vsscnt += 1
        if p.startswith('samp_body'):
            xy0 = pdict_m5m6_thick_temp_samp[isamp.name][p]
            laygen.pin(name='samp_body',
                       layer=laygen.layers['pin'][6],
                       xy=xy0,
                       gridname=rg_m5m6_thick_temp_samp,
                       netname='samp_body')
            bodycnt += 1
    # VBB
    pdict_m3m4 = laygen.get_inst_pin_xy(None, None, rg_m3m4_basic_thick)
    if not num_inv_bb == 0:
        rvbb_m3 = []
        for p in pdict_m3m4[isar.name]:
            if p.startswith('VBB') and p.endswith('0'):
                rvbb_m3.append(pdict_m3m4[isar.name][p])
                # laygen.pin(name='bottom_body'+str(p), layer=laygen.layers['pin'][3], xy=pdict_m3m4[isar.name][p], gridname=rg_m3m4, netname='bottom_body')
        rvbb_m4 = laygenhelper.generate_power_rails_from_rails_xy(
            laygen,
            routename_tag='_M4_',
            layer=laygen.layers['metal'][4],
            gridname=rg_m3m4_basic_thick,
            netnames=['bottom_body'],
            direction='x',
            input_rails_xy=[rvbb_m3],
            generate_pin=False,
            overwrite_start_coord=None,
            overwrite_end_coord=None,
            offset_start_index=0,
            offset_end_index=0)
        rvbb_m5 = laygenhelper.generate_power_rails_from_rails_rect(
            laygen,
            routename_tag='_M5_',
            layer=laygen.layers['metal'][5],
            gridname=rg_m4m5_thick,
            netnames=['bottom_body'],
            direction='y',
            input_rails_rect=rvbb_m4,
            generate_pin=False,
            overwrite_start_coord=None,
            overwrite_end_coord=None,
            offset_start_index=0,
            offset_end_index=-2)
        rvbb_m6 = laygenhelper.generate_power_rails_from_rails_rect(
            laygen,
            routename_tag='_M6_',
            layer=laygen.layers['metal'][6],
            gridname=rg_m5m6_thick,
            netnames=['bottom_body'],
            direction='x',
            input_rails_rect=rvbb_m5,
            generate_pin=False,
            overwrite_start_coord=None,
            overwrite_end_coord=None,
            offset_start_index=0,
            offset_end_index=0)
        num_m6 = len(rvbb_m6[0])
        for i in range(num_m6):
            if i % 2 == 1:
                rvbb_m6[0].remove(rvbb_m6[0][num_m6 - i - 1])
            print(rvbb_m6[0])
        rvbb_m7 = laygenhelper.generate_power_rails_from_rails_rect(
            laygen,
            routename_tag='_M7_',
            layer=laygen.layers['pin'][7],
            gridname=rg_m6m7_thick,
            netnames=['bottom_body'],
            direction='y',
            input_rails_rect=rvbb_m6,
            generate_pin=True,
            overwrite_start_coord=None,
            overwrite_end_coord=None,
            offset_start_index=0,
            offset_end_index=0)
def generate_space_array(laygen,
                         objectname_pfix,
                         tisar_libname,
                         space_libname,
                         tisar_name,
                         space_name,
                         placement_grid,
                         routing_grid_m3m4_thick,
                         routing_grid_m4m5_thick,
                         routing_grid_m5m6_thick,
                         origin=np.array([0, 0])):
    """generate tisar space """
    pg = placement_grid
    ttisar = laygen.templates.get_template(tisar_name, libname=tisar_libname)
    tspace = laygen.templates.get_template(space_name, libname=space_libname)

    sar_pins = ttisar.pins

    tbnd_bottom = laygen.templates.get_template('boundary_bottom')
    tbnd_bleft = laygen.templates.get_template('boundary_bottomleft')
    space_xy = np.array([tspace.size[0], ttisar.size[1]])
    # laygen.add_rect(None, np.array([origin, origin+space_xy+2*tbnd_bleft.size[0]*np.array([1, 0])]), laygen.layers['prbnd'])
    # num_space_tot=int((ttisar.size[1]-2*tbnd_bottom.size[1])/tspace.size[1])
    # tbnd_bleft_size=tbnd_bleft.size

    #VDD/VSS/VREF integration
    rvddclkd = []
    rvddsamp = []
    rvddsar = []
    rvddsar_upper = []
    rvref0 = []
    rvref1 = []
    rvref2 = []
    rvssclkd = []
    rvsssamp = []
    rvsssar = []
    rvsssar_upper = []
    vddclkd_xy = []
    vddsamp_xy = []
    vddsar_xy = []
    vddsar_upper_xy = []
    vssclkd_xy = []
    vsssamp_xy = []
    vsssar_xy = []
    vsssar_upper_xy = []
    y_vddsar_max = 0
    y_vddsar_lower_max = 0
    y_vddsamp_min = 500000
    y_vddsamp_max = 0
    y_vddclkd_min = 500000
    y_vref0 = sar_pins['VREF0<0>']['xy'][0][1]

    num_unit_row = 4
    num_unit_col = 3
    ispace_sar = []
    for col in range(num_unit_col):
        ispace_sar.append([])
        space_origin = origin + [
            col * laygen.get_template_xy(space_name, pg, workinglib)[0], 0
        ]
        ispace_sar[col].append(
            laygen.place(name="I" + objectname_pfix + 'SPSAR' + str(col),
                         templatename=space_name,
                         gridname=pg,
                         xy=space_origin,
                         template_libname=space_libname))

        for i in range(1, num_unit_row):
            if i % 2 == 0:
                ispace_sar[col].append(
                    laygen.relplace(name="I" + objectname_pfix + 'SPSAR' +
                                    str(col) + str(i),
                                    templatename=space_name,
                                    gridname=pg,
                                    refinstname=ispace_sar[col][-1].name,
                                    direction='top',
                                    transform='R0',
                                    template_libname=space_libname))
            else:
                ispace_sar[col].append(
                    laygen.relplace(name="I" + objectname_pfix + 'SPSAR' +
                                    str(col) + str(i),
                                    templatename=space_name,
                                    gridname=pg,
                                    refinstname=ispace_sar[col][-1].name,
                                    direction='top',
                                    transform='MX',
                                    template_libname=space_libname))
    #
    # m_bnd = int(space_xy[0] / tbnd_bottom.size[0])
    # [bnd_bottom, bnd_top, bnd_left, bnd_right] \
    #     = laygenhelper.generate_boundary(laygen, objectname_pfix='BNDSAR0', placement_grid=pg,
    #                         devname_bottom=['boundary_bottomleft', 'boundary_bottom', 'boundary_bottomright'],
    #                         shape_bottom=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
    #                         devname_top=['boundary_topleft', 'boundary_top', 'boundary_topright'],
    #                         shape_top=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
    #                         devname_left=devname_bnd_left,
    #                         transform_left=transform_bnd_left,
    #                         devname_right=devname_bnd_right,
    #                         transform_right=transform_bnd_right,
    #                         origin=origin)

    # #vddsamp cap
    # num_space_samp=num_space_tot-num_space_sar-1
    # space_origin = origin + laygen.get_xy(obj=laygen.get_template(name = 'boundary_bottomleft'), gridname = pg) * np.array([1, (3 + 2 * num_space_sar)])
    # ispace_samp = [laygen.place(name="I" + objectname_pfix + 'SPSAMP0', templatename=space_name,
    #                       gridname=pg, xy=space_origin, template_libname=space_libname)]
    # devname_bnd_left = ['nmos4_fast_left', 'pmos4_fast_left']
    # devname_bnd_right = ['nmos4_fast_right', 'pmos4_fast_right']
    # transform_bnd_left = ['R0', 'MX']
    # transform_bnd_right = ['R0', 'MX']
    #
    # for i in range(1, num_space_samp):
    #     if i % 2 == 0:
    #         ispace_samp.append(laygen.relplace(name="I" + objectname_pfix + 'SPSAMP' + str(i), templatename=space_name,
    #                                    gridname=pg, refinstname=ispace_samp[-1].name, direction='top', transform='R0',
    #                                    template_libname=space_libname))
    #         devname_bnd_left += ['nmos4_fast_left', 'pmos4_fast_left']
    #         devname_bnd_right += ['nmos4_fast_right', 'pmos4_fast_right']
    #         transform_bnd_left += ['R0', 'MX']
    #         transform_bnd_right += ['R0', 'MX']
    #     else:
    #         ispace_samp.append(laygen.relplace(name="I" + objectname_pfix + 'SPSAMP' + str(i), templatename=space_name,
    #                                    gridname=pg, refinstname=ispace_samp[-1].name, direction='top', transform='MX',
    #                                    template_libname=space_libname))
    #         devname_bnd_left += ['pmos4_fast_left', 'nmos4_fast_left']
    #         devname_bnd_right += ['pmos4_fast_right', 'nmos4_fast_right']
    #         transform_bnd_left += ['R0', 'MX']
    #         transform_bnd_right += ['R0', 'MX']
    #
    # m_bnd = int(space_xy[0] / tbnd_bottom.size[0])
    # [bnd_bottom, bnd_top, bnd_left, bnd_right] \
    #     = laygenhelper.generate_boundary(laygen, objectname_pfix='BNDSAMP0', placement_grid=pg,
    #                                      devname_bottom=['boundary_bottomleft', 'boundary_bottom', 'boundary_bottomright'],
    #                                      shape_bottom=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
    #                                      devname_top=['boundary_topleft', 'boundary_top', 'boundary_topright'],
    #                                      shape_top=[np.array([1, 1]), np.array([m_bnd, 1]), np.array([1, 1])],
    #                                      devname_left=devname_bnd_left,
    #                                      transform_left=transform_bnd_left,
    #                                      devname_right=devname_bnd_right,
    #                                      transform_right=transform_bnd_right,
    #                                      origin=space_origin-laygen.get_xy(obj=laygen.get_template(name = 'boundary_bottomleft'), gridname = pg))
    #vdd/vss
    #m3
    rvdd_sar_xy_m3 = []
    rvss_sar_xy_m3 = []
    space_template = laygen.templates.get_template(space_name, workinglib)
    space_pins = space_template.pins
    vddcnt = 0
    vsscnt = 0
    for col in range(num_unit_col):
        space_origin_phy = ispace_sar[col][0].bbox[0]
        for pn, p in space_pins.items():
            if pn.startswith('VDD'):
                pxy = space_origin_phy + np.array(
                    [p['xy'][0], p['xy'][1] * np.array([1, num_unit_row])])
                pxy[0][1] = y_vddsar_lower_max
                pxy[1][1] = ispace_sar[0][-1].bbox[1][1]
                laygen.add_rect(None, pxy, laygen.layers['metal'][3])
                rvdd_sar_xy_m3.append(
                    laygen.grids.get_absgrid_coord_region(
                        gridname=rg_m3m4_thick, xy0=pxy[0], xy1=pxy[1]))
                vddcnt += 1
            if pn.startswith('VSS'):
                pxy = space_origin_phy + np.array(
                    [p['xy'][0], p['xy'][1] * np.array([1, num_unit_row])])
                pxy[0][1] = y_vddsar_lower_max
                pxy[1][1] = ispace_sar[0][-1].bbox[1][1]
                laygen.add_rect(None, pxy, laygen.layers['metal'][3])
                rvss_sar_xy_m3.append(
                    laygen.grids.get_absgrid_coord_region(
                        gridname=rg_m3m4_thick, xy0=pxy[0], xy1=pxy[1]))
                vsscnt += 1

    print(vddcnt, vsscnt)
    # rvdd_samp_xy_m3=[]
    # rvss_samp_xy_m3=[]
    # space_template = laygen.templates.get_template(space_name, workinglib)
    # space_pins=space_template.pins
    # space_origin_phy = ispace_samp[0].bbox[0]
    # vddcnt=0
    # vsscnt=0
    # for pn, p in space_pins.items():
    #     if pn.startswith('VDD'):
    #         pxy=space_origin_phy+np.array([p['xy'][0], p['xy'][1]*np.array([1, num_space_samp])])
    #         laygen.add_rect(None, pxy, laygen.layers['metal'][3])
    #         rvdd_samp_xy_m3.append(laygen.grids.get_absgrid_coord_region(gridname=rg_m3m4_thick, xy0=pxy[0], xy1=pxy[1]))
    #         vddcnt += 1
    #     if pn.startswith('VSS'):
    #         pxy=space_origin_phy+np.array([p['xy'][0], p['xy'][1]*np.array([1, num_space_samp])])
    #         laygen.add_rect(None, pxy, laygen.layers['metal'][3])
    #         rvss_samp_xy_m3.append(laygen.grids.get_absgrid_coord_region(gridname=rg_m3m4_thick, xy0=pxy[0], xy1=pxy[1]))
    #         vsscnt += 1
    #m4
    # input_rails_xy = [rvdd_samp_xy_m3, rvss_samp_xy_m3]
    # rvdd_samp_m4, rvss_samp_m4 = laygenhelper.generate_power_rails_from_rails_xy(laygen, routename_tag='_M4_SAMP_', layer=laygen.layers['metal'][4],
    #                                     gridname=rg_m3m4_thick, netnames=['VDDSAMP', 'VSSSAMP'], direction='x',
    #                                     input_rails_xy=input_rails_xy, generate_pin=False,
    #                                     overwrite_start_coord=None, overwrite_end_coord=None,
    #                                     offset_start_index=0, offset_end_index=0)
    end = laygen.get_xy(obj = ispace_sar[-1][0], gridname=rg_m3m4_thick)[0]\
         +laygen.get_xy(obj = ispace_sar[-1][0].template, gridname=rg_m3m4_thick)[0]
    input_rails_xy = [rvdd_sar_xy_m3, rvss_sar_xy_m3]
    rvdd_sar_m4, rvss_sar_m4 = laygenhelper.generate_power_rails_from_rails_xy(
        laygen,
        routename_tag='_M4_',
        layer=laygen.layers['metal'][4],
        gridname=rg_m3m4_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_xy=input_rails_xy,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=end,
        offset_start_index=0,
        offset_end_index=0)

    end = laygen.get_xy(obj=ispace_sar[-1][-1], gridname=rg_m4m5_thick)[1]
    if num_unit_row % 2 == 1:
        end = end + laygen.get_xy(obj=ispace_sar[-1][0].template,
                                  gridname=rg_m4m5_thick)[1]
    input_rails_rect = [rvdd_sar_m4, rvss_sar_m4]
    rvdd_sar_m5, rvss_sar_m5 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M5_',
        layer=laygen.layers['metal'][5],
        gridname=rg_m4m5_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=end,
        offset_start_index=0,
        offset_end_index=0)

    end = laygen.get_xy(obj = ispace_sar[-1][0], gridname=rg_m5m6_thick)[0]\
         +laygen.get_xy(obj = ispace_sar[-1][0].template, gridname=rg_m5m6_thick)[0]
    input_rails_rect = [rvdd_sar_m5, rvss_sar_m5]
    rvdd_sar_m6, rvss_sar_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M6_',
        layer=laygen.layers['metal'][6],
        gridname=rg_m5m6_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=end,
        offset_start_index=0,
        offset_end_index=0)

    end = laygen.get_xy(obj=ispace_sar[-1][-1], gridname=rg_m6m7_thick)[1]
    if num_unit_row % 2 == 1:
        end = end + laygen.get_xy(obj=ispace_sar[-1][0].template,
                                  gridname=rg_m6m7_thick)[1]
    input_rails_rect = [rvdd_sar_m6, rvss_sar_m6]
    rvdd_sar_m7, rvss_sar_m7 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M7_',
        layer=laygen.layers['metal'][7],
        gridname=rg_m6m7_thick,
        netnames=['VDD', 'VSS'],
        direction='y',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=end,
        offset_start_index=1,
        offset_end_index=0)

    for _idx, _vdd in enumerate(rvdd_sar_m6):
        xy0 = laygen.get_xy(obj=_vdd, gridname=rg_m5m6_thick)
        laygen.pin(name='VDD_M6' + str(_idx),
                   layer=laygen.layers['pin'][6],
                   xy=xy0,
                   gridname=rg_m5m6_thick,
                   netname='VDD')

    for _idx, _vdd in enumerate(rvss_sar_m6):
        xy0 = laygen.get_xy(obj=_vdd, gridname=rg_m5m6_thick)
        laygen.pin(name='VSS_M6' + str(_idx),
                   layer=laygen.layers['pin'][6],
                   xy=xy0,
                   gridname=rg_m5m6_thick,
                   netname='VSS')
    #
    end = laygen.get_xy(obj = ispace_sar[-1][0], gridname=rg_m7m8_thick)[0]\
         +laygen.get_xy(obj = ispace_sar[-1][0].template, gridname=rg_m7m8_thick)[0]
    input_rails_rect = [rvdd_sar_m7, rvss_sar_m7]
    rvdd_sar_m6, rvss_sar_m6 = laygenhelper.generate_power_rails_from_rails_rect(
        laygen,
        routename_tag='_M8_',
        layer=laygen.layers['metal'][8],
        gridname=rg_m7m8_thick,
        netnames=['VDD', 'VSS'],
        direction='x',
        input_rails_rect=input_rails_rect,
        generate_pin=False,
        overwrite_start_coord=0,
        overwrite_end_coord=end,
        offset_start_index=0,
        offset_end_index=0)