Пример #1
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,
                                use_offset=True,
                                clkin_trackm=12,
                                clk_cdac_bits=5,
                                clk_pulse=False,
                                clkdist_offset=2,
                                ret_use_laygo=True,
                                use_sf=False,
                                vref_sf=False,
                                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
    space_size = laygen.templates.get_template(space_1x_name,
                                               libname=space_1x_libname).size
    # 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_xy(obj=laygen.get_template(
        name=ret_name, libname=ret_libname),
                                     gridname=pg) * 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*space_size[1]/laygen.get_grid(pg).height)
    # clkdist_xy = laygen.get_xy(obj =isar, gridname=pg) \
    #              + (laygen.get_xy(obj=laygen.get_template(name = sar_name, libname=sar_libname), gridname=pg) * np.array([0, 1])) \
    #              + np.array([0, clkdist_offset_pg])
    clkdist_xy = laygen.templates.get_template(clkdist_name,
                                               libname=clkdist_libname).xy
    clkdist_off_y = laygen.grids.get_absgrid_y(pg, clkdist_xy[0][1])
    clkdist_off_y_voff_route_phy = int(laygen.grids.get_phygrid_y(rg_m5m6, num_hori * num_vert+4) / \
                                       laygen.get_template_size('space_1x', gridname=None, libname=logictemplib)[1]+1) * \
                                   laygen.get_template_size('space_1x', gridname=None, libname=logictemplib)[1]
    clkdist_off_y_voff_route = laygen.grids.get_absgrid_y(
        pg, clkdist_off_y_voff_route_phy)

    iclkdist = laygen.relplace(
        name="I" + objectname_pfix + 'CLKDIST0',
        templatename=clkdist_name,
        gridname=pg,
        refinstname=isar.name,
        direction='top',
        xy=[0, -clkdist_off_y + clkdist_off_y_voff_route],
        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
    clkdist_size = laygen.templates.get_template(clkdist_name,
                                                 libname=clkdist_libname).size
    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]) /
                 space_size[1] + 1 + clkdist_offset) * space_size[1]
    laygen.add_rect(None,
                    np.array([origin, origin + np.array([size_x, size_y])]),
                    laygen.layers['prbnd'])

    pdict_m2m3 = laygen.get_inst_pin_xy(None, None, rg_m2m3)
    pdict_m3m4 = laygen.get_inst_pin_xy(None, None, rg_m3m4)
    pdict_m4m5 = laygen.get_inst_pin_xy(None, None, rg_m4m5)
    pdict_m4m5_basic_thick = laygen.get_inst_pin_xy(None, None,
                                                    rg_m4m5_basic_thick)
    pdict_m4m5_thick = laygen.get_inst_pin_xy(None, None, rg_m4m5_thick)
    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)

    #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
    for pn, p in clkdist_pins.items():
        if pn.startswith('VDD'):
            pxy = clkdist_xy + clkdist_pins[pn]['xy']
            laygen.add_pin('VDDSAMP' + str(vddsampcnt), 'VDDSAMP:', pxy,
                           clkdist_pins[pn]['layer'])
            vddsampcnt += 1
        if pn.startswith('VSS'):
            pxy = clkdist_xy + clkdist_pins[pn]['xy']
            laygen.add_pin('VSS' + str(vsscnt), 'VSS:', pxy,
                           clkdist_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
    if input_htree == False:
        #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_xy(
            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'][0][1]
        in_y1 = in_y0 + 2
        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])
    else:
        for i in range(num_slices):
            pn = 'INP' + str(i)
            laygen.pin(name=pn,
                       layer=laygen.layers['pin'][5],
                       xy=pdict_m4m5_thick[isar.name][pn],
                       gridname=rg_m4m5_thick)
            pn = 'INM' + str(i)
            laygen.pin(name=pn,
                       layer=laygen.layers['pin'][5],
                       xy=pdict_m4m5_thick[isar.name][pn],
                       gridname=rg_m4m5_thick)

    #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.boundary_pin_from_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.boundary_pin_from_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.boundary_pin_from_rect(r,
                                          rg_m5m6,
                                          pn_out,
                                          laygen.layers['pin'][5],
                                          size=4,
                                          direction='bottom')

    # MODESEL pins
    if clkgen_mode == True:
        for i in range(num_slices):
            pn = 'MODESEL' + str(i)
            pn_out = 'MODESEL' + 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.boundary_pin_from_rect(r,
                                          rg_m5m6,
                                          pn_out,
                                          laygen.layers['pin'][5],
                                          size=4,
                                          direction='bottom')
    #osp/osm pins
    if use_offset == True:
        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(clk_cdac_bits):
            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(clkin_trackm):
        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'])

    # VREF SF pins
    if vref_sf == True:
        for pn, p in sar_pins.items():
            if pn.startswith('VREF_SF_'):
                for i in range(num_slices):
                    pxy = sar_xy + sar_pins[pn]['xy']
                    laygen.add_pin(pn, pn, pxy, sar_pins[pn]['layer'])
    # SF pins
    if use_sf == True:
        for pn, p in sar_pins.items():
            if pn.startswith('SF_'):
                for i in range(num_slices):
                    pxy = sar_xy + sar_pins[pn]['xy']
                    laygen.add_pin(pn, pn, pxy, sar_pins[pn]['layer'])

    #clkdist-sar routes (clock)
    #make virtual grids and route on the grids (assuming drc clearance of each block)
    # 1x pulsewidth
    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_xy(None, None, rg_m4m5_temp)
    # if clk_pulse == False:
    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]),
                        gridname0=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 + 0
                     ]),
                     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)

    # # clock routing for TISAR: 2x pulsewidth
    # elif clk_pulse == True:
    #     for i in range(num_slices):
    #         laygen.route_vhv(layerv0=laygen.layers['metal'][5], layerh=laygen.layers['metal'][4],
    #                         xy0=pdict_m4m5_basic_thick[isar.name]['CLK'+str(i)][0],
    #                         xy1=[pdict_m4m5[iclkdist.name]['CLKO%d<0>'%i][0][0]-3, pdict_m4m5[iclkdist.name]['DATAO<%d>'%i][0][1]-16],
    #                         track_y=pdict_m4m5[isar.name]['CLK'+str(i)][0][1]+3,
    #                         gridname0=rg_m4m5_basic_thick, layerv1=laygen.layers['metal'][5], gridname1=rg_m4m5, extendl=0, extendr=0)
    #         laygen.route_vhv(layerv0=laygen.layers['metal'][5], layerh=laygen.layers['metal'][4],
    #                      xy0=[pdict_m4m5[iclkdist.name]['CLKO%d<0>'%i][0][0]-3, pdict_m4m5[iclkdist.name]['DATAO<%d>'%i][0][1]-16],
    #                      xy1=pdict_m3m4[iclkdist.name]['DATAO<%d>'%i][0],
    #                      track_y=pdict_m4m5[iclkdist.name]['DATAO<%d>'%i][0][1]-14,
    #                      gridname0=rg_m4m5, layerv1=laygen.layers['metal'][3], gridname1=rg_m3m4, extendl=0, extendr=0)

    #sar-retimer routes (data)
    for i in range(num_slices):
        for j in range(num_bits):
            if ret_use_laygo == True:
                if j == num_bits - 1:
                    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_m4m5[iret.name]['in_' + str(i) + '<' +
                                                  str(j) + '>'][0],
                        track_y=pdict_m4m5[isar.name]['ADCOUT' + str(i) + '<' +
                                                      str(j) + '>'][0][1] +
                        j * 2 + 2,
                        gridname0=rg_m4m5,
                        layerv1=laygen.layers['metal'][5],
                        gridname1=rg_m4m5,
                        extendl=0,
                        extendr=0)
                else:
                    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,
                        gridname0=rg_m4m5,
                        layerv1=laygen.layers['metal'][3],
                        gridname1=rg_m3m4,
                        extendl=0,
                        extendr=0)
            else:
                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,
                    gridname0=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
    if num_slices > 4:
        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
    elif num_slices == 4:
        ck_phase_2 = 2
        ck_phase_1 = 0
        ck_phase_0_0 = 3
        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_xy(None, None, rg_m3m4_temp_clk)
    for i in ck_phase_buf:
        for j in range(1):
            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 + 2 * (ck_phase_buf.index(i)),
                gridname0=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 + 2 * (ck_phase_buf.index(i)),
                gridname0=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)
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_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_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_m5m6_thick,
                       routing_grid_m5m6_thick_basic,
                       num_bits=9,
                       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)

    #prboundary
    sar_size = laygen.templates.get_template(sar_name, libname=workinglib).size
    samp_size = laygen.templates.get_template(samp_name, libname=samp_lib).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]
    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)

    #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
    #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)

    #input pins (just duplicate from lower hierarchy cells)
    laygen.add_pin('CLK', 'CLK', samp_xy + samp_pins['ckin']['xy'],
                   samp_pins['ckin']['layer'])
    laygen.add_pin('INP', 'INP', samp_xy + samp_pins['inp']['xy'],
                   samp_pins['ckin']['layer'])
    laygen.add_pin('INM', 'INM', samp_xy + samp_pins['inn']['xy'],
                   samp_pins['ckin']['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' + str(bodycnt),
                       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)
    rvbb_m3 = []
    for p in pdict_m3m4[isar.name]:
        if p.startswith('VBB'):
            laygen.pin(name='bottom_body' + str(p),
                       layer=laygen.layers['pin'][3],
                       xy=pdict_m3m4[isar.name][p],
                       gridname=rg_m3m4,
                       netname='bottom_body')
def generate_tisaradc_body_core(laygen,
                                objectname_pfix,
                                ret_libname,
                                sar_libname,
                                ret_name,
                                sar_name,
                                placement_grid,
                                routing_grid_m3m4,
                                routing_grid_m4m5,
                                routing_grid_m5m6,
                                routing_grid_m5m6_thick,
                                routing_grid_m5m6_thick_basic,
                                routing_grid_m6m7_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_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
    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)
    sar_template = laygen.templates.get_template(sar_name, sar_libname)
    sar_pins = sar_template.pins
    sar_xy = isar.xy[0]
    ret_template = laygen.templates.get_template(ret_name, ret_libname)
    ret_pins = ret_template.pins
    ret_xy = iret.xy[0]

    #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
    laygen.add_rect(
        None,
        np.array([
            origin, origin + np.array(
                [max((sar_size[0], ret_size[0])), sar_size[1] + ret_size[1]])
        ]), laygen.layers['prbnd'])

    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 (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'])
            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

    #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,
        template_libname=sar_libname,
        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 + 4
    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])

    #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 pins
    for i in range(num_slices):
        pn = 'EXTCLK' + str(i)
        pn_out = 'EXTCLK' + 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')
    #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
    for i in range(num_slices):
        laygen.pin(name='VREF' + str(i) + '<0>',
                   layer=laygen.layers['pin'][6],
                   xy=pdict_m5m6_thick[isar.name]['VREF' + str(i) + '<0>'],
                   gridname=rg_m5m6_thick)
        laygen.pin(name='VREF' + str(i) + '<1>',
                   layer=laygen.layers['pin'][6],
                   xy=pdict_m5m6_thick[isar.name]['VREF' + str(i) + '<1>'],
                   gridname=rg_m5m6_thick)
        laygen.pin(name='VREF' + str(i) + '<2>',
                   layer=laygen.layers['pin'][6],
                   xy=pdict_m5m6_thick[isar.name]['VREF' + str(i) + '<2>'],
                   gridname=rg_m5m6_thick)
    #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)
    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,
        template_libname=ret_libname,
        inst_pin_prefix=[
            'clk' + str(2 * i + 1) for i in range(int(num_slices / 2))
        ],
        xy_grid_type='xgrid')
    pdict_m3m4_temp_clk = laygen.get_inst_pin_coord(None, None,
                                                    rg_m3m4_temp_clk)
    for i in range(int(num_slices / 2)):
        for j in range(4):
            laygen.route_vhv(
                layerv0=laygen.layers['metal'][5],
                layerh=laygen.layers['metal'][4],
                xy0=pdict_m4m5[isar.name]['CLKO' + str(2 * i + 1)][0],
                xy1=pdict_m3m4_temp_clk[iret.name]['clk' + str(2 * i + 1)][0],
                track_y=pdict_m4m5[isar.name]['CLKO0'][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(2 * i + 1)][0],
            xy1=np.array([
                pdict_m3m4_temp_clk[iret.name]['clk' + str(2 * i + 1)][0][0],
                pdict_m4m5[isar.name]['CLKO0'][0][1] + num_bits * 2 + 2 +
                2 * 4 + 1
            ]),
            gridname0=rg_m3m4_temp_clk)
Пример #6
0
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_m5m6_thick, routing_grid_m5m6_thick_basic,
                       num_bits=9, 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)
    # manual vref1
    #ivref1=laygen.place(name="I" + objectname_pfix + 'VREF1', templatename='sar_wsamp_vref1_manual',
    #                  gridname=pg, xy=origin, template_libname=workinglib)
    ivref1=laygen.add_inst(None, workinglib, 'sar_wsamp_vref1_manual', xy=np.array([0, 0]))

    #prboundary
    sar_size = laygen.templates.get_template(sar_name, libname=workinglib).size
    samp_size = laygen.templates.get_template(samp_name, libname=samp_lib).size
    space_size = laygen.templates.get_template(space_1x_name, libname=space_1x_lib).size
    #size_x=sar_size[0]
    size_x=sar_size[0]+2.4
    print(laygen.get_inst(ivref1.name, libname=workinglib).size)
    size_y=int((sar_size[1]+samp_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)

    #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
    #sar_xy=isar.xy[0]
    #samp_xy=isamp.xy[0]
    sar_xy=isar.xy
    samp_xy=isamp.xy

    # export_dict will be written to a yaml file for using with StdCellBase
    export_dict = {'boundaries': {'lib_name': 'tsmcN16_logic_templates',
                                  'lr_width': 8,
                                  'tb_height': 0.5},
                   'cells': {'sar_wsamp': {'cell_name': 'sar_wsamp',
                                                 'lib_name': workinglib,
                                                 'size': [40, 1]}},
                   'spaces': [{'cell_name': 'space_4x',
                               'lib_name': 'tsmcN16_logic_templates',
                               'num_col': 4},
                              {'cell_name': 'space_2x',
                               'lib_name': 'tsmcN16_logic_templates',
                               'num_col': 2}],
                   'tech_params': {'col_pitch': 0.09,
                                   'directions': ['x', 'y', 'x', 'y'],
                                   'height': 0.96,
                                   'layers': [2, 3, 4, 5],
                                   'spaces': [0.064, 0.05, 0.05, 0.05],
                                   'widths': [0.032, 0.04, 0.04, 0.04]}}
    export_ports = dict()


    #signa lroute (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', 'outp', 'outn'], xy_grid_type='xgrid')
    pdict_m5m6_thick_basic_temp_sig = laygen.get_inst_pin_xy(None, None, rg_m5m6_thick_basic_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)

    #frontend sig
    inp_y_list=[]
    inm_y_list=[]
    for pn, p in pdict_m5m6_thick_basic_temp_sig[isar.name].items():
        if pn.startswith('INP'):
            inp_y_list.append(p[0][1])
            pv=np.array([pdict_m5m6_thick_basic_temp_sig[isamp.name]['outp'][0][0], p[0][1]])
            laygen.via(None,pv, rg_m5m6_thick_basic_temp_sig)
        if pn.startswith('INM'):
            inm_y_list.append(p[0][1])
            pv=np.array([pdict_m5m6_thick_basic_temp_sig[isamp.name]['outn'][0][0], p[0][1]])
            laygen.via(None,pv, 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_m5m6_thick_basic_temp_sig[isamp.name]['outp'][0],
                         xy1=np.array([pdict_m5m6_thick_basic_temp_sig[isamp.name]['outp'][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[isamp.name]['outn'][0][0],inm_y-1]), 
                         gridname0=rg_m5m6_thick_basic_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)

    #input pins (just duplicate from lower hierarchy cells)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isamp.name, 'ckin', rg_m3m4)[1]+np.array([-2,0]), 
                 xy1=laygen.get_inst_pin_xy(isamp.name, 'ckin', rg_m3m4)[1]+np.array([-2,6]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    CLK_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='CLK', layer=laygen.layers['pin'][3], size=4, direction="top")
    #CLK_pin=laygen.add_pin('CLK', 'CLK', samp_xy+samp_pins['ckin']['xy'], samp_pins['ckin']['layer'])
    export_ports = add_to_export_ports(export_ports, CLK_pin)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isamp.name, 'inp', rg_m3m4)[1]+np.array([-1,0]), 
                 xy1=laygen.get_inst_pin_xy(isamp.name, 'inp', rg_m3m4)[1]+np.array([-1,18]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    inp_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='INP', layer=laygen.layers['pin'][3], size=4, direction="top")
    #inp_pin=laygen.add_pin('INP', 'INP', samp_xy+samp_pins['inp']['xy'], samp_pins['ckin']['layer'])
    export_ports = add_to_export_ports(export_ports, inp_pin)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isamp.name, 'inn', rg_m3m4)[1]+np.array([1,0]), 
                 xy1=laygen.get_inst_pin_xy(isamp.name, 'inn', rg_m3m4)[1]+np.array([1,18]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    inn_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='INM', layer=laygen.layers['pin'][3], size=4, direction="top")
    #inn_pin=laygen.add_pin('INM', 'INM', samp_xy+samp_pins['inn']['xy'], samp_pins['ckin']['layer'])
    export_ports = add_to_export_ports(export_ports, inn_pin)

    #laygen.route(None, laygen.layers['metal'][4],
    #             xy0=laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m3m4)[0]+np.array([0,0]),
    #             xy1=laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m3m4)[0]+np.array([-5,0]), 
    #             via0=[0, 0], gridname0=rg_m3m4)
    #laygen.route(None, laygen.layers['metal'][5],
    #             xy0=laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m4m5)[0]+np.array([-5,0]),
    #             xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m4m5)[0][0],0])+np.array([-5,9]), 
    #             via0=[0, 0], via1=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    #laygen.route(None, laygen.layers['metal'][4],
    #             xy0=np.array([laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m3m4)[0][0],0])+np.array([-5,9]), 
    #             xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m3m4)[0][0],0])+np.array([0,9]), 
    #             gridname0=rg_m3m4)
    #rpin=laygen.route(None, laygen.layers['metal'][3],
    #             xy0=np.array([laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m3m4)[0][0],0])+np.array([0,9]), 
    #             xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m3m4)[0][0],0])+np.array([0,0]), 
    #             via0=[0, 0], gridname0=rg_m3m4)
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m3m4)[0]+np.array([0,0]),
                 xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'SAOP', rg_m3m4)[0][0],laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m3m4)[0][1]]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    laygen.route(None, laygen.layers['metal'][5],
                 xy0=np.array([laygen.get_inst_pin_xy(isar.name, 'SAOP', rg_m4m5)[0][0],laygen.get_inst_pin_xy(isar.name, 'OSP', rg_m4m5)[0][1]]),
                 xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'SAOP', rg_m4m5)[0][0],laygen.get_inst_pin_xy(isamp.name, 'ckout', rg_m4m5)[0][1]]), 
                 via0=[0, 0], via1=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=np.array([laygen.get_inst_pin_xy(isar.name, 'SAOP', rg_m3m4)[0][0],laygen.get_inst_pin_xy(isamp.name, 'ckout', rg_m3m4)[0][1]]),
                 xy1=np.array([laygen.get_inst_pin_xy(isamp.name, 'inp', rg_m3m4)[0][0]-24,laygen.get_inst_pin_xy(isamp.name, 'ckout', rg_m3m4)[0][1]]), 
                 gridname0=rg_m3m4)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=np.array([laygen.get_inst_pin_xy(isamp.name, 'inp', rg_m3m4)[0][0]-24,laygen.get_inst_pin_xy(isamp.name, 'ckout', rg_m3m4)[0][1]]), 
                 xy1=laygen.get_inst_pin_xy(isamp.name, 'inp', rg_m3m4)[1]+np.array([-24,18]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    osp_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='OSP', layer=laygen.layers['pin'][3], size=4, direction="top")
    #osp_pin=laygen.add_pin('OSP', 'OSP', sar_xy+sar_pins['OSP']['xy'], sar_pins['OSP']['layer'])
    export_ports = add_to_export_ports(export_ports, osp_pin)
    #laygen.route(None, laygen.layers['metal'][4],
    #             xy0=laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m3m4)[0]+np.array([0,0]),
    #             xy1=laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m3m4)[0]+np.array([4,0]), 
    #             via0=[0, 0], gridname0=rg_m3m4)
    #laygen.route(None, laygen.layers['metal'][5],
    #             xy0=laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m4m5)[0]+np.array([4,0]),
    #             xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m4m5)[0][0],0])+np.array([4,9]), 
    #             via0=[0, 0], via1=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    #laygen.route(None, laygen.layers['metal'][4],
    #             xy0=np.array([laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m3m4)[0][0],0])+np.array([4,9]), 
    #             xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m3m4)[0][0],0])+np.array([0,9]), 
    #             gridname0=rg_m3m4)
    #rpin=laygen.route(None, laygen.layers['metal'][3],
    #             xy0=np.array([laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m3m4)[0][0],0])+np.array([0,9]), 
    #             xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m3m4)[0][0],0])+np.array([0,0]), 
    #             via0=[0, 0], gridname0=rg_m3m4)
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m3m4)[0]+np.array([0,0]),
                 xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<1>', rg_m3m4)[0][0]+5,laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m3m4)[0][1]]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    laygen.route(None, laygen.layers['metal'][5],
                 xy0=np.array([laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<1>', rg_m4m5)[0][0]+5,laygen.get_inst_pin_xy(isar.name, 'OSM', rg_m4m5)[0][1]]),
                 xy1=np.array([laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<1>', rg_m4m5)[0][0]+5,laygen.get_inst_pin_xy(isamp.name, 'ckout', rg_m4m5)[0][1]]), 
                 via0=[0, 0], via1=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=np.array([laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<1>', rg_m3m4)[0][0]+5,laygen.get_inst_pin_xy(isamp.name, 'ckout', rg_m3m4)[0][1]]),
                 xy1=np.array([laygen.get_inst_pin_xy(isamp.name, 'inn', rg_m3m4)[0][0]+24,laygen.get_inst_pin_xy(isamp.name, 'ckout', rg_m3m4)[0][1]]), 
                 gridname0=rg_m3m4)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=np.array([laygen.get_inst_pin_xy(isamp.name, 'inn', rg_m3m4)[0][0]+24,laygen.get_inst_pin_xy(isamp.name, 'ckout', rg_m3m4)[0][1]]), 
                 xy1=laygen.get_inst_pin_xy(isamp.name, 'inn', rg_m3m4)[1]+np.array([24,18]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    osn_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='OSM', layer=laygen.layers['pin'][3], size=4, direction="top")
    #osn_pin=laygen.add_pin('OSM', 'OSM', sar_xy+sar_pins['OSM']['xy'], sar_pins['OSM']['layer'])
    export_ports = add_to_export_ports(export_ports, osn_pin)
    # For fader, VREF2=VDD, VREF0=VSS
    for pn, p in sar_pins.items():
        if pn.startswith('VREF<0>'):
            pxy=sar_xy+sar_pins[pn]['xy']
            #vref0_pin=laygen.add_pin(pn, 'VREF<0>', pxy, sar_pins[pn]['layer'])
            laygen.via(None, xy=np.array([2, 0]), gridname=rg_m5m6_thick, refinstname=isar.name, refpinname=pn)
            #export_ports = add_to_export_ports(export_ports, vref0_pin)
        if pn.startswith('VREF<1>'):
            pxy=sar_xy+sar_pins[pn]['xy']
            #vref1_pin=laygen.add_pin(pn, 'VREF<1>', pxy, sar_pins[pn]['layer'])
            #export_ports = add_to_export_ports(export_ports, vref1_pin)
        if pn.startswith('VREF<2>'):
            pxy=sar_xy+sar_pins[pn]['xy']
            #vref2_pin=laygen.add_pin(pn, 'VREF<2>', pxy, sar_pins[pn]['layer'])
            laygen.via(None, xy=np.array([1, 0]), gridname=rg_m5m6_thick, refinstname=isar.name, refpinname=pn)
            #export_ports = add_to_export_ports(export_ports, vref2_pin)
    #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.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CKDSEL0<1>', rg_m4m5)[0]+np.array([0,9]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CKDSEL0<1>', rg_m4m5)[0]+np.array([4,9]), 
                 via0=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CKDSEL0<1>', rg_m3m4)[0]+np.array([4,9]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CKDSEL0<1>', rg_m3m4)[0]+np.array([4,0]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    ckd01_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='CKDSEL0<1>', layer=laygen.layers['pin'][3], size=4, direction="bottom")
    #ckd01_pin=laygen.add_pin('CKDSEL0<1>', 'CKDSEL0<1>', sar_xy+sar_pins['CKDSEL0<1>']['xy'], sar_pins['CKDSEL0<1>']['layer'])
    export_ports = add_to_export_ports(export_ports, ckd01_pin)
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CKDSEL0<0>', rg_m4m5)[0]+np.array([0,8]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CKDSEL0<0>', rg_m4m5)[0]+np.array([4,8]), 
                 via0=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CKDSEL0<0>', rg_m3m4)[0]+np.array([4,8]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CKDSEL0<0>', rg_m3m4)[0]+np.array([4,0]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    ckd00_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='CKDSEL0<0>', layer=laygen.layers['pin'][3], size=4, direction="bottom")
    #ckd00_pin=laygen.add_pin('CKDSEL0<0>', 'CKDSEL0<0>', sar_xy+sar_pins['CKDSEL0<0>']['xy'], sar_pins['CKDSEL0<0>']['layer'])
    export_ports = add_to_export_ports(export_ports, ckd00_pin)
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<1>', rg_m4m5)[0]+np.array([0,5]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<1>', rg_m4m5)[0]+np.array([4,5]), 
                 via0=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<1>', rg_m3m4)[0]+np.array([4,5]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<1>', rg_m3m4)[0]+np.array([4,0]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    ckd11_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='CKDSEL1<1>', layer=laygen.layers['pin'][3], size=4, direction="bottom")
    #ckd11_pin=laygen.add_pin('CKDSEL1<1>', 'CKDSEL1<1>', sar_xy+sar_pins['CKDSEL1<1>']['xy'], sar_pins['CKDSEL1<1>']['layer'])
    export_ports = add_to_export_ports(export_ports, ckd11_pin)
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<0>', rg_m4m5)[0]+np.array([0,6]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<0>', rg_m4m5)[0]+np.array([4,6]), 
                 via0=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<0>', rg_m3m4)[0]+np.array([4,6]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CKDSEL1<0>', rg_m3m4)[0]+np.array([4,0]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    ckd10_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='CKDSEL1<0>', layer=laygen.layers['pin'][3], size=4, direction="bottom")
    #ckd10_pin=laygen.add_pin('CKDSEL1<0>', 'CKDSEL1<0>', sar_xy+sar_pins['CKDSEL1<0>']['xy'], sar_pins['CKDSEL1<0>']['layer'])
    export_ports = add_to_export_ports(export_ports, ckd10_pin)
    #laygen.add_pin('EXTCLK', 'EXTCLK', sar_xy+sar_pins['EXTCLK']['xy'], sar_pins['EXTCLK']['layer'])
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'EXTSEL_CLK', rg_m4m5)[0]+np.array([0,6]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'EXTSEL_CLK', rg_m4m5)[0]+np.array([4,6]), 
                 via0=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'EXTSEL_CLK', rg_m3m4)[0]+np.array([4,6]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'EXTSEL_CLK', rg_m3m4)[0]+np.array([4,0]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    extsel_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='EXTSEL_CLK', layer=laygen.layers['pin'][3], size=4, direction="bottom")
    #extsel_pin=laygen.add_pin('EXTSEL_CLK', 'EXTSEL_CLK', sar_xy+sar_pins['EXTSEL_CLK']['xy'], sar_pins['EXTSEL_CLK']['layer'])
    export_ports = add_to_export_ports(export_ports, extsel_pin)
    #output pins (just duplicate from lower hierarchy cells)
    for i in range(num_bits):
        pn='ADCOUT'+'<'+str(i)+'>'
        laygen.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, pn, rg_m4m5)[0]+np.array([0,5+i]),
                 xy1=laygen.get_inst_pin_xy(isar.name, pn, rg_m4m5)[0]+np.array([4,5+i]), 
                 via0=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
        rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isar.name, pn, rg_m3m4)[0]+np.array([4,5+i]),
                 xy1=laygen.get_inst_pin_xy(isar.name, pn, rg_m3m4)[0]+np.array([4,0]), 
                 via0=[0, 0], gridname0=rg_m3m4)
        adcout_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name=pn, layer=laygen.layers['pin'][3], size=4, direction="bottom")
        #laygen.add_pin(pn, pn, sar_xy+sar_pins[pn]['xy'], sar_pins[pn]['layer'])
        export_ports = add_to_export_ports(export_ports, adcout_pin)
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CLKOUT0', rg_m4m5)[0]+np.array([0,6]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CLKOUT0', rg_m4m5)[0]+np.array([4,6]), 
                 via0=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CLKOUT0', rg_m3m4)[0]+np.array([4,6]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CLKOUT0', rg_m3m4)[0]+np.array([4,0]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    clko0_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='CLKO0', netname='CLKO', layer=laygen.layers['pin'][3], size=4, direction="bottom")
    #clko0_pin=laygen.add_pin('CLKO0', 'CLKO', sar_xy+sar_pins['CLKOUT0']['xy'], sar_pins['CLKOUT0']['layer'])
    export_ports = add_to_export_ports(export_ports, clko0_pin)
    laygen.route(None, laygen.layers['metal'][4],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CLKOUT1', rg_m4m5)[0]+np.array([0,8]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CLKOUT1', rg_m4m5)[0]+np.array([4,8]), 
                 via0=[0, 0], endstyle1="extend", gridname0=rg_m4m5)
    rpin=laygen.route(None, laygen.layers['metal'][3],
                 xy0=laygen.get_inst_pin_xy(isar.name, 'CLKOUT1', rg_m3m4)[0]+np.array([4,8]),
                 xy1=laygen.get_inst_pin_xy(isar.name, 'CLKOUT1', rg_m3m4)[0]+np.array([4,0]), 
                 via0=[0, 0], gridname0=rg_m3m4)
    clko1_pin=laygen.boundary_pin_from_rect(rpin, gridname=rg_m3m4, name='CLKO2', netname='CLKO', layer=laygen.layers['pin'][3], size=4, direction="bottom")
    #clko1_pin=laygen.add_pin('CLKO1', 'CLKO', sar_xy+sar_pins['CLKOUT1']['xy'], sar_pins['CLKOUT1']['layer'])
    export_ports = add_to_export_ports(export_ports, clko1_pin)
    #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]
            vdd_pin=laygen.pin(name='VDDSAR' + str(vddcnt), layer=laygen.layers['pin'][6], xy=xy0, gridname=rg_m5m6_thick, netname='VDDSAR')
            vddcnt+=1
            export_ports = add_to_export_ports(export_ports, vdd_pin)
        if p.startswith('VSS'):
            xy0=pdict_m5m6_thick[isar.name][p]
            vss_pin=laygen.pin(name='VSSSAR' + str(vsscnt), layer=laygen.layers['pin'][6], xy=xy0, gridname=rg_m5m6_thick, netname='VSS:')
            vsscnt+=1
            export_ports = add_to_export_ports(export_ports, vss_pin)
    #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'], 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
    for p in pdict_m5m6_thick_temp_samp[isamp.name]:
        if p.startswith('VDD'):
            xy0=pdict_m5m6_thick_temp_samp[isamp.name][p]
            vddsamp_pin=laygen.pin(name='VDDSAMP' + str(vddcnt), layer=laygen.layers['pin'][6], xy=xy0, gridname=rg_m5m6_thick_temp_samp, netname='VDDSAMP')
            vddcnt+=1
            export_ports = add_to_export_ports(export_ports, vddsamp_pin)
        if p.startswith('VSS'):
            xy0=pdict_m5m6_thick_temp_samp[isamp.name][p]
            vsssamp_pin=laygen.pin(name='VSSSAMP' + str(vsscnt), layer=laygen.layers['pin'][6], xy=xy0, gridname=rg_m5m6_thick_temp_samp, netname='VSS:')
            vsscnt+=1
            export_ports = add_to_export_ports(export_ports, vsssamp_pin)

    export_dict['cells']['sar_wsamp']['ports'] = export_ports
    export_dict['cells']['sar_wsamp']['size_um'] = [float(int(size_x*1e3))/1e3, float(int(size_y*1e3))/1e3]
    #export_dict['cells']['clk_dis_N_units']['num_ways'] = num_ways
    # print('export_dict:')
    # pprint(export_dict)
    # save_path = path.dirname(path.dirname(path.realpath(__file__))) + '/dsn_scripts/'
    save_path = workinglib 
    #if path.isdir(save_path) == False:
    #    mkdir(save_path)
    with open(save_path + '_int.yaml', 'w') as f:
        yaml.dump(export_dict, f, default_flow_style=False)
Пример #7
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'])