def draw_layout(self): lch = self.params['lch'] ptap_w = self.params['ptap_w'] ntap_w = self.params['ntap_w'] w_dict = self.params['w_dict'] th_dict = self.params['th_dict'] seg_dict = self.params['seg_dict'] ndum = self.params['ndum'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] show_pins = self.params['show_pins'] top_layer = self.params['top_layer'] nw_list = [w_dict['n']] pw_list = [w_dict['p']] * 2 nth_list = [th_dict['n']] pth_list = [th_dict['p']] * 2 tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) hm_layer = self.mos_conn_layer + 1 wtr_in = tr_manager.get_width(hm_layer, 'in') wtr_out = tr_manager.get_width(hm_layer, 'out') wtr_en = tr_manager.get_width(hm_layer, 'en') wtr_mid = tr_manager.get_width(hm_layer, 'mid') sp_io = tr_manager.get_space(hm_layer, ('in', 'out')) sp_mid = tr_manager.get_space(hm_layer, ('in', 'mid')) seg_invp = seg_dict['invp'] seg_enp = seg_dict['enp'] seg_invn = seg_dict['invn'] seg_enn = seg_dict['enn'] seg_single = max(seg_invp, seg_invn, seg_enp) if (seg_invp - seg_invn) % 4 != 0 or (seg_enp - seg_invp) % 4 != 0: raise ValueError( 'We assume seg_invp/seg_invn/seg_enp differ by multiples of 4.' ) # compute total number of fingers ana_info = AnalogBaseInfo(self.grid, lch, 0, top_layer=top_layer) if ana_info.abut_analog_mos: fg_sep = 0 else: fg_sep = ana_info.min_fg_sep enp_idx = ndum + (seg_single - seg_enp) // 2 invp_idx = ndum + (seg_single - seg_invp) // 2 invn_idx = ndum + (seg_single - seg_invn) // 2 enn_idx = max(enp_idx + seg_enp, invn_idx + seg_invn + fg_sep) seg_tot = max(seg_single + 2 * ndum, enn_idx + seg_enn + ndum) # draw transistor rows ng_tracks = [wtr_out + sp_io] pg_tracks = [wtr_in, wtr_en] pds_tracks = [sp_mid + wtr_mid, 0] nds_tracks = [0] p_orient = ['R0', 'MX'] n_orient = ['MX'] self.draw_base( lch, seg_tot, ptap_w, ntap_w, nw_list, nth_list, pw_list, pth_list, ng_tracks=ng_tracks, nds_tracks=nds_tracks, pg_tracks=pg_tracks, pds_tracks=pds_tracks, n_orientations=n_orient, p_orientations=p_orient, top_layer=top_layer, ) # draw transistors invn = self.draw_mos_conn('nch', 0, invn_idx, seg_invn, 0, 2, s_net='', d_net='out') enn = self.draw_mos_conn('nch', 0, enn_idx, seg_enn, 0, 2, s_net='', d_net='out') invp = self.draw_mos_conn('pch', 0, invp_idx, seg_invp, 2, 0, s_net='mid', d_net='out') enp = self.draw_mos_conn('pch', 1, enp_idx, seg_enp, 0, 2, s_net='mid', d_net='') # connect supplies self.connect_to_substrate('ntap', enp['d']) self.connect_to_substrate('ptap', invn['s']) self.connect_to_substrate('ptap', enn['s']) # connect output loc = tr_manager.align_wires(hm_layer, ['out'], ng_tracks[0], alignment=1)[0] tid = self.make_track_id('nch', 0, 'g', loc, width=wtr_out) warr = self.connect_to_tracks([invp['d'], invn['d'], enn['d']], tid) self.add_pin('out', warr, show=show_pins) # connect input loc = tr_manager.align_wires(hm_layer, ['in'], pg_tracks[0], alignment=1)[0] tid = self.make_track_id('pch', 0, 'g', loc, width=wtr_in) warr = self.connect_to_tracks([invp['g'], invn['g']], tid) self.add_pin('in', warr, show=show_pins) # connect mid loc = tr_manager.align_wires(hm_layer, ['mid'], pds_tracks[0], alignment=1)[0] tid = self.make_track_id('pch', 0, 'ds', loc, width=wtr_mid) self.connect_to_tracks([invp['s'], enp['s']], tid) # connect enable loc = tr_manager.align_wires(hm_layer, ['en'], pg_tracks[1], alignment=1)[0] tid = self.make_track_id('pch', 1, 'g', loc, width=wtr_en) warr = self.connect_to_tracks([enp['g'], enn['g']], tid) self.add_pin('enb', warr, show=show_pins) # draw dummies ptap_wire_arrs, ntap_wire_arrs = self.fill_dummy() # export supplies self.add_pin('VSS', ptap_wire_arrs) self.add_pin('VDD', ntap_wire_arrs) # compute schematic parameters self._sch_params = dict( lch=lch, w_dict=w_dict, th_dict=th_dict, seg_dict=seg_dict, dum_info=self.get_sch_dummy_info(), )
def draw_layout(self): # type: () -> None config_file = self.params['config_file'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] top_layer = self.params['top_layer'] hm_sp_sig = self.params['hm_sp_sig'] hm_mid_idx_off = self.params['hm_mid_idx_off'] show_pins = self.params['show_pins'] tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) # use standard cell routing grid self.update_routing_grid() self.set_draw_boundaries(True) # create masters tap_params = dict(cell_name='tap_pwr', config_file=config_file) tap_master = self.new_template(params=tap_params, temp_cls=StdCellTemplate) flop_params = dict(cell_name='dff_1x', config_file=config_file) flop_master = self.new_template(params=flop_params, temp_cls=StdCellTemplate) inv_params = dict(cell_name='inv_clk_16x', config_file=config_file) inv_master = self.new_template(params=inv_params, temp_cls=StdCellTemplate) # place instances flop_ncol = flop_master.std_size[0] tap_ncol = tap_master.std_size[0] inv_ncol = inv_master.std_size[0] space_ncol = 2 tap_list = [self.add_std_instance(tap_master, 'XTAP00', loc=(0, 0)), self.add_std_instance(tap_master, 'XTAP01', loc=(0, 1))] xcur = tap_ncol + space_ncol ff_bot0 = self.add_std_instance(flop_master, 'XFFB0', loc=(xcur, 0)) ff_bot1 = self.add_std_instance(flop_master, 'XFFB1', loc=(xcur + flop_ncol, 0)) ff_top0 = self.add_std_instance(flop_master, 'XFFT0', loc=(xcur, 1)) ff_top1 = self.add_std_instance(flop_master, 'XFFT1', loc=(xcur + flop_ncol, 1)) xcur += 2 * flop_ncol inv_bot0 = self.add_std_instance(inv_master, 'XINVB0', loc=(xcur, 0)) inv_bot1 = self.add_std_instance(inv_master, 'XINVB1', loc=(xcur + inv_ncol, 0)) inv_top0 = self.add_std_instance(inv_master, 'XINVT0', loc=(xcur, 1)) inv_top1 = self.add_std_instance(inv_master, 'XINVT1', loc=(xcur + inv_ncol, 1)) xcur += 2 * inv_ncol + space_ncol tap_list.append(self.add_std_instance(tap_master, 'XTAP10', loc=(xcur, 0))) tap_list.append(self.add_std_instance(tap_master, 'XTAP11', loc=(xcur, 1))) # set template size and draw space/boundaries self.set_std_size((xcur + tap_ncol, 2), top_layer=top_layer) self.fill_space() self.draw_boundaries() # connect signals warr_dict = {} for inst, name in ((ff_bot0, 'FB0'), (ff_bot1, 'FB1'), (ff_top0, 'FT0'), (ff_top1, 'FT1'), (inv_bot0, 'IB0'), (inv_bot1, 'IB1'), (inv_top0, 'IT0'), (inv_top1, 'IT1')): warr_dict['%s%s' % (name, 'I')] = inst.get_all_port_pins('I')[0] warr_dict['%s%s' % (name, 'O')] = inst.get_all_port_pins('O')[0] port_layer = warr_dict['FB0I'].layer_id hm_layer = port_layer + 1 vm_layer = hm_layer + 1 if vm_layer < self.grid.top_private_layer + 1: raise ValueError('This generator assumes layer %d is public.' % vm_layer) mid_tidx = self.grid.coord_to_nearest_track(hm_layer, self.bound_box.height_unit // 2, half_track=True, mode=0, unit_mode=True) mid_tidx += hm_mid_idx_off top_tidx = mid_tidx + 1 + hm_sp_sig bot_tidx = mid_tidx - 1 - hm_sp_sig bot_tid = TrackID(hm_layer, bot_tidx) top_tid = TrackID(hm_layer, top_tidx) mid_tid = TrackID(hm_layer, mid_tidx) self.connect_to_tracks([warr_dict['FB0O'], warr_dict['FB1I'], warr_dict['IB0I']], bot_tid) self.connect_to_tracks([warr_dict['FT0O'], warr_dict['FT1I']], top_tid) self.connect_to_tracks([warr_dict['FT1O'], warr_dict['FB0I'], warr_dict['IT0I']], mid_tid) self.connect_to_tracks([warr_dict['IB0O'], warr_dict['IB1I']], bot_tid) self.connect_to_tracks([warr_dict['IT0O'], warr_dict['IT1I']], top_tid) # gather/connect supply wires on port layer vdd_warrs, vss_warrs = [], [] for inst in tap_list: vdd_warrs.extend(inst.get_all_port_pins('VDD')) vss_warrs.extend(inst.get_all_port_pins('VSS')) vdd_warrs = self.connect_wires(vdd_warrs) vss_warrs = self.connect_wires(vss_warrs) # connect rst w_rst = tr_manager.get_width(hm_layer, 'rst') rst = warr_dict['FT0I'] rst = self.connect_to_tracks(rst, top_tid, min_len_mode=True) vm_tidx = self.grid.coord_to_nearest_track(vm_layer, rst.middle, half_track=True) vm_tid = TrackID(vm_layer, vm_tidx, width=w_rst) self.add_pin('rst', self.connect_to_tracks(rst, vm_tid, min_len_mode=0), show=show_pins) # connect enables hm_w_en = tr_manager.get_width(hm_layer, 'en') vm_w_en = tr_manager.get_width(vm_layer, 'en') for name, pin in (('IT1O', 'rstp'), ('IB1O', 'rstn')): warr = warr_dict[name] tid = warr.track_id.base_index coord = self.grid.track_to_coord(port_layer, tid, unit_mode=True) vm_tidx = self.grid.coord_to_nearest_track(vm_layer, coord, half_track=True, mode=1, unit_mode=True) warrs = self.connect_with_via_stack(warr, TrackID(vm_layer, vm_tidx, width=vm_w_en), tr_w_list=[hm_w_en], min_len_mode_list=0) self.add_pin(pin, warrs[-1], show=show_pins) # connect clocks and supplies clkp = [ff_top0.get_all_port_pins('CLK')[0], ff_top1.get_all_port_pins('CLK')[0]] clkn = [ff_bot0.get_all_port_pins('CLK')[0], ff_bot1.get_all_port_pins('CLK')[0]] top_tidx = self.grid.coord_to_nearest_track(hm_layer, self.bound_box.height_unit, half_track=False, unit_mode=True, mode=1) bot_tidx = -1 hm_w_clk = tr_manager.get_width(hm_layer, 'clk') vm_w_clk = tr_manager.get_width(vm_layer, 'clk') ntr, ploc_list = tr_manager.place_wires(hm_layer, ['clk', 'sup', 'sup'], start_idx=top_tidx) _, nloc_list = tr_manager.place_wires(hm_layer, ['sup', 'sup', 'clk'], start_idx=bot_tidx - ntr) clk_tidx = None for name, warrs, tidx, wdir in [('clkp', clkp, ploc_list[0], 1), ('clkn', clkn, nloc_list[2], -1)]: tid = TrackID(hm_layer, tidx, width=hm_w_clk) warr = self.connect_to_tracks(warrs, tid, min_len_mode=0) if clk_tidx is None: clk_tidx = self.grid.coord_to_nearest_track(vm_layer, warr.middle, mode=0, half_track=True) tid = TrackID(vm_layer, clk_tidx, width=vm_w_clk) warr = self.connect_to_tracks(warr, tid, min_len_mode=wdir) self.add_pin(name, warr, show=show_pins) hm_w_sup = tr_manager.get_width(hm_layer, 'sup') vm_w_sup = tr_manager.get_width(vm_layer, 'sup') vss_list = [self.connect_to_tracks(vss_warrs, TrackID(hm_layer, ploc_list[1], width=hm_w_sup)), self.connect_to_tracks(vss_warrs, TrackID(hm_layer, nloc_list[1], width=hm_w_sup))] vdd_list = [self.connect_to_tracks(vdd_warrs, TrackID(hm_layer, ploc_list[2], width=hm_w_sup)), self.connect_to_tracks(vdd_warrs, TrackID(hm_layer, nloc_list[0], width=hm_w_sup))] _, sup_loc_list = tr_manager.place_wires(vm_layer, ['clk', 'sup', 'sup']) vdd_idx = sup_loc_list[2] + clk_tidx - sup_loc_list[0] vss_idx = sup_loc_list[1] + clk_tidx - sup_loc_list[0] vss = self.connect_to_tracks(vss_list, TrackID(vm_layer, vss_idx, width=vm_w_sup)) vdd = self.connect_to_tracks(vdd_list, TrackID(vm_layer, vdd_idx, width=vm_w_sup)) # export pins self.add_pin('VDD', vdd, show=show_pins) self.add_pin('VSS', vss, show=show_pins) # compute schematic parameters self._sch_params = dict( flop_info=flop_master.get_sch_master_info(), inv_info=inv_master.get_sch_master_info(), )
def draw_layout(self): """Draw the layout of a dynamic latch chain. """ lch = self.params['lch'] ptap_w = self.params['ptap_w'] ntap_w = self.params['ntap_w'] w_dict = self.params['w_dict'] th_dict = self.params['th_dict'] seg_dict = self.params['seg_dict'] fg_dum = self.params['fg_dum'] flip_out_sd = self.params['flip_out_sd'] guard_ring_nf = self.params['guard_ring_nf'] top_layer = self.params['top_layer'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] show_pins = self.params['show_pins'] options = self.params['options'] if options is None: options = {} # make SerdesRXBaseInfo and compute total number of fingers. serdes_info = SerdesRXBaseInfo(self.grid, lch, guard_ring_nf, top_layer=top_layer) diffamp_info = serdes_info.get_diffamp_info(seg_dict, fg_dum=fg_dum, flip_out_sd=flip_out_sd) fg_tot = diffamp_info['fg_tot'] # construct number of tracks dictionary row_names = ['load', 'casc', 'in', 'sw', 'en', 'tail'] gtr_lists = [['bias'], ['bias'], ['in', 'in'], ['bias'], ['bias'], ['bias']] dtr_lists = [['out', 'out'], ['mid'], [], ['vdd'], ['tail'], ['tail']] dtr_names = [['outp', 'outn'], [('midp', 'midn')], [], ['vddn'], ['tail'], ['tail']] # rename tail row drain net name if enable row exists if w_dict.get('en', 0) > 0: dtr_lists[-1][0] = 'foot' hm_layer = self.mos_conn_layer + 1 tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) g_ntr_dict, ds_ntr_dict, tr_indices = {}, {}, {} for row_name, gtr_list, dtr_list, dtr_name_list in \ zip(row_names, gtr_lists, dtr_lists, dtr_names): w_row = w_dict.get(row_name, 0) if w_row > 0: num_gtr, _ = tr_manager.place_wires(hm_layer, gtr_list) if dtr_list: dtr_sp = tr_manager.get_space(hm_layer, dtr_list[0]) num_dtr, didx_list = tr_manager.place_wires( hm_layer, dtr_list, start_idx=dtr_sp) for dtr_name, dtr_idx in zip(dtr_name_list, didx_list): if isinstance(dtr_name, tuple): for dtr_n in dtr_name: tr_indices[dtr_n] = dtr_idx else: tr_indices[dtr_name] = dtr_idx num_dtr += 2 * dtr_sp else: num_dtr = 1 g_ntr_dict[row_name] = num_gtr ds_ntr_dict[row_name] = num_dtr # draw transistor rows self.draw_rows(lch, fg_tot, ptap_w, ntap_w, w_dict, th_dict, g_ntr_dict, ds_ntr_dict, guard_ring_nf=guard_ring_nf, **options) # draw diffamp amp_ports, _ = self.draw_diffamp(0, seg_dict, tr_widths=tr_widths, tr_spaces=tr_spaces, tr_indices=tr_indices, fg_dum=fg_dum, flip_out_sd=flip_out_sd) # add dummies and pins vss_warrs, vdd_warrs = self.fill_dummy() self.add_pin('VSS', vss_warrs) self.add_pin('VDD', vdd_warrs) hide_pins = {'midp', 'midn', 'tail', 'foot'} for pname, warrs in amp_ports.items(): self.add_pin(pname, warrs, show=show_pins and pname not in hide_pins) # compute schematic parameters self._sch_params = dict( lch=lch, w_dict=w_dict.copy(), th_dict=th_dict.copy(), seg_dict=seg_dict.copy(), dum_info=self.get_sch_dummy_info(), )
def draw_layout(self): # get parameters tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] show_pins = self.params['show_pins'] export_probe = self.params['export_probe'] sum_master, end_row_master, div2_master, div3_master = self._make_masters( ) end_row_box = end_row_master.array_box sum_arr_box = sum_master.array_box # place instances vm_layer = top_layer = sum_master.top_layer bot_row = self.add_instance(end_row_master, 'XROWB', loc=(0, 0), unit_mode=True) y1 = ycur = end_row_box.top_unit inst1 = self.add_instance(sum_master, 'X1', loc=(0, ycur), unit_mode=True) ycur += sum_arr_box.top_unit + sum_arr_box.top_unit inst2 = self.add_instance(sum_master, 'X2', loc=(0, ycur), orient='MX', unit_mode=True) inst0 = self.add_instance(sum_master, 'X0', loc=(0, ycur), unit_mode=True) y3 = ycur = ycur + sum_arr_box.top_unit + sum_arr_box.top_unit inst3 = self.add_instance(sum_master, 'X3', loc=(0, ycur), orient='MX', unit_mode=True) ycur += end_row_box.top_unit top_row = self.add_instance(end_row_master, 'XROWT', loc=(0, ycur), orient='MX', unit_mode=True) inst_list = [inst0, inst1, inst2, inst3] self.fill_box = inst1.fill_box.merge(inst3.fill_box) blockage_y = [0, top_row.bound_box.top_unit] div_grp_x0, div_grp_y0 = sum_master.div_grp_loc div_grp_x = div_grp_x0 - div3_master.array_box.left_unit div_grp_y3 = div_grp_y0 + y1 div_grp_y2 = y3 - div_grp_y0 div3_inst = self.add_instance(div3_master, 'XDIV3', loc=(div_grp_x, div_grp_y3), unit_mode=True) div2_inst = self.add_instance(div2_master, 'XDIV2', loc=(div_grp_x, div_grp_y2), orient='MX', unit_mode=True) tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) # re-export supply pins vdd_list = list( chain(div3_inst.port_pins_iter('VDD'), div2_inst.port_pins_iter('VDD'), *(inst.port_pins_iter('VDD') for inst in inst_list))) vss_list = list( chain(div3_inst.port_pins_iter('VSS'), div2_inst.port_pins_iter('VSS'), *(inst.port_pins_iter('VSS') for inst in inst_list))) vdd_list = self.connect_wires(vdd_list) vss_list = self.connect_wires(vss_list) self.add_pin('VDD', vdd_list, show=show_pins) self.add_pin('VSS', vss_list, show=show_pins) # draw wires # compute output wire tracks xr = vdd_list[0].upper_unit tidx0 = self.grid.find_next_track(vm_layer, xr, mode=1, unit_mode=True) _, out_locs = tr_manager.place_wires( vm_layer, [1, 'out', 'out', 1, 'out', 'out', 1, 'out', 'out', 1], start_idx=tidx0) # re-export ports, and gather wires outp_warrs = [[], [], [], []] outn_warrs = [[], [], [], []] en_warrs = [[div2_inst.get_pin('qb')], [div3_inst.get_pin('qb')], [div2_inst.get_pin('q')], [div3_inst.get_pin('q')]] biasf_warrs = [] clk_warrs = [ list( chain(div2_inst.port_pins_iter('clkp'), div3_inst.port_pins_iter('clkp'))), list( chain(div2_inst.port_pins_iter('clkn'), div3_inst.port_pins_iter('clkn'))) ] biasd_warrs = [] biasm_warrs = [] for idx, inst in enumerate(inst_list): pidx = (idx + 1) % 4 nidx = (idx - 1) % 4 outp_warrs[idx].extend(inst.port_pins_iter('outp_m')) outn_warrs[idx].extend(inst.port_pins_iter('outn_m')) outp_warrs[pidx].extend(inst.port_pins_iter('fbp')) outn_warrs[pidx].extend(inst.port_pins_iter('fbn')) biasf_warrs.extend(inst.port_pins_iter('biasp_f')) biasm_warrs.extend(inst.port_pins_iter('biasp_m')) biasd_warrs.extend(inst_list[pidx].port_pins_iter('biasn_d')) for off in range(4): en_pin = 'en<%d>' % off en_idx = (off + idx + 1) % 4 if inst.has_port(en_pin): en_warrs[en_idx].extend(inst.port_pins_iter(en_pin)) self.reexport(inst.get_port('inp'), net_name='inp<%d>' % pidx, show=show_pins) self.reexport(inst.get_port('inn'), net_name='inn<%d>' % pidx, show=show_pins) self.reexport(inst.get_port('outp_main'), net_name='outp<%d>' % idx, show=show_pins) self.reexport(inst.get_port('outn_main'), net_name='outn<%d>' % idx, show=show_pins) self.reexport(inst.get_port('outp_d'), net_name='outp_d<%d>' % nidx, show=show_pins) self.reexport(inst.get_port('outn_d'), net_name='outn_d<%d>' % nidx, show=show_pins) if idx % 2 == 1: clk_warrs[0].extend(inst.port_pins_iter('clkp')) clk_warrs[1].extend(inst.port_pins_iter('clkn')) else: clk_warrs[1].extend(inst.port_pins_iter('clkp')) clk_warrs[0].extend(inst.port_pins_iter('clkn')) # connect output wires out_map = [4, 4, 1, 1] vm_w_out = tr_manager.get_width(vm_layer, 'out') for outp, outn, idx in zip(outp_warrs, outn_warrs, out_map): self.connect_differential_tracks(outp, outn, vm_layer, out_locs[idx], out_locs[idx + 1], width=vm_w_out) # draw enable wires en_locs = sum_master.en_locs vm_w_en = tr_manager.get_width(vm_layer, 'en') for en_idx, (tr_idx, en_warr) in enumerate(zip(en_locs, en_warrs)): en_warr = self.connect_to_tracks( en_warr, TrackID(vm_layer, tr_idx, width=vm_w_en)) if export_probe: self.add_pin('en<%d>' % en_idx, en_warr, show=show_pins) # draw clock/bias_f wires vm_w_clk = tr_manager.get_width(vm_layer, 'clk') start_idx0 = en_locs[3] - (vm_w_en - 1) / 2 ntr = out_locs[0] + 1 - start_idx0 try: clk_locs = tr_manager.spread_wires( vm_layer, ['en', 1, 'clk', 'clk', 'clk', 'clk', 1], ntr, ('clk', ''), alignment=1, start_idx=start_idx0) except ValueError: sp_min = self.grid.get_num_space_tracks(vm_layer, vm_w_clk, half_space=True) clk_locs = tr_manager.spread_wires( vm_layer, ['en', 1, 'clk', 'clk', 'clk', 'clk', 1], ntr, ('clk', ''), alignment=1, start_idx=start_idx0, sp_override={('clk', 'clk'): { vm_layer: sp_min }}) clkn, clkp = self.connect_differential_tracks(clk_warrs[1], clk_warrs[0], vm_layer, clk_locs[2], clk_locs[5], width=vm_w_clk) bf0, bf3 = self.connect_differential_tracks(biasf_warrs[0], biasf_warrs[3], vm_layer, clk_locs[3], clk_locs[4], width=vm_w_clk) bf2, bf1 = self.connect_differential_tracks(biasf_warrs[2], biasf_warrs[1], vm_layer, clk_locs[3], clk_locs[4], width=vm_w_clk) self.add_pin('clkp', clkp, show=show_pins) self.add_pin('clkn', clkn, show=show_pins) self.add_pin('bias_f<0>', bf0, show=show_pins, edge_mode=1) blockage_y[1] = min(blockage_y[1], bf0.lower_unit) self.add_pin('bias_f<1>', bf1, show=show_pins, edge_mode=-1) blockage_y[0] = max(blockage_y[0], bf1.upper_unit) self.add_pin('bias_f<2>', bf2, show=show_pins, edge_mode=-1) self.add_pin('bias_f<3>', bf3, show=show_pins, edge_mode=1) # compute bias_m/bias_d wires locations shield_tidr = tr_manager.get_next_track(vm_layer, en_locs[0], 'en', 1, up=False) sp_clk = clk_locs[3] - clk_locs[2] sp_clk_shield = clk_locs[2] - clk_locs[1] right_tidx = shield_tidr - sp_clk_shield bias_locs = [right_tidx + idx * sp_clk for idx in range(-3, 1, 1)] shield_tidl = bias_locs[0] - sp_clk_shield # draw shields self._blockage_intvs = [] sh_tid = TrackID(vm_layer, shield_tidl, num=2, pitch=shield_tidr - shield_tidl) sh_warrs = self.connect_to_tracks(vss_list, sh_tid, unit_mode=True) tr_lower, tr_upper = sh_warrs.lower_unit, sh_warrs.upper_unit sh_box = sh_warrs.get_bbox_array(self.grid).get_overall_bbox() self._blockage_intvs.append(sh_box.get_interval('x', unit_mode=True)) self.add_pin('VSS', sh_warrs, show=show_pins) sh_pitch = out_locs[3] - out_locs[0] out_sh_tid = TrackID(vm_layer, out_locs[0] + sh_pitch, num=2, pitch=sh_pitch) sh_warrs = self.connect_to_tracks(vdd_list, out_sh_tid, track_lower=tr_lower, track_upper=tr_upper, unit_mode=True) self.add_pin('VDD', sh_warrs, show=show_pins) clk_sh_tid = TrackID(vm_layer, clk_locs[1], num=2, pitch=out_locs[0] - clk_locs[1]) sh_warrs = self.connect_to_tracks(vdd_list, clk_sh_tid, track_lower=tr_lower, track_upper=tr_upper, unit_mode=True) sh_box = sh_warrs.get_bbox_array(self.grid).get_overall_bbox() self._blockage_intvs.append(sh_box.get_interval('x', unit_mode=True)) self.add_pin('VDD', sh_warrs, show=show_pins) self.add_pin('VDD_ext', sh_warrs, show=False) bm0, bm3 = self.connect_differential_tracks(biasm_warrs[0], biasm_warrs[3], vm_layer, bias_locs[0], bias_locs[3], width=vm_w_clk) bm2, bm1 = self.connect_differential_tracks(biasm_warrs[2], biasm_warrs[1], vm_layer, bias_locs[0], bias_locs[3], width=vm_w_clk) bd2, bd3 = self.connect_differential_tracks(biasd_warrs[2], biasd_warrs[3], vm_layer, bias_locs[1], bias_locs[2], width=vm_w_clk) bd0, bd1 = self.connect_differential_tracks(biasd_warrs[0], biasd_warrs[1], vm_layer, bias_locs[1], bias_locs[2], width=vm_w_clk) self.add_pin('bias_m<0>', bm0, show=show_pins, edge_mode=1) blockage_y[1] = min(blockage_y[1], bm0.lower_unit) self.add_pin('bias_m<1>', bm1, show=show_pins, edge_mode=-1) blockage_y[0] = max(blockage_y[0], bm1.upper_unit) self.add_pin('bias_m<2>', bm2, show=show_pins, edge_mode=-1) self.add_pin('bias_m<3>', bm3, show=show_pins, edge_mode=1) self.add_pin('bias_d<0>', bd0, show=show_pins, edge_mode=-1) self.add_pin('bias_d<1>', bd1, show=show_pins, edge_mode=-1) blockage_y[0] = max(blockage_y[0], bd1.upper_unit) self.add_pin('bias_d<2>', bd2, show=show_pins, edge_mode=1) blockage_y[1] = min(blockage_y[1], bd2.lower_unit) self.add_pin('bias_d<3>', bd3, show=show_pins, edge_mode=1) # set size bnd_box = bot_row.bound_box.merge(top_row.bound_box) bnd_xr = self.grid.track_to_coord(vm_layer, out_locs[0] + 2 * sh_pitch + 0.5, unit_mode=True) bnd_box = bnd_box.extend(x=bnd_xr, unit_mode=True) self.set_size_from_bound_box(top_layer, bnd_box) self.array_box = bnd_box self.add_cell_boundary(bnd_box) # mark blockages res = self.grid.resolution for xl, xu in self._blockage_intvs: self.mark_bbox_used( vm_layer, BBox(xl, bnd_box.bottom_unit, xu, blockage_y[0], res, unit_mode=True)) self.mark_bbox_used( vm_layer, BBox(xl, blockage_y[1], xu, bnd_box.top_unit, res, unit_mode=True)) # draw en_div/scan wires tr_scan = shield_tidl - 1 tr_en2 = tr_scan - sp_clk_shield tr_en_div = tr_en2 - sp_clk_shield scan_tid = TrackID(vm_layer, tr_scan) en2_tid = TrackID(vm_layer, tr_en2, width=vm_w_clk) en_div_tid = TrackID(vm_layer, tr_en_div) scan3 = self.connect_to_tracks(div3_inst.get_pin('scan_s'), scan_tid, min_len_mode=1) scan2 = self.connect_to_tracks(div2_inst.get_pin('scan_s'), scan_tid, min_len_mode=-1) self.add_pin('scan_div<3>', scan3, show=show_pins) self.add_pin('scan_div<2>', scan2, show=show_pins) self.connect_to_tracks( [div3_inst.get_pin('en2'), div2_inst.get_pin('en2')], en2_tid) en_div = self.connect_to_tracks(div3_inst.get_pin('in'), en_div_tid, min_len_mode=1) self.add_pin('en_div', en_div, show=show_pins) # set schematic parameters self._sch_params = dict( sum_params=sum_master.sch_params['sum_params'], lat_params=sum_master.sch_params['lat_params'], div_params=div3_master.sch_params, export_probe=export_probe, ) inp = sum_master.get_port('inp').get_pins()[0].track_id inn = sum_master.get_port('inn').get_pins()[0].track_id outp_m = sum_master.get_port('outp_m').get_pins()[0].track_id outn_m = sum_master.get_port('outn_m').get_pins()[0].track_id self._in_tr_info = (inp.base_index, inn.base_index, inp.width) self._out_tr_info = (outp_m.base_index, outn_m.base_index, outp_m.width) self._data_tr_info = sum_master.data_tr_info self._div_tr_info = sum_master.div_tr_info self._sum_row_info = sum_master.sum_row_info self._lat_row_info = sum_master.lat_row_info
def draw_layout(self): """Draw the layout of a dynamic latch chain. """ w_dict = self.params['w_dict'] th_dict = self.params['th_dict'] seg_dict = self.params['seg_dict'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] top_layer = self.params['top_layer'] draw_boundaries = self.params['draw_boundaries'] end_mode = self.params['end_mode'] min_height = self.params['min_height'] sup_tids = self.params['sup_tids'] clk_tidx = self.params['clk_tidx'] show_pins = self.params['show_pins'] export_probe = self.params['export_probe'] and show_pins n_in = seg_dict['in'] n_tail = seg_dict['tail'] n_ninv = seg_dict['ninv'] n_pinv = seg_dict['pinv'] n_rst = seg_dict['rst'] n_dum = seg_dict['dum'] n_nand = seg_dict['nand'] n_buf = seg_dict['buf'] blk_sp = seg_dict.get('sp', 2) if n_tail > n_in or n_in > n_ninv: raise ValueError( 'This generator only works for seg_tail <= seg_in <= seg_ninv') # error checking for name, val in seg_dict.items(): if name != 'sp' and (val % 2 != 0 or val <= 0): raise ValueError('seg_%s = %d is not even or positive.' % (name, val)) w_sub = self.params['config']['w_sub'] w_tail = w_dict['tail'] w_in = w_dict['in'] w_ninv = w_dict['ninv'] w_pinv = w_dict['pinv'] th_tail = th_dict['tail'] th_in = th_dict['in'] th_ninv = th_dict['ninv'] th_pinv = th_dict['pinv'] tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) # get row information row_list = ['ptap', 'ptap', 'nch', 'nch', 'nch', 'pch', 'ntap', 'ntap'] orient_list = ['R0', 'R0', 'R0', 'MX', 'MX', 'R0', 'MX', 'MX'] thres_list = [ th_tail, th_tail, th_tail, th_in, th_ninv, th_pinv, th_pinv, th_pinv ] w_list = [w_sub, w_sub, w_tail, w_in, w_ninv, w_pinv, w_sub, w_sub] if end_mode is None: end_mode = 15 if draw_boundaries else 0 # get track information wire_names = [ dict(ds=['sup'], ), dict(ds=['sup'], ), dict( g=['clk'], gb=['tail'], ), dict( g=['in', 'in'], gb=['out'], ), dict( g=['out', 'out'], gb=['out'], ), dict( g=['out', 'out'], gb=['out', 'out'], ), dict(ds=['sup'], ), dict(ds=['sup'], ), ] # determine number of blocks laygo_info = self.laygo_info lch = laygo_info.lch hm_layer = self.conn_layer + 1 ym_layer = hm_layer + 1 xm_layer = ym_layer + 1 # determine number of separation blocks needed for mid reset wires to be DRC clean hm_w_out = tr_manager.get_width(hm_layer, 'out') vm_w = self.grid.get_track_width(hm_layer - 1, 1, unit_mode=True) # type: int hm_out_vext = self.grid.get_via_extensions( hm_layer - 1, 1, hm_w_out, unit_mode=True)[1] # type: int hm_out_sple = self.grid.get_line_end_space(hm_layer, hm_w_out, unit_mode=True) # type: int n_sep = -(-(vm_w + hm_out_vext * 2 + hm_out_sple) // (2 * laygo_info.col_width)) * 2 # determine total number of latch blocks n_ptot = n_pinv + 2 * n_rst n_ntot = max(n_ninv, n_in, n_tail) n_single = max(n_ptot, n_ntot) n_latch = n_sep + 2 * (n_single + n_dum) col_p = n_dum + n_single - n_ptot col_ninv = n_dum + n_single - n_ninv col_in = n_dum + n_single - n_in col_tail = n_dum + n_single - n_tail # find space between latch and nand gates from wire placement # step 1: find relative track index of clock clk_col = n_dum + n_single + n_sep // 2 clk_ridx = laygo_info.col_to_nearest_rel_track(ym_layer, clk_col, half_track=True, mode=0) # step 2: find relative track index of outp op_col = col_ninv + n_ninv // 2 op_ridx = laygo_info.col_to_nearest_rel_track(ym_layer, op_col, half_track=True, mode=-1) # make sure spacing between outp and clk is satisfied. op_ridx2 = tr_manager.get_next_track(ym_layer, clk_ridx, 'clk', 'out', up=False) op_ridx = min(op_ridx, op_ridx2) # step 3: find NAND outp relative track index num_ym_tracks, loc_ym = tr_manager.place_wires(ym_layer, ['out'] * 6) on_ridx = clk_ridx + (clk_ridx - op_ridx) nand_op_ridx = on_ridx + loc_ym[-1] - loc_ym[0] # step 4: find left NAND gate column index, then compute number of space columns col_nand = laygo_info.rel_track_to_nearest_col(ym_layer, nand_op_ridx, mode=1) n_sp = max(col_nand - n_latch - n_nand - blk_sp - n_buf, blk_sp) n_sr_tot = 2 * n_buf + 3 * blk_sp + 4 * n_nand n_tot = n_latch + n_sp + n_sr_tot # specify row types self.set_row_types(row_list, w_list, orient_list, thres_list, draw_boundaries, end_mode, top_layer=top_layer, num_col=n_tot, min_height=min_height, tr_manager=tr_manager, wire_names=wire_names) # calculate clk/outp/outn vertical track indices clk_idx = laygo_info.col_to_track(ym_layer, clk_col) op_idx = laygo_info.col_to_track(ym_layer, op_col) op_idx2 = tr_manager.get_next_track(ym_layer, clk_idx, 'clk', 'out', up=False) op_idx = min(op_idx, op_idx2) on_idx = clk_idx + (clk_idx - op_idx) # add blocks ndum_list, pdum_list, dum_info_list = [], [], [] # nwell tap row_off = 1 cur_col, row_idx = 0, 7 nw_tap1 = self.add_laygo_mos(row_idx, cur_col, n_tot) row_idx -= 1 nw_tap0 = self.add_laygo_mos(row_idx, cur_col, n_tot) row_idx -= 1 # pmos inverter row cur_col = 0 pdum_list.append(self.add_laygo_mos(row_idx, cur_col, col_p)) cur_col += col_p rst_midp = self.add_laygo_mos(row_idx, cur_col, n_rst) cur_col += n_rst rst_outp = self.add_laygo_mos(row_idx, cur_col, n_rst) cur_col += n_rst pinv_outp = self.add_laygo_mos(row_idx, cur_col, n_pinv) cur_col += n_pinv pdum_list.append(self.add_laygo_mos(row_idx, cur_col, n_sep)) cur_col += n_sep pinv_outn = self.add_laygo_mos(row_idx, cur_col, n_pinv) cur_col += n_pinv rst_outn = self.add_laygo_mos(row_idx, cur_col, n_rst) cur_col += n_rst rst_midn = self.add_laygo_mos(row_idx, cur_col, n_rst) cur_col += n_rst pdum_list.append(self.add_laygo_mos(row_idx, cur_col, col_p)) cur_col += col_p + n_sp pbufl = self.add_laygo_mos(row_idx, cur_col, n_buf) cur_col += n_buf + blk_sp nandpl = self.add_laygo_mos(row_idx, cur_col, n_nand * 2, gate_loc='s') cur_col += n_nand * 2 + blk_sp nandpr = self.add_laygo_mos(row_idx, cur_col, n_nand * 2, gate_loc='s') cur_col += n_nand * 2 + blk_sp pbufr = self.add_laygo_mos(row_idx, cur_col, n_buf) dum_info_list.append( (('pch', w_pinv, lch, th_pinv, '', ''), 2 * col_p + n_sep)) row_idx -= 1 # nmos inverter row cur_col = 0 cur_col = self._draw_nedge_dummy(row_idx, cur_col, col_ninv, ndum_list, left=True) ninv_outp = self.add_laygo_mos(row_idx, cur_col, n_ninv) cur_col += n_ninv cur_col = self._draw_nsep_dummy(row_idx, cur_col, n_sep, ndum_list) ninv_outn = self.add_laygo_mos(row_idx, cur_col, n_ninv) cur_col += n_ninv cur_col = self._draw_nedge_dummy(row_idx, cur_col, col_ninv, ndum_list, left=False) cur_col += n_sp nbufl = self.add_laygo_mos(row_idx, cur_col, n_buf) cur_col += n_buf + blk_sp nandnl = self.add_laygo_mos(row_idx, cur_col, n_nand, stack=True, gate_loc='s') cur_col += n_nand * 2 + blk_sp nandnr = self.add_laygo_mos(row_idx, cur_col, n_nand, stack=True, gate_loc='s') cur_col += n_nand * 2 + blk_sp nbufr = self.add_laygo_mos(row_idx, cur_col, n_buf) dum_info_list.append( (('nch', w_ninv, lch, th_ninv, '', ''), 2 * col_ninv + n_sep - 4)) dum_info_list.append((('nch', w_ninv, lch, th_ninv, '', 'intp'), 2)) dum_info_list.append((('nch', w_ninv, lch, th_ninv, '', 'intn'), 2)) row_idx -= 1 # nmos input row cur_col = 0 cur_col = self._draw_nedge_dummy(row_idx, cur_col, col_in, ndum_list, left=True) inn = self.add_laygo_mos(row_idx, cur_col, n_in) cur_col += n_in cur_col = self._draw_nsep_dummy(row_idx, cur_col, n_sep, ndum_list) inp = self.add_laygo_mos(row_idx, cur_col, n_in) cur_col += n_in ndum = n_tot - cur_col self._draw_nedge_dummy(row_idx, cur_col, ndum, ndum_list, left=False) dum_info_list.append( (('nch', w_in, lch, th_in, '', ''), col_in + ndum + n_sep - 4)) dum_info_list.append((('nch', w_in, lch, th_in, '', 'intp'), 2)) dum_info_list.append((('nch', w_in, lch, th_in, '', 'intn'), 2)) row_idx -= 1 # nmos tail row cur_col = 0 ndum_list.append((self.add_laygo_mos(row_idx, cur_col, col_tail), 0)) cur_col += col_tail tailn = self.add_laygo_mos(row_idx, cur_col, n_tail) cur_col += n_tail ndum_list.append((self.add_laygo_mos(row_idx, cur_col, n_sep), 0)) cur_col += n_sep tailp = self.add_laygo_mos(row_idx, cur_col, n_tail) cur_col += n_tail ndum = n_tot - cur_col ndum_list.append((self.add_laygo_mos(row_idx, cur_col, ndum), 0)) dum_info_list.append( (('nch', w_tail, lch, th_tail, '', ''), col_in + ndum + n_sep)) row_idx -= 1 # pwell tap cur_col = 0 pw_tap0 = self.add_laygo_mos(row_idx, cur_col, n_tot) row_idx -= 1 pw_tap1 = self.add_laygo_mos(row_idx, cur_col, n_tot) # fill dummy self.fill_space() sup_tid = self.get_sup_tid(n_tot) # connect inputs inn_tid = self.get_wire_id(row_off + 2, 'g', wire_idx=0) inp_tid = self.get_wire_id(row_off + 2, 'g', wire_idx=1) inp_idx, inn_idx = inp_tid.base_index, inn_tid.base_index inp_warr, inn_warr = self.connect_differential_tracks( inp['g'], inn['g'], hm_layer, inp_idx, inn_idx, width=inp_tid.width) ym_w_in = tr_manager.get_width(ym_layer, 'in') shr_idx = sup_tid.base_index inp_idx = tr_manager.get_next_track(ym_layer, shr_idx, 1, 'in', up=False) inn_idx = tr_manager.get_next_track(ym_layer, inp_idx, 'in', 'in', up=False) shl_idx = tr_manager.get_next_track(ym_layer, inn_idx, 'in', 1, up=False) inp_ym, inn_ym = self.connect_differential_tracks(inp_warr, inn_warr, ym_layer, inp_idx, inn_idx, width=ym_w_in) shields = self.add_wires(ym_layer, shl_idx, inp_ym.lower_unit, inp_ym.upper_unit, num=2, pitch=shr_idx - shl_idx, unit_mode=True) self.add_pin('inp', inp_ym, show=show_pins) self.add_pin('inn', inn_ym, show=show_pins) # connect vss xm_w_sup = tr_manager.get_width(xm_layer, 'sup') vss_s = [ pw_tap0['VSS_s'], tailn['s'], tailp['s'], nandnl['s'], nandnr['s'], nbufl['s'], nbufr['s'], pw_tap1['VSS_s'] ] vss_d = [pw_tap0['VSS_d'], pw_tap1['VSS_d']] for inst, mode in ndum_list: if mode == 0: vss_s.append(inst['s']) vss_d.append(inst['d']) vss_d.append(inst['g']) vss_s_tid = self.get_wire_id(row_off, 'ds') vss_s_tid2 = self.get_wire_id(0, 'ds') self.connect_wires(vss_d) vss_s_warrs = self.connect_to_tracks(vss_s, vss_s_tid) vss_s2_warrs = self.connect_to_tracks(vss_s, vss_s_tid2) vss_warrs = self.connect_to_tracks([vss_s_warrs, vss_s2_warrs], sup_tid) if sup_tids is not None: vss_warrs = self.connect_to_tracks( vss_warrs, TrackID(xm_layer, sup_tids[0], width=xm_w_sup)) self.add_pin('VSS', vss_warrs, show=show_pins) # connect vdd vdd_s = [ nw_tap0['VDD_s'], pinv_outp['s'], pinv_outn['s'], rst_midp['s'], rst_midn['s'], nandpl['s'], nandpr['s'], pbufl['s'], pbufr['s'], nw_tap1['VDD_s'] ] vdd_d = [nw_tap0['VDD_d'], nw_tap1['VDD_d']] for inst in pdum_list: vdd_d.append(inst['d']) vdd_d.append(inst['g']) vdd_s.append(inst['s']) vdd_s_tid = self.get_wire_id(row_off + 5, 'ds') vdd_s2_tid = self.get_wire_id(row_off + 6, 'ds') self.connect_wires(vdd_d) vdd_s_warrs = self.connect_to_tracks(vdd_s, vdd_s_tid) vdd_s2_warrs = self.connect_to_tracks(vdd_s, vdd_s2_tid) vdd_hm_list = [vdd_s_warrs, vdd_s2_warrs] vdd_warrs = self.connect_to_tracks(vdd_hm_list, sup_tid) if shields is not None: self.connect_to_tracks(vdd_hm_list, shields.track_id) vdd_warrs = [vdd_warrs, shields] if sup_tids is not None: vdd_warrs = self.connect_to_tracks( vdd_warrs, TrackID(xm_layer, sup_tids[1], width=xm_w_sup)) self.add_pin('VDD', vdd_warrs, show=show_pins) # connect tail tail = [tailp['d'], tailn['d'], inp['d'], inn['d']] tail_tid = self.get_wire_id(row_off + 1, 'gb', wire_idx=0) self.connect_to_tracks(tail, tail_tid) # connect tail clk tclk_tid = self.get_wire_id(row_off + 1, 'g', wire_idx=0) clk_list = [self.connect_to_tracks([tailp['g'], tailn['g']], tclk_tid)] # get output/mid horizontal track id nout_tid = self.get_wire_id(row_off + 3, 'gb', wire_idx=0) mid_tid = self.get_wire_id(row_off + 2, 'gb', wire_idx=0) # connect nmos mid nmidp = [inn['s'], ninv_outp['s']] nmidn = [inp['s'], ninv_outn['s']] nmidp = self.connect_to_tracks(nmidp, mid_tid) nmidn = self.connect_to_tracks(nmidn, mid_tid) # connect pmos mid mid_tid = self.get_wire_id(row_off + 4, 'gb', wire_idx=1) pmidp = self.connect_to_tracks(rst_midp['d'], mid_tid, min_len_mode=-1) pmidn = self.connect_to_tracks(rst_midn['d'], mid_tid, min_len_mode=1) # connect nmos output noutp = self.connect_to_tracks(ninv_outp['d'], nout_tid, min_len_mode=0) noutn = self.connect_to_tracks(ninv_outn['d'], nout_tid, min_len_mode=0) # connect pmos output pout_tid = self.get_wire_id(row_off + 4, 'gb', wire_idx=0) poutp = [pinv_outp['d'], rst_outp['d']] poutn = [pinv_outn['d'], rst_outn['d']] poutp = self.connect_to_tracks(poutp, pout_tid) poutn = self.connect_to_tracks(poutn, pout_tid) # connect clock in inverter row pclk = [rst_midp['g'], rst_midn['g'], rst_outp['g'], rst_outn['g']] pclk_tid = self.get_wire_id(row_off + 4, 'g', wire_idx=0) clk_list.append(self.connect_to_tracks(pclk, pclk_tid)) # connect inverter gate invg_tid = self.get_wire_id(row_off + 3, 'g', wire_idx=1) invgp = self.connect_to_tracks([ninv_outp['g'], pinv_outp['g']], invg_tid) invgn = self.connect_to_tracks([ninv_outn['g'], pinv_outn['g']], invg_tid) # connect nand nand_gbl_tid = self.get_wire_id(row_off + 3, 'g', wire_idx=0) nand_gtl_tid = self.get_wire_id(row_off + 3, 'g', wire_idx=1) nand_gtr_tid = self.get_wire_id(row_off + 4, 'g', wire_idx=0) nand_gbr_tid = self.get_wire_id(row_off + 4, 'g', wire_idx=1) nand_nmos_out_tid = self.get_wire_id(row_off + 3, 'gb', wire_idx=0) nand_outnl = self.connect_to_tracks(nandnl['d'], nand_nmos_out_tid, min_len_mode=0) nand_outnr = self.connect_to_tracks(nandnr['d'], nand_nmos_out_tid, min_len_mode=0) nand_gtl = [ nandnl['g1'], nandpl['g1'], nandpr['d'], nbufr['g'], pbufr['g'] ] nand_gtr = [ nandnr['g1'], nandpr['g1'], nandpl['d'], nbufl['g'], pbufl['g'] ] tr_w = nand_gtr_tid.width pidx = nand_gtr_tid.base_index nidx = nand_gtl_tid.base_index nand_outpl, nand_outpr = self.connect_differential_tracks(nand_gtr, nand_gtl, hm_layer, pidx, nidx, width=tr_w) nand_gbl = self.connect_to_tracks([nandnl['g0'], nandpl['g0']], nand_gbl_tid) nand_gbr = self.connect_to_tracks([nandnr['g0'], nandpr['g0']], nand_gbr_tid) # connect buffer buf_pmos_out_tid = self.get_wire_id(row_off + 4, 'gb', wire_idx=0) buf_noutl = self.connect_to_tracks(nbufl['d'], nand_nmos_out_tid, min_len_mode=0) buf_noutr = self.connect_to_tracks(nbufr['d'], nand_nmos_out_tid, min_len_mode=0) buf_poutl = self.connect_to_tracks(pbufl['d'], buf_pmos_out_tid, min_len_mode=0) buf_poutr = self.connect_to_tracks(pbufr['d'], buf_pmos_out_tid, min_len_mode=0) # connect buffer ym wires ym_w_out = tr_manager.get_width(ym_layer, 'out') outb_tid = self.grid.coord_to_nearest_track(ym_layer, buf_noutl.middle_unit, half_track=True, mode=1, unit_mode=True) out_tid = self.grid.coord_to_nearest_track(ym_layer, buf_noutr.middle_unit, half_track=True, mode=-1, unit_mode=True) self.connect_to_tracks([buf_noutl, buf_poutl], TrackID(ym_layer, outb_tid, width=ym_w_out)) out_warr = self.connect_to_tracks([buf_noutr, buf_poutr], TrackID(ym_layer, out_tid, width=ym_w_out)) # connect nand ym wires nand_outl_id = self.grid.coord_to_nearest_track(ym_layer, nand_outnl.middle_unit, half_track=True, mode=1, unit_mode=True) nand_outr_id = self.grid.coord_to_nearest_track(ym_layer, nand_outnr.middle_unit, half_track=True, mode=-1, unit_mode=True) nand_gbr_yt = self.grid.get_wire_bounds(hm_layer, nand_gbr_tid.base_index, unit_mode=True)[1] ym_via_ext = self.grid.get_via_extensions(hm_layer, 1, 1, unit_mode=True)[1] out_upper = nand_gbr_yt + ym_via_ext nand_outl, nand_outr = self.connect_differential_tracks( nand_outpl, nand_outpr, ym_layer, nand_outl_id, nand_outr_id, track_upper=out_upper, width=ym_w_out, unit_mode=True) nand_outl = self.connect_to_tracks(nand_outnl, nand_outl.track_id, track_upper=out_upper, unit_mode=True) nand_outr = self.connect_to_tracks(nand_outnr, nand_outr.track_id, track_upper=out_upper, unit_mode=True) self.add_pin('qp', nand_outl, show=export_probe) self.add_pin('qn', nand_outr, show=export_probe) ym_pitch_out = tr_manager.get_space(ym_layer, ('out', 'out')) + ym_w_out nand_inn_tid = nand_outl_id - ym_pitch_out nand_inp_tid = nand_outr_id + ym_pitch_out self.connect_differential_tracks(nand_gbl, nand_gbr, ym_layer, nand_inn_tid, nand_inp_tid, width=ym_w_out) # connect ym wires clk_tid = TrackID(ym_layer, clk_idx, width=tr_manager.get_width(ym_layer, 'clk')) clk_warr = self.connect_to_tracks(clk_list, clk_tid) if clk_tidx is not None: clk_tid = TrackID(xm_layer, clk_tidx, width=tr_manager.get_width(xm_layer, 'clk')) clk_warr = self.connect_to_tracks(clk_warr, clk_tid) self.add_pin('clk', clk_warr, show=show_pins) tr_w_out_ym = tr_manager.get_width(ym_layer, 'out') sp_out_ym = tr_manager.get_space(ym_layer, ('out', 'out')) + tr_w_out_ym op_tid = TrackID(ym_layer, op_idx, width=tr_w_out_ym) outp1 = self.connect_to_tracks([poutp, noutp], op_tid) on_tid = TrackID(ym_layer, on_idx, width=tr_w_out_ym) outn1 = self.connect_to_tracks([poutn, noutn], on_tid) op_tid = TrackID(ym_layer, on_idx + sp_out_ym, width=tr_w_out_ym) on_tid = TrackID(ym_layer, op_idx - sp_out_ym, width=tr_w_out_ym) outp2 = self.connect_to_tracks(invgn, op_tid) outn2 = self.connect_to_tracks(invgp, on_tid) ym_sp_out = tr_manager.get_space(ym_layer, ('out', 'out')) sp_out_mid = sp_out_ym + ym_sp_out + (ym_w_out + tr_w_out_ym) / 2 mn_tid = TrackID(ym_layer, on_idx + sp_out_mid, width=ym_w_out) mp_tid = TrackID(ym_layer, op_idx - sp_out_mid, width=ym_w_out) self.connect_to_tracks([nmidn, pmidn], mn_tid) self.connect_to_tracks([nmidp, pmidp], mp_tid) om_idx = self.grid.coord_to_nearest_track(xm_layer, outp1.middle, half_track=True) _, loc_xm_out = tr_manager.place_wires(xm_layer, ['out', 'out']) out_mid_idx = (loc_xm_out[0] + loc_xm_out[1]) / 2 midp_idx = loc_xm_out[1] + om_idx - out_mid_idx midn_idx = loc_xm_out[0] + om_idx - out_mid_idx tr_w_out_xm = tr_manager.get_width(xm_layer, 'out') midp, midn = self.connect_differential_tracks([outp1, outp2], [outn1, outn2], xm_layer, midp_idx, midn_idx, width=tr_w_out_xm) self.add_pin('midp', midp, show=export_probe) self.add_pin('midn', midn, show=export_probe) self.connect_differential_tracks(midn, midp, ym_layer, nand_inn_tid, nand_inp_tid, width=ym_w_out) out_xm_idx = self.grid.get_middle_track(midn_idx, midp_idx, round_up=True) out_warr = self.connect_to_tracks( out_warr, TrackID(xm_layer, out_xm_idx, width=tr_w_out_xm)) self.add_pin('out', out_warr, show=show_pins) # do max space fill for lay_id in range(1, xm_layer): self.do_max_space_fill(lay_id) self.fill_box = self.bound_box # set schematic parameters self._sch_params = dict( lch=lch, w_dict=w_dict, th_dict=th_dict, seg_dict=seg_dict, dum_info=dum_info_list, export_probe=export_probe, ) self._fg_tot = n_tot
def draw_layout(self): blk_sp = 2 fanout = 4 config = self.params['config'] seg = self.params['seg'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] wp = self.params['wp'] wn = self.params['wn'] row_layout_info = self.params['row_layout_info'] sig_locs = self.params['sig_locs'] show_pins = self.params['show_pins'] wp_row = config['wp'] wn_row = config['wn'] if wp is None: wp = wp_row if wn is None: wn = wn_row if wp < 0 or wp > wp_row or wn < 0 or wn > wn_row: raise ValueError('Invalid choice of wp and/or wn.') if sig_locs is None: sig_locs = {} # setup floorplan params = self.params.copy() params['wp'] = wp params['wn'] = wn params['show_pins'] = False params['sig_locs'] = None params['out_vm'] = True if row_layout_info is not None: self.initialize(row_layout_info, 1) else: inv_master = self.new_template(params=params, temp_cls=Inverter) params[ 'row_layout_info'] = row_layout_info = inv_master.row_layout_info self.initialize(row_layout_info, 1) # compute track locations hm_layer = self.conn_layer + 1 ym_layer = hm_layer + 1 tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) g_locs = tr_manager.place_wires(hm_layer, ['in', 'in'])[1] d_locs = tr_manager.place_wires(hm_layer, ['out', 'out'])[1] ng0_tidx = self.get_track_index(0, 'g', g_locs[0]) ng1_tidx = self.get_track_index(0, 'g', g_locs[1]) pg0_tidx = self.get_track_index(1, 'g', g_locs[0]) pg1_tidx = self.get_track_index(1, 'g', g_locs[1]) nd0_tidx = self.get_track_index(0, 'gb', d_locs[0]) nd1_tidx = self.get_track_index(0, 'gb', d_locs[1]) pd0_tidx = self.get_track_index(1, 'gb', d_locs[0]) pd1_tidx = self.get_track_index(1, 'gb', d_locs[1]) pen_tidx = sig_locs.get('pen', pg1_tidx) nen_tidx = sig_locs.get('nen', ng1_tidx) in0_tidx = sig_locs.get('in0', pg0_tidx) in1_tidx = sig_locs.get('in1', ng0_tidx) out_vm_tidx = sig_locs.get('out', None) # make masters seg_in = max(1, int(round(seg / (fanout // 2)))) seg_sel = max(1, int(round(seg_in // fanout))) params['sig_locs'] = { 'in': pen_tidx, 'pout': pd0_tidx, 'nout': nd0_tidx } params['out_vm'] = False inv_master = self.new_template(params=params, temp_cls=Inverter) params['seg'] = seg_sel params['sig_locs'] = { 'in': pen_tidx, 'pout': pd0_tidx, 'nout': nd0_tidx } params['out_vm'] = True sel_master = self.new_template(params=params, temp_cls=Inverter) params['seg'] = seg_in params['sig_locs'] = { 'in': in0_tidx, 'pout': pd1_tidx, 'nout': nd1_tidx, 'en': nen_tidx, 'enb': pen_tidx } params['out_vm'] = False t0_master = self.new_template(params=params, temp_cls=InverterTristate) params['sig_locs'] = { 'in': in1_tidx, 'pout': pd1_tidx, 'nout': nd1_tidx, 'en': nen_tidx, 'enb': pen_tidx } t1_master = self.new_template(params=params, temp_cls=InverterTristate) # set size sel_ncol = sel_master.num_cols sel_sep = blk_sp + 1 if sel_ncol % 2 == 1 else blk_sp t0_ncol = t0_master.num_cols t1_ncol = t1_master.num_cols inv_ncol = inv_master.num_cols num_cols = sel_ncol + t0_ncol + t1_ncol + inv_ncol + 2 * blk_sp + sel_sep self.set_digital_size(num_cols) # add instances t0_col = sel_ncol + sel_sep t1_col = t0_col + t0_ncol + blk_sp inv_col = num_cols - inv_ncol sel = self.add_digital_block(sel_master, (0, 0)) t0 = self.add_digital_block(t0_master, (t0_col, 0)) t1 = self.add_digital_block(t1_master, (t1_col, 0)) inv = self.add_digital_block(inv_master, (inv_col, 0)) self.fill_space() # connect/export VSS/VDD vss_list, vdd_list = [], [] for inst in (sel, t0, t1, inv): vss_list.append(inst.get_pin('VSS')) vdd_list.append(inst.get_pin('VDD')) self.add_pin('VSS', self.connect_wires(vss_list), show=show_pins) self.add_pin('VDD', self.connect_wires(vdd_list), show=show_pins) # export input/output self.add_pin('in0', t0.get_pin('in'), show=show_pins) self.add_pin('in1', t1.get_pin('in'), show=show_pins) pout = inv.get_pin('pout') nout = inv.get_pin('nout') if out_vm_tidx is None: out_vm_tidx = self.laygo_info.col_to_track(ym_layer, num_cols - 1) out = self.connect_to_tracks([pout, nout], TrackID(ym_layer, out_vm_tidx)) self.add_pin('out', out, show=show_pins) # connect middle node col_idx = inv_col - blk_sp // 2 tr_idx = self.laygo_info.col_to_track(ym_layer, col_idx) hm_list = [ t0.get_pin('pout'), t0.get_pin('nout'), t1.get_pin('pout'), t1.get_pin('nout'), inv.get_pin('in') ] self.connect_to_tracks(hm_list, TrackID(ym_layer, tr_idx)) # connect enables sel0l = self.extend_wires(t0.get_pin('en'), min_len_mode=0)[0] sel0r = self.extend_wires(t1.get_pin('enb'), min_len_mode=0)[0] sel1l = self.connect_wires([sel.get_pin('in'), t0.get_pin('enb')])[0] sel1r = self.extend_wires(t1.get_pin('en'), min_len_mode=0)[0] self.add_pin('sel1_hm', sel1l, label='sel1', show=False) self.connect_to_track_wires(sel.get_pin('out'), sel0l) ym_tidx = self.grid.coord_to_nearest_track(ym_layer, sel0l.middle_unit, mode=1, half_track=True, unit_mode=True) ym_tid = TrackID(ym_layer, ym_tidx) sel0l = self.connect_to_tracks(sel0l, ym_tid, min_len_mode=-1) sel1l = self.connect_to_tracks(sel1l, ym_tid, min_len_mode=1) self.add_pin('sel1', sel1l, show=show_pins) sel0l = self.connect_to_tracks(sel0l, TrackID(hm_layer, nd0_tidx), min_len_mode=1) sel1l = self.connect_to_tracks(sel1l, TrackID(hm_layer, pd0_tidx), min_len_mode=1) sel0_tidx = self.grid.find_next_track(ym_layer, sel0r.middle_unit, mode=-1, half_track=True, unit_mode=True) sel1_tidx = sel0_tidx + 1 self.connect_to_tracks([sel0l, sel0r], TrackID(ym_layer, sel0_tidx)) self.connect_to_tracks([sel1l, sel1r], TrackID(ym_layer, sel1_tidx)) # set properties pseg_t0 = t0_master.sch_params['segp'] nseg_t0 = t0_master.sch_params['segn'] psel = sel_master.sch_params['segp'] nsel = sel_master.sch_params['segn'] self._sch_params = dict( lch=config['lch'], wp=wp, wn=wn, thp=config['thp'], thn=config['thn'], seg_dict=dict(pinv=seg, ninv=seg, pt0=pseg_t0, nt0=nseg_t0, psel=psel, nsel=nsel), ) self._seg_in = seg_in
def draw_layout(self): lch = self.params['lch'] ptap_w = self.params['ptap_w'] ntap_w = self.params['ntap_w'] w_dict = self.params['w_dict'] th_dict = self.params['th_dict'] seg_dict = self.params['seg_dict'] stack_dict = self.params['stack_dict'] fg_duml = self.params['fg_duml'] fg_dumr = self.params['fg_dumr'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] top_layer = self.params['top_layer'] end_mode = self.params['end_mode'] guard_ring_nf = self.params['guard_ring_nf'] options = self.params['options'] show_pins = self.params['show_pins'] tech_cls_name = self.params['tech_cls_name'] if options is None: options = {} if tech_cls_name is not None: self.set_tech_class(tech_cls_name) seg_gm = seg_dict['gm'] seg_tail = seg_dict['tail'] stack_gm = stack_dict['gm'] stack_tail = stack_dict['tail'] if seg_gm % 2 != 0 or seg_tail % 2 != 0: raise ValueError('seg_gm and seg_tail must be even') fg_gm = seg_gm * stack_gm fg_tail = seg_tail * stack_tail # get fg_sep, make sure it is even info = AnalogBaseInfo(self.grid, lch, guard_ring_nf, top_layer=top_layer, end_mode=end_mode) fg_sep = info.min_fg_sep fg_sep = -(-fg_sep // 2) * 2 # get number of fingers fg_single = max(fg_gm, fg_tail) fg_tot = fg_duml + fg_dumr + 2 * fg_single + fg_sep # get row information nw_list = [w_dict['tail'], w_dict['gm']] nth_list = [th_dict['tail'], th_dict['gm']] n_orientations = ['R0', 'R0'] pw_list = pth_list = p_orientations = [] # get track manager and wire names tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) wire_names = dict( nch=[ dict(g=['bias'], ds2=['tail']), dict(g2=['out', 'out'], ds2=['out', 'out']), ], pch=[], ) # draw base self.draw_base(lch, fg_tot, ptap_w, ntap_w, nw_list, nth_list, pw_list, pth_list, tr_manager=tr_manager, wire_names=wire_names, n_orientations=n_orientations, p_orientations=p_orientations, guard_ring_nf=guard_ring_nf, top_layer=top_layer, options=options) # draw transistors col_taill = fg_duml + fg_single - fg_tail col_gml = fg_duml + fg_single - fg_gm col_gmr = col_tailr = fg_duml + fg_single + fg_sep taill = self.draw_mos_conn('nch', 0, col_taill, fg_tail, 2, 0, s_net='tail', d_net='', stack=stack_tail) tailr = self.draw_mos_conn('nch', 0, col_tailr, fg_tail, 2, 0, s_net='tail', d_net='', stack=stack_tail) gml = self.draw_mos_conn('nch', 1, col_gml, fg_gm, 0, 2, s_net='tail', d_net='outp', stack=stack_gm) gmr = self.draw_mos_conn('nch', 1, col_gmr, fg_gm, 0, 2, s_net='tail', d_net='outn', stack=stack_gm) # draw connections # VSS self.connect_to_substrate('ptap', [taill['d'], tailr['d']]) # tail tail_tid = self.get_wire_id('nch', 0, 'ds2', wire_name='tail') self.connect_to_tracks([taill['s'], tailr['s'], gml['s'], gmr['s']], tail_tid) # bias bias_tid = self.get_wire_id('nch', 0, 'g', wire_name='bias') bias = self.connect_to_tracks([taill['g'], tailr['g']], bias_tid) # input differential pair outn_tid = self.get_wire_id('nch', 1, 'g2', wire_name='out', wire_idx=0) outp_tid = self.get_wire_id('nch', 1, 'g2', wire_name='out', wire_idx=1) outp_g, outn_g = self.connect_differential_tracks(gmr['g'], gml['g'], outn_tid.layer_id, outp_tid.base_index, outn_tid.base_index, width=outp_tid.width) # output differential pair outn_tid = self.get_wire_id('nch', 1, 'ds2', wire_name='out', wire_idx=0) outp_tid = self.get_wire_id('nch', 1, 'ds2', wire_name='out', wire_idx=1) outp_d, outn_d = self.connect_differential_tracks(gml['d'], gmr['d'], outn_tid.layer_id, outp_tid.base_index, outn_tid.base_index, width=outp_tid.width) # fill dummies vss, vdd = self.fill_dummy() # add pins self.add_pin('VSS', vss, show=show_pins) self.add_pin('bias', bias, show=show_pins) self.add_pin('outp', outp_g, label='outp:', show=show_pins) self.add_pin('outn', outn_g, label='outn:', show=show_pins) self.add_pin('outp', outp_d, label='outp:', show=show_pins) self.add_pin('outn', outn_d, label='outn:', show=show_pins) # compute schematic parameters self._sch_params = dict( lch=lch, w_dict=w_dict, th_dict=th_dict, seg_dict=seg_dict, stack_dict=stack_dict, dum_info=self.get_sch_dummy_info(), )
def draw_layout(self): config = self.params['config'] seg_list = self.params['seg_list'] stack_list = self.params['stack_list'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] wp_list = self.params['wp_list'] wn_list = self.params['wn_list'] sig_locs = self.params['sig_locs'] row_layout_info = self.params['row_layout_info'] show_pins = self.params['show_pins'] ninv = len(seg_list) wp_row = config['wp'] wn_row = config['wn'] if wp_list is None: wp_list = [wp_row] * ninv elif len(wp_list) != ninv: raise ValueError('length of wp_list != %d' % ninv) if wn_list is None: wn_list = [wn_row] * ninv elif len(wn_list) != ninv: raise ValueError('length of wn_list != %d' % ninv) if stack_list is None: stack_list = [False] * ninv elif len(stack_list) != ninv: raise ValueError('length of stack_list != %d' % ninv) # TODO: remove restriction if ninv != 2: raise ValueError('Now only 2 inverters are supported.') seg_in, seg_out = seg_list stack_in, stack_out = stack_list seg_tot = self.compute_num_cols(seg_list, stack_list=stack_list) vss_tid, vdd_tid = self.setup_floorplan(config, row_layout_info, seg_tot) tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) if sig_locs is None: sig_locs = {} # get track information hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 hm_w_g = tr_manager.get_width(hm_layer, 'in') hm_w_d = tr_manager.get_width(hm_layer, 'out') vm_w_d = tr_manager.get_width(vm_layer, 'out') g_locs = tr_manager.place_wires(hm_layer, ['in', 'in'])[1] d_locs = tr_manager.place_wires(hm_layer, ['out', 'out'])[1] ng0_tid = self.make_track_id(0, 'g', g_locs[0], width=hm_w_g) pg0_tid = self.make_track_id(1, 'g', g_locs[0], width=hm_w_g) nd0_tid = self.make_track_id(0, 'gb', d_locs[0], width=hm_w_d) nd1_tid = self.make_track_id(0, 'gb', d_locs[1], width=hm_w_d) pd0_tid = self.make_track_id(1, 'gb', d_locs[0], width=hm_w_d) pd1_tid = self.make_track_id(1, 'gb', d_locs[1], width=hm_w_d) if 'mid' in sig_locs: mid_tid = TrackID(hm_layer, sig_locs['mid']) else: mid_tid = None if 'in' in sig_locs: in_tid = TrackID(hm_layer, sig_locs['in'], width=hm_w_g) if mid_tid is None: if in_tid.base_index == pg0_tid.base_index: mid_tid = ng0_tid else: mid_tid = pg0_tid else: in_tid = ng0_tid if mid_tid is None: mid_tid = pg0_tid # add blocks and collect wires pinv0 = self.add_laygo_mos(1, 0, seg_in, w=wp_list[0], stack=stack_in) ninv0 = self.add_laygo_mos(0, 0, seg_in, w=wn_list[0], stack=stack_in) fg_in = seg_in * 2 if stack_in else seg_in col = fg_in if seg_in % 2 == 0 else fg_in + self.blk_sp pinv1 = self.add_laygo_mos(1, col, seg_out, w=wp_list[1], stack=stack_out) ninv1 = self.add_laygo_mos(0, col, seg_out, w=wn_list[1], stack=stack_out) # compute overall block size and fill spaces self.fill_space() # connect input in_warr = self.connect_to_tracks([pinv0['g'], ninv0['g']], in_tid, min_len_mode=0) self.add_pin('in', in_warr, show=show_pins) # connect output pout_warr = self.connect_to_tracks(pinv1['d'], pd0_tid, min_len_mode=0) nout_warr = self.connect_to_tracks(ninv1['d'], nd0_tid, min_len_mode=0) if 'out' in sig_locs: out_tidx = sig_locs['out'] else: out_tidx = self.grid.coord_to_nearest_track(vm_layer, pout_warr.middle, half_track=True) tid = TrackID(vm_layer, out_tidx, width=vm_w_d) out_warr = self.connect_to_tracks([pout_warr, nout_warr], tid) self.add_pin('out', out_warr, show=show_pins) # connect middle pout_warr = self.connect_to_tracks(pinv0['d'], pd1_tid, min_len_mode=0) nout_warr = self.connect_to_tracks(ninv0['d'], nd1_tid, min_len_mode=0) mid_warr = self.connect_to_tracks([pinv1['g'], ninv1['g']], mid_tid, min_len_mode=-1) mid_tidx = self.grid.coord_to_nearest_track(vm_layer, pout_warr.middle, half_track=True) tid = TrackID(vm_layer, mid_tidx, width=vm_w_d) self.connect_to_tracks([pout_warr, nout_warr, mid_warr], tid) # connect supplies vdd = [pinv0['s'], pinv1['s']] vss = [ninv0['s'], ninv1['s']] vss_warr = self.connect_to_tracks(vss, vss_tid) vdd_warr = self.connect_to_tracks(vdd, vdd_tid) self.add_pin('VSS', vss_warr, show=show_pins) self.add_pin('VDD', vdd_warr, show=show_pins) # set properties self._sch_params = dict( lch=config['lch'], thp=config['thp'], thn=config['thn'], segp_list=seg_list, segn_list=seg_list, wp_list=wp_list, wn_list=wn_list, stack_list=stack_list, ) self._mid_tidx = mid_tidx
def draw_layout(self): config = self.params['config'] seg = self.params['seg'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] wp = self.params['wp'] wn = self.params['wn'] stack = self.params['stack'] row_layout_info = self.params['row_layout_info'] sig_locs = self.params['sig_locs'] out_vm = self.params['out_vm'] show_pins = self.params['show_pins'] wp_row = config['wp'] wn_row = config['wn'] if wp is None: wp = wp_row if wn is None: wn = wn_row if wp < 0 or wp > wp_row or wn < 0 or wn > wn_row: raise ValueError('Invalid choice of wp and/or wn.') if sig_locs is None: sig_locs = {} in_tidx = sig_locs.get('in', None) pout_tidx = sig_locs.get('pout', None) nout_tidx = sig_locs.get('nout', None) out_tidx = sig_locs.get('out', None) fg = seg * 2 if stack else seg vss_tid, vdd_tid = self.setup_floorplan(config, row_layout_info, fg) tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) # get track information hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 tr_w_in = tr_manager.get_width(hm_layer, 'in') tr_w_out_h = tr_manager.get_width(hm_layer, 'out') tr_w_out_v = tr_manager.get_width(vm_layer, 'out') # add blocks and collect wires pinv = self.add_laygo_mos(1, 0, seg, w=wp, gate_loc='d', stack=stack) ninv = self.add_laygo_mos(0, 0, seg, w=wn, gate_loc='d', stack=stack) vdd = pinv['s'] vss = ninv['s'] pout = pinv['d'] nout = ninv['d'] pin = pinv['g'] nin = ninv['g'] # compute overall block size and fill spaces self.fill_space() # connect input if in_tidx is None: loc = tr_manager.place_wires(hm_layer, ['in'])[1][0] in_tidx = self.get_track_index(0, 'g', loc) tid = TrackID(hm_layer, in_tidx, width=tr_w_in) in_warr = self.connect_to_tracks([pin, nin], tid) # connect output out_loc = tr_manager.place_wires(hm_layer, ['out'])[1][0] if pout_tidx is None: pout_tidx = self.get_track_index(1, 'gb', out_loc) tid = TrackID(hm_layer, pout_tidx, width=tr_w_out_h) pout_warr = self.connect_to_tracks(pout, tid, min_len_mode=0) if nout_tidx is None: nout_tidx = self.get_track_index(0, 'gb', out_loc) tid = TrackID(hm_layer, nout_tidx, width=tr_w_out_h) nout_warr = self.connect_to_tracks(nout, tid, min_len_mode=0) if out_vm: if out_tidx is None: out_tidx = self.grid.coord_to_nearest_track(vm_layer, pout_warr.middle, half_track=True) tid = TrackID(vm_layer, out_tidx, width=tr_w_out_v) out_warr = self.connect_to_tracks([pout_warr, nout_warr], tid) self.add_pin('out', out_warr, show=show_pins) # connect supplies vss_warr = self.connect_to_tracks(vss, vss_tid) vdd_warr = self.connect_to_tracks(vdd, vdd_tid) # export self.add_pin('VSS', vss_warr, show=show_pins) self.add_pin('VDD', vdd_warr, show=show_pins) self.add_pin('in', in_warr, show=show_pins) self.add_pin('pout', pout_warr, label='out', show=False) self.add_pin('nout', nout_warr, label='out', show=False) # set properties self._sch_params = dict( lch=config['lch'], wp=wp, wn=wn, thp=config['thp'], thn=config['thn'], segp=seg, segn=seg, stack=stack, )
def draw_layout(self): blk_sp = 2 in_fanout = 4 fb_fanout = 8 config = self.params['config'] seg = self.params['seg'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] wp = self.params['wp'] wn = self.params['wn'] row_layout_info = self.params['row_layout_info'] sig_locs = self.params['sig_locs'] pass_zero = self.params['pass_zero'] show_pins = self.params['show_pins'] wp_row = config['wp'] wn_row = config['wn'] if wp is None: wp = wp_row if wn is None: wn = wn_row if wp < 0 or wp > wp_row or wn < 0 or wn > wn_row: raise ValueError('Invalid choice of wp and/or wn.') if sig_locs is None: sig_locs = {} # setup floorplan params = self.params.copy() params['wp'] = wp params['wn'] = wn params['show_pins'] = False params['sig_locs'] = None params['out_vm'] = True if row_layout_info is not None: self.initialize(row_layout_info, 1) else: inv_master = self.new_template(params=params, temp_cls=Inverter) params[ 'row_layout_info'] = row_layout_info = inv_master.row_layout_info self.initialize(row_layout_info, 1) # compute track locations hm_layer = self.conn_layer + 1 ym_layer = hm_layer + 1 tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) ym_w_in = tr_manager.get_width(ym_layer, 'in') g_locs = tr_manager.place_wires(hm_layer, ['in', 'in'])[1] d_locs = tr_manager.place_wires(hm_layer, ['out', 'out'])[1] ng0_tidx = self.get_track_index(0, 'g', g_locs[0]) ng1_tidx = self.get_track_index(0, 'g', g_locs[1]) pg0_tidx = self.get_track_index(1, 'g', g_locs[0]) pg1_tidx = self.get_track_index(1, 'g', g_locs[1]) nd0_tidx = self.get_track_index(0, 'gb', d_locs[0]) nd1_tidx = self.get_track_index(0, 'gb', d_locs[1]) pd0_tidx = self.get_track_index(1, 'gb', d_locs[0]) pd1_tidx = self.get_track_index(1, 'gb', d_locs[1]) t0_in_tidx = sig_locs.get('in', pg1_tidx) t0_enb_tidx = sig_locs.get('pclkb', pg0_tidx) t0_en_tidx = sig_locs.get('nclk', ng0_tidx) t1_en_tidx = sig_locs.get('nclkb', ng1_tidx) clk_tidx = sig_locs.get('clk', None) clkb_tidx = sig_locs.get('clkb', None) # make masters seg_t1 = max(1, int(round(seg / (2 * fb_fanout))) * 2) seg_t0 = max(2 * seg_t1, max(2, int(round(seg / (2 * in_fanout))) * 2)) params['sig_locs'] = { 'in': t0_en_tidx, 'pout': pd1_tidx, 'nout': nd1_tidx } inv_master = self.new_template(params=params, temp_cls=Inverter) params['seg'] = seg_t0 params['out_vm'] = False params['sig_locs'] = { 'in': t0_in_tidx, 'pout': pd0_tidx, 'nout': nd0_tidx, 'en': t0_en_tidx, 'enb': t0_enb_tidx } params['pmos_switch'] = not pass_zero t0_master = self.new_template(params=params, temp_cls=InverterTristate) params['seg'] = seg_t1 params['sig_locs'] = { 'in': t0_enb_tidx, 'pout': pd0_tidx, 'nout': nd0_tidx, 'en': t1_en_tidx, 'enb': t0_in_tidx } params['pmos_switch'] = True t1_master = self.new_template(params=params, temp_cls=InverterTristate) # set size t0_ncol = t0_master.num_cols t1_ncol = t1_master.num_cols inv_ncol = inv_master.num_cols num_cols = t0_ncol + t1_ncol + inv_ncol + blk_sp * 2 self.set_digital_size(num_cols) # add instances t1_col = t0_ncol + blk_sp inv_col = num_cols - inv_ncol t0 = self.add_digital_block(t0_master, (0, 0)) t1 = self.add_digital_block(t1_master, (t1_col, 0)) inv = self.add_digital_block(inv_master, (inv_col, 0)) self.fill_space() # connect/export VSS/VDD vss_list, vdd_list = [], [] for inst in (t0, t1, inv): vss_list.append(inst.get_pin('VSS')) vdd_list.append(inst.get_pin('VDD')) self.add_pin('VSS', self.connect_wires(vss_list), show=show_pins) self.add_pin('VDD', self.connect_wires(vdd_list), show=show_pins) # export input self.add_pin('in', t0.get_pin('in'), show=show_pins) # connect output out = inv.get_pin('out') in2 = t1.get_pin('in') self.connect_to_track_wires(in2, out) self.add_pin('out', out, show=show_pins) self.add_pin('out_hm', in2, label='out', show=show_pins) # connect middle node lay_info = self.laygo_info col = inv_col - blk_sp // 2 ym_tid = TrackID(ym_layer, lay_info.col_to_track(ym_layer, col), width=ym_w_in) warrs = [ t0.get_pin('pout'), t0.get_pin('nout'), t1.get_pin('pout'), t1.get_pin('nout'), inv.get_pin('in') ] self.connect_to_tracks(warrs, ym_tid) # connect clocks clk_col = t1_col + 1 clkb_col = t1_col - blk_sp - 1 if clk_tidx is None: clk_tidx = lay_info.col_to_track(ym_layer, clk_col) clk_tid = TrackID(ym_layer, clk_tidx, width=ym_w_in) if clkb_tidx is None: clkb_tidx = lay_info.col_to_track(ym_layer, clkb_col) clkb_tid = TrackID(ym_layer, clkb_tidx, width=ym_w_in) t0_en = t0.get_pin('en') t1_en = t1.get_pin('en') t1_enb = t1.get_pin('enb') t1_enb = self.extend_wires(t1_enb, min_len_mode=-1)[0] clk = self.connect_to_tracks([t0_en, t1_enb], clk_tid) if not pass_zero: t0_enb = t0.get_pin('enb') t0_enb = self.extend_wires(t0_enb, min_len_mode=1)[0] clkb = self.connect_to_tracks([t0_enb, t1_en], clkb_tid) self.add_pin('pclkb', t0_enb, label='clkb', show=False) else: clkb = self.connect_to_tracks(t1_en, clkb_tid, min_len_mode=0) self.add_pin('clk', clk, show=show_pins) self.add_pin('clkb', clkb, show=show_pins) self.add_pin('nclk', t0_en, label='clk', show=False) self.add_pin('pclk', t1_enb, label='clk', show=False) self.add_pin('nclkb', t1_en, label='clkb', show=False) # set properties pseg_t0 = t0_master.sch_params['segp'] self._sch_params = dict( lch=config['lch'], wp=wp, wn=wn, thp=config['thp'], thn=config['thn'], seg_dict=dict(pinv=seg, ninv=seg, pt0=pseg_t0, nt0=seg_t0, pt1=seg_t1, nt1=seg_t1), pass_zero=pass_zero, ) self._seg_in = seg_t0
def draw_layout(self): config = self.params['config'] seg = self.params['seg'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] wp = self.params['wp'] wn = self.params['wn'] row_layout_info = self.params['row_layout_info'] sig_locs = self.params['sig_locs'] out_vm = self.params['out_vm'] pmos_switch = self.params['pmos_switch'] show_pins = self.params['show_pins'] wp_row = config['wp'] wn_row = config['wn'] if wp is None: wp = wp_row if wn is None: wn = wn_row if wp < 0 or wp > wp_row or wn < 0 or wn > wn_row: raise ValueError('Invalid choice of wp and/or wn.') vss_tid, vdd_tid = self.setup_floorplan(config, row_layout_info, seg * 2) if sig_locs is None: sig_locs = {} in_tidx = sig_locs.get('in', None) pout_tidx = sig_locs.get('pout', None) nout_tidx = sig_locs.get('nout', None) out_tidx = sig_locs.get('out', None) enb_tidx = sig_locs.get('enb', None) en_tidx = sig_locs.get('en', None) tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) # get track information hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 tr_w_in = tr_manager.get_width(hm_layer, 'in') tr_w_en = tr_manager.get_width(hm_layer, 'en') tr_w_out_h = tr_manager.get_width(hm_layer, 'out') tr_w_out_v = tr_manager.get_width(vm_layer, 'out') # add blocks and collect wires pseg = seg if pmos_switch else 2 * seg pinv = self.add_laygo_mos(1, 0, pseg, gate_loc='s', stack=pmos_switch, w=wp) ninv = self.add_laygo_mos(0, 0, seg, gate_loc='s', stack=True, w=wn) vdd = pinv['s'] vss = ninv['s'] pout = pinv['d'] nout = ninv['d'] enb = pinv['g1'] en = ninv['g1'] pin = pinv['g0'] nin = ninv['g0'] # compute overall block size and fill spaces self.fill_space() # get track locations if en_tidx is None: ntr = self.get_num_tracks(0, 'g') loc = tr_manager.align_wires(hm_layer, ['en'], ntr, alignment=1)[0] en_tidx = self.get_track_index(0, 'g', loc) if enb_tidx is None: ntr = self.get_num_tracks(1, 'g') loc = tr_manager.align_wires(hm_layer, ['en'], ntr, alignment=1)[0] enb_tidx = self.get_track_index(1, 'g', loc) if in_tidx is None: in_tidx2 = int(round(2 * (en_tidx + enb_tidx))) if in_tidx2 % 4 == 0: in_tidx = in_tidx2 // 4 elif in_tidx2 % 2 == 0: in_tidx = in_tidx2 / 4 else: in_tidx = (in_tidx2 + 1) / 4 out_loc = tr_manager.place_wires(hm_layer, ['out'])[1][0] if pout_tidx is None: pout_tidx = self.get_track_index(1, 'gb', out_loc) if nout_tidx is None: nout_tidx = self.get_track_index(0, 'gb', out_loc) # connect wires in_warr_list = [pin, nin] if pmos_switch: tid = TrackID(hm_layer, enb_tidx, width=tr_w_en) enb_warr = self.connect_to_tracks(enb, tid) self.add_pin('enb', enb_warr, show=show_pins) else: in_warr_list.append(enb) tid = TrackID(hm_layer, in_tidx, width=tr_w_in) in_warr = self.connect_to_tracks(in_warr_list, tid) tid = TrackID(hm_layer, en_tidx, width=tr_w_en) en_warr = self.connect_to_tracks(en, tid) tid = TrackID(hm_layer, pout_tidx, width=tr_w_out_h) pout_warr = self.connect_to_tracks(pout, tid, min_len_mode=0) tid = TrackID(hm_layer, nout_tidx, width=tr_w_out_h) nout_warr = self.connect_to_tracks(nout, tid, min_len_mode=0) # connect output if out_vm: if out_tidx is None: out_tidx = self.grid.coord_to_nearest_track(vm_layer, pout_warr.middle, half_track=True) tid = TrackID(vm_layer, out_tidx, width=tr_w_out_v) out_warr = self.connect_to_tracks([pout_warr, nout_warr], tid) self.add_pin('out', out_warr, show=show_pins) # connect supplies vss_warr = self.connect_to_tracks(vss, vss_tid) vdd_warr = self.connect_to_tracks(vdd, vdd_tid) # export self.add_pin('VSS', vss_warr, show=show_pins) self.add_pin('VDD', vdd_warr, show=show_pins) self.add_pin('in', in_warr, show=show_pins) self.add_pin('en', en_warr, show=show_pins) self.add_pin('pout', pout_warr, label='out', show=False) self.add_pin('nout', nout_warr, label='out', show=False) # set properties self._sch_params = dict( lch=config['lch'], wp=wp, wn=wn, thp=config['thp'], thn=config['thn'], segp=pseg, segn=seg, pmos_switch=pmos_switch, )
def draw_layout(self): blk_sp = 2 fanout = 4 config = self.params['config'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] wp = self.params['wp'] wn = self.params['wn'] row_layout_info = self.params['row_layout_info'] sig_locs = self.params['sig_locs'] pass_zero = self.params['pass_zero'] show_pins = self.params['show_pins'] wp_row = config['wp'] wn_row = config['wn'] if wp is None: wp = wp_row if wn is None: wn = wn_row if wp < 0 or wp > wp_row or wn < 0 or wn > wn_row: raise ValueError('Invalid choice of wp and/or wn.') if sig_locs is None: sig_locs = {} # setup floorplan params = self.params.copy() params['wp'] = wp params['wn'] = wn params['show_pins'] = False params['sig_locs'] = None if row_layout_info is not None: self.initialize(row_layout_info, 1) else: m_master = self.new_template(params=params, temp_cls=LatchCK2) params[ 'row_layout_info'] = row_layout_info = m_master.row_layout_info self.initialize(row_layout_info, 1) # compute track locations hm_layer = self.conn_layer + 1 tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) g_locs = tr_manager.place_wires(hm_layer, ['in', 'in'])[1] ng0_tidx = self.get_track_index(0, 'g', g_locs[0]) ng1_tidx = self.get_track_index(0, 'g', g_locs[1]) pg0_tidx = self.get_track_index(1, 'g', g_locs[0]) pg1_tidx = self.get_track_index(1, 'g', g_locs[1]) in_tidx = sig_locs.get('in', pg0_tidx) pclkb_tidx = sig_locs.get('pclkb', pg1_tidx) # make masters params['sig_locs'] = {'nclk': ng1_tidx, 'nclkb': ng0_tidx} s_master = self.new_template(params=params, temp_cls=LatchCK2) seg_m = max(2, int(round(s_master.seg_in / (2 * fanout))) * 2) params['seg'] = seg_m params['sig_locs'] = { 'in': in_tidx, 'pclkb': pclkb_tidx, 'clkb': sig_locs.get('clk', None), 'clk': sig_locs.get('clkb', None) } m_master = self.new_template(params=params, temp_cls=LatchCK2) # set size m_ncol = m_master.num_cols s_ncol = s_master.num_cols num_cols = m_ncol + s_ncol + blk_sp self.set_digital_size(num_cols) # add instances s_col = m_ncol + blk_sp m_inst = self.add_digital_block(m_master, (0, 0)) s_inst = self.add_digital_block(s_master, (s_col, 0)) self.fill_space() # connect/export VSS/VDD vss_list, vdd_list = [], [] for inst in (m_inst, s_inst): vss_list.append(inst.get_pin('VSS')) vdd_list.append(inst.get_pin('VDD')) self.add_pin('VSS', self.connect_wires(vss_list), show=show_pins) self.add_pin('VDD', self.connect_wires(vdd_list), show=show_pins) # connect intermediate node self.connect_wires([s_inst.get_pin('in'), m_inst.get_pin('out_hm')]) # connect clocks self.connect_wires([s_inst.get_pin('nclk'), m_inst.get_pin('nclkb')]) if pass_zero: self.connect_to_track_wires(s_inst.get_pin('clkb'), m_inst.get_pin('pclk')) else: self.connect_wires( [s_inst.get_pin('pclkb'), m_inst.get_pin('pclk')]) # add pins self.add_pin('in', m_inst.get_pin('in'), show=show_pins) self.add_pin('out', s_inst.get_pin('out'), show=show_pins) self.add_pin('out_hm', s_inst.get_pin('out_hm'), show=show_pins) self.add_pin('clk', m_inst.get_pin('clkb'), show=show_pins) self.add_pin('clkb', m_inst.get_pin('clk'), show=show_pins) self.add_pin('clkb_hm', m_inst.get_pin('nclk'), label='clkb', show=show_pins) self.add_pin('clk_hm', m_inst.get_pin('nclkb'), label='clk', show=show_pins) # set properties self._sch_params = dict( lch=config['lch'], wp=wp, wn=wn, thp=config['thp'], thn=config['thn'], seg_m=m_master.sch_params['seg_dict'], seg_s=s_master.sch_params['seg_dict'], pass_zero=pass_zero, ) self._seg_in = m_master.seg_in
def draw_diffamp( self, # type: SerdesRXBase col_idx, # type: int seg_dict, # type: Dict[str, int] tr_widths=None, # type: Optional[Dict[str, Dict[int, int]]] tr_spaces=None, # type: Optional[Dict[Union[str, Tuple[str, str]], Dict[int, int]]] tr_indices=None, # type: Optional[Dict[str, int]] fg_min=0, # type: int fg_dum=0, # type: int flip_out_sd=False, # type: bool net_prefix='', # type: str ): # type: (...) -> Tuple[Dict[str, WireArray], Dict[str, Any]] """Draw a differential amplifier. Parameters ---------- col_idx : int the left-most transistor index. 0 is the left-most transistor. seg_dict : Dict[str, int] a dictionary containing number of segments per transistor type. tr_widths : Optional[Dict[str, Dict[int, int]]] the track width dictionary. tr_spaces : Optional[Dict[Union[str, Tuple[str, str]], Dict[int, int]]] the track spacing dictionary. tr_indices : Optional[Dict[str, int]] the track index dictionary. Maps from net name to relative track index. fg_min : int minimum number of total fingers. fg_dum : int minimum single-sided number of dummy fingers. flip_out_sd : bool True to draw output on source instead of drain. net_prefix : str this prefit will be added to net names in draw_mos_conn() method and the returned port dictionary. Returns ------- port_dict : Dict[str, WireArray] a dictionary from connection name to WireArrays on horizontal routing layer. amp_info : Dict[str, Any] the amplifier layout information dictionary """ if tr_widths is None: tr_widths = {} if tr_spaces is None: tr_spaces = {} if tr_indices is None: tr_indices = {} # get layout information amp_info = self._serdes_info.get_diffamp_info(seg_dict, fg_min=fg_min, fg_dum=fg_dum, flip_out_sd=flip_out_sd) fg_tot = amp_info['fg_tot'] fg_single = amp_info['fg_single'] fg_sep = amp_info['fg_sep'] fg_dum = amp_info['fg_dum'] tran_info = amp_info['tran_info'] # draw main transistors and collect ports warr_dict = self._draw_diffamp_mos(col_idx, seg_dict, tran_info, fg_single, fg_dum, fg_sep, net_prefix) # draw load/tail reference transistor for tran_name, mos_type, sup_name in (('tail', 'nch', 'VSS'), ('load', 'pch', 'VDD')): fg_name = '%s_ref' % tran_name fg_ref = seg_dict.get(fg_name, 0) if fg_ref > 0: mos_type, row_idx = self.get_row_index(tran_name) # error checking if (fg_tot - fg_ref) % 2 != 0: raise ValueError( 'fg_tot = %d and fg_%s = %d has opposite parity.' % (fg_tot, fg_name, fg_ref)) # get reference column index col_ref = col_idx + (fg_tot - fg_ref) // 2 # get drain/source name/direction cur_info = tran_info[tran_name] dname, sname, ddir, sdir = cur_info[1:] gname = 'bias_%s' % tran_name if dname == sup_name: sname = gname else: dname = gname # draw transistor warrs = self.draw_mos_conn(mos_type, row_idx, col_ref, fg_ref, sdir, ddir, s_net=net_prefix + sname, d_net=net_prefix + dname) self._append_to_warr_dict(warr_dict, gname, warrs['g']) self._append_to_warr_dict(warr_dict, dname, warrs['d']) self._append_to_warr_dict(warr_dict, sname, warrs['s']) # draw load/tail decap transistor for tran_name, mos_type, sup_name in (('tail', 'nch', 'VSS'), ('load', 'pch', 'VDD')): fg_name = '%s_cap' % tran_name fg_cap = seg_dict.get(fg_name, 0) if fg_cap > 0: mos_type, row_idx = self.get_row_index(tran_name) # compute decap column index fg_row_tot = amp_info['fg_%s_tot' % tran_name] col_l = col_idx + fg_dum + fg_single - fg_row_tot col_r = col_idx + fg_dum + fg_single + fg_sep + fg_row_tot - fg_cap fg_cap_single = fg_cap // 2 p_warrs = self.draw_mos_decap(mos_type, row_idx, col_l, fg_cap_single, False, export_gate=True) n_warrs = self.draw_mos_decap(mos_type, row_idx, col_r, fg_cap_single, False, export_gate=True) gname = 'bias_%s' % tran_name self._append_to_warr_dict(warr_dict, gname, p_warrs['g']) self._append_to_warr_dict(warr_dict, gname, n_warrs['g']) # connect to horizontal wires # nets relative index parameters tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) nets = [ 'outp', 'outn', 'bias_load', 'midp', 'midn', 'bias_casc', 'tail', 'inp', 'inn', 'vddn', 'clk_sw', 'foot', 'enable', 'bias_tail' ] rows = [ 'load', 'load', 'load', 'casc', 'casc', 'casc', 'tail', 'in', 'in', 'sw', 'sw', 'tail', 'en', 'tail' ] trns = [ 'ds', 'ds', 'g', 'ds', 'ds', 'g', 'ds', 'g', 'g', 'ds', 'g', 'ds', 'g', 'g' ] tr_type_dict = dict( outp='out', outn='out', bias_load='bias', midp='mid', midn='mid', bias_casc='bias', tail='tail', inp='in', inn='in', vddn='vdd', clk_sw='bias', foot='tail', enable='bias', bias_tail='bias', ) # tail net should be connected on enable row if it exists if 'enable' in warr_dict: rows[6] = 'en' # compute default inp/inn/outp/outn indices. hm_layer = self.mos_conn_layer + 1 for tran_name, net_type, net_base, order in (('in', 'g', 'in', -1), ('load', 'ds', 'out', 1)): pname, nname = '%sp' % net_base, '%sn' % net_base if pname not in tr_indices or nname not in tr_indices: tr_indices = tr_indices.copy() ntr_used, (netp_idx, netn_idx) = tr_manager.place_wires( hm_layer, [net_base, net_base]) if order < 0: netp_idx, netn_idx = netn_idx, netp_idx mos_type, row_idx = self.get_row_index(tran_name) ntr_tot = self.get_num_tracks(mos_type, row_idx, net_type) if ntr_tot < ntr_used: raise ValueError( 'Need at least %d tracks to draw %s and %s' % (ntr_used, pname, nname)) tr_indices[pname] = netp_idx + (ntr_tot - ntr_used) tr_indices[nname] = netn_idx + (ntr_tot - ntr_used) # connect horizontal wires result = {} inp_tidx, inn_tidx, outp_tidx, outn_tidx = 0, 0, 0, 0 for net_name, row_type, tr_type in zip(nets, rows, trns): if net_name in warr_dict: mos_type, row_idx = self.get_row_index(row_type) tr_w = tr_manager.get_width(hm_layer, tr_type_dict[net_name]) if net_name in tr_indices: # use specified relative index tr_idx = tr_indices[net_name] else: # compute default relative index. Try to use the tracks closest to transistor. ntr_used, (tr_idx, ) = tr_manager.place_wires( hm_layer, [tr_type_dict[net_name]]) ntr_tot = self.get_num_tracks(mos_type, row_idx, tr_type) if ntr_tot < ntr_used: raise ValueError( 'Need at least %d %s tracks to draw %s track' % (ntr_used, tr_type, net_name)) if tr_type == 'g': tr_idx += (ntr_tot - ntr_used) # get track locations and connect if net_name == 'inp': inp_tidx = self.get_track_index(mos_type, row_idx, tr_type, tr_idx) elif net_name == 'inn': inn_tidx = self.get_track_index(mos_type, row_idx, tr_type, tr_idx) elif net_name == 'outp': outp_tidx = self.get_track_index(mos_type, row_idx, tr_type, tr_idx) elif net_name == 'outn': outn_tidx = self.get_track_index(mos_type, row_idx, tr_type, tr_idx) else: tid = self.make_track_id(mos_type, row_idx, tr_type, tr_idx, width=tr_w) result[net_prefix + net_name] = self.connect_to_tracks( warr_dict[net_name], tid) # connect differential input/output inp_warr, inn_warr = self.connect_differential_tracks( warr_dict['inp'], warr_dict['inn'], hm_layer, inp_tidx, inn_tidx, width=tr_manager.get_width(hm_layer, 'in')) outp_warr, outn_warr = self.connect_differential_tracks( warr_dict['outp'], warr_dict['outn'], hm_layer, outp_tidx, outn_tidx, width=tr_manager.get_width(hm_layer, 'out')) result[net_prefix + 'inp'] = inp_warr result[net_prefix + 'inn'] = inn_warr result[net_prefix + 'outp'] = outp_warr result[net_prefix + 'outn'] = outn_warr # connect VDD and VSS self.connect_to_substrate('ptap', warr_dict['VSS']) if 'VDD' in warr_dict: self.connect_to_substrate('ntap', warr_dict['VDD']) # return result return result, amp_info
def draw_layout(self): lch = self.params['lch'] ptap_w = self.params['ptap_w'] ntap_w = self.params['ntap_w'] w_dict = self.params['w_dict'] th_dict = self.params['th_dict'] seg_main = self.params['seg_main'] seg_fb = self.params['seg_fb'] fg_dumr = self.params['fg_dum'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] fg_min = self.params['fg_min'] options = self.params['options'] min_height = self.params['min_height'] sup_tids = self.params['sup_tids'] sch_hp_params = self.params['sch_hp_params'] show_pins = self.params['show_pins'] if options is None: options = {} end_mode = 12 # get track manager and wire names tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) wire_names = { 'tail': dict(g=['clk'], ds=['ntail']), 'nen': dict(g=['en'], ds=['ntail']), 'in': dict(g2=['in', 'in']), 'pen': dict(ds2=['out', 'out'], g=['en', 'en']), 'load': dict(ds=['ptail'], g=['clk', 'clk']), } # get total number of fingers hm_layer = self.mos_conn_layer + 1 top_layer = hm_layer + 1 qdr_info = HybridQDRBaseInfo(self.grid, lch, 0, top_layer=top_layer, end_mode=end_mode, **options) fg_sep_out = qdr_info.get_fg_sep_from_hm_space(tr_manager.get_width( hm_layer, 'out'), round_even=True) # TODO: find this properly. We need to check if there are fg=2, gate on source # TODO: transistors, and if they need larger spacing fg_sep_out = 4 fg_sep_hm = qdr_info.get_fg_sep_from_hm_space(tr_manager.get_width( hm_layer, 1), round_even=True) fg_sep_hm = max(0, fg_sep_hm) main_info = qdr_info.get_integ_amp_info(seg_main, fg_dum=0, fg_sep_hm=fg_sep_hm) fb_info = qdr_info.get_integ_amp_info(seg_fb, fg_dum=0, fg_sep_hm=fg_sep_hm) fg_main = main_info['fg_tot'] fg_amp = fg_main + fb_info['fg_tot'] + fg_sep_out fg_tot = max(fg_min, fg_amp + 2 * fg_dumr) fg_duml = fg_tot - fg_dumr - fg_amp self.draw_rows(lch, fg_tot, ptap_w, ntap_w, w_dict, th_dict, tr_manager, wire_names, top_layer=top_layer, end_mode=end_mode, min_height=min_height, **options) # draw amplifier main_ports, _ = self.draw_integ_amp(fg_duml, seg_main, fg_dum=0, fg_sep_hm=fg_sep_hm) col_main_end = fg_duml + fg_main col_fb = col_main_end + fg_sep_out col_mid = col_main_end + (fg_sep_out // 2) fb_ports, _ = self.draw_integ_amp(col_fb, seg_fb, invert=True, fg_dum=0, fg_sep_hm=fg_sep_hm) w_sup = tr_manager.get_width(hm_layer, 'sup') vss_warrs, vdd_warrs = self.fill_dummy(vdd_width=w_sup, vss_width=w_sup, sup_tids=sup_tids) ports_list = [main_ports, fb_ports] for name in ('outp', 'outn', 'en2', 'clkp', 'clkn'): if name == 'en2': wname = 'pen2' port_name = 'en<2>' else: wname = port_name = name wlist = [p[wname] for p in ports_list if wname in p] cur_warr = self.connect_wires(wlist) self.add_pin(port_name, cur_warr, show=show_pins) for name in ('pen3', 'nen3'): wlist = [p[name] for p in ports_list if name in p] cur_warr = self.connect_wires(wlist) self.add_pin('en<3>', cur_warr, label='en<3>:', show=show_pins) self.add_pin('biasp_m', main_ports['biasp'], show=show_pins) self.add_pin('biasp_f', fb_ports['biasp'], show=show_pins) self.add_pin('inp', main_ports['inp'], show=show_pins) self.add_pin('inn', main_ports['inn'], show=show_pins) self.add_pin('fbp', fb_ports['inp'], show=show_pins) self.add_pin('fbn', fb_ports['inn'], show=show_pins) self.add_pin('VSS', vss_warrs, show=show_pins) self.add_pin('VDD', vdd_warrs, show=show_pins) # do max space fill bnd_box = self.bound_box for lay_id in range(1, hm_layer): self.do_max_space_fill(lay_id, bound_box=bnd_box, fill_pitch=1.5) self.fill_box = bnd_box # set properties self._sch_params = dict( lch=lch, w_dict=w_dict, th_dict=th_dict, seg_main=seg_main, seg_fb=seg_fb, hp_params=sch_hp_params, m_dum_info=self.get_sch_dummy_info(col_start=0, col_stop=col_mid), f_dum_info=self.get_sch_dummy_info(col_start=col_mid, col_stop=None), ) self._fg_tot = fg_tot
def draw_layout(self): lch = self.params['lch'] ptap_w = self.params['ptap_w'] ntap_w = self.params['ntap_w'] w_dict = self.params['w_dict'] th_dict = self.params['th_dict'] seg_dict = self.params['seg_dict'] ndum = self.params['ndum'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] show_pins = self.params['show_pins'] top_layer = self.params['top_layer'] nw_list = [w_dict['n']] pw_list = [w_dict['p']] * 2 nth_list = [th_dict['n']] pth_list = [th_dict['p']] * 2 tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) hm_layer = self.mos_conn_layer + 1 wtr_out = tr_manager.get_width(hm_layer, 'out') wtr_in = tr_manager.get_width(hm_layer, 'in') wtr_en = tr_manager.get_width(hm_layer, 'en') wtr_mid = tr_manager.get_width(hm_layer, 'mid') sp_io = tr_manager.get_space(hm_layer, ('in', 'out')) sp_mid = tr_manager.get_space(hm_layer, ('in', 'mid')) seg_p = seg_dict['p'] seg_n = seg_dict['n'] seg_single = max(seg_p, seg_n) seg_tot = seg_single + 2 * ndum if (seg_p - seg_n) % 4 != 0: raise ValueError( 'We assume seg_p and seg_n differ by multiples of 4.') # draw transistor rows ng_tracks = [wtr_in + sp_io] pg_tracks = [wtr_out, wtr_en] pds_tracks = [sp_mid + wtr_mid, 0] nds_tracks = [0] p_orient = ['R0', 'MX'] n_orient = ['MX'] self.draw_base( lch, seg_tot, ptap_w, ntap_w, nw_list, nth_list, pw_list, pth_list, ng_tracks=ng_tracks, nds_tracks=nds_tracks, pg_tracks=pg_tracks, pds_tracks=pds_tracks, n_orientations=n_orient, p_orientations=p_orient, top_layer=top_layer, ) # draw transistors pidx = ndum + (seg_single - seg_p) // 2 nidx = ndum + (seg_single - seg_n) // 2 p_ports = self.draw_mos_conn('pch', 0, pidx, seg_p, 2, 0, s_net='', d_net='out') n_ports = self.draw_mos_conn('nch', 0, nidx, seg_n, 0, 2, s_net='', d_net='out') # connect supplies self.connect_to_substrate('ntap', p_ports['s']) self.connect_to_substrate('ptap', n_ports['s']) # connect input loc = tr_manager.align_wires(hm_layer, ['in'], ng_tracks[0], alignment=1)[0] tid = self.make_track_id('nch', 0, 'g', loc, width=wtr_in) warr = self.connect_to_tracks([p_ports['g'], n_ports['g']], tid) self.add_pin('in', warr, show=show_pins) # connect output loc = tr_manager.align_wires(hm_layer, ['out'], pg_tracks[0], alignment=1)[0] tid = self.make_track_id('pch', 0, 'g', loc, width=wtr_out) warr = self.connect_to_tracks([p_ports['d'], n_ports['d']], tid) self.add_pin('out', warr, show=show_pins) # draw dummies ptap_wire_arrs, ntap_wire_arrs = self.fill_dummy() # export supplies self.add_pin('VSS', ptap_wire_arrs) self.add_pin('VDD', ntap_wire_arrs) # compute schematic parameters self._sch_params = dict( lch=lch, w_dict=w_dict, th_dict=th_dict, seg_dict=seg_dict, dum_info=self.get_sch_dummy_info(), )
def draw_layout(self): w = self.params['w'] h_unit = self.params['h_unit'] lch = self.params['lch'] ptap_w = self.params['ptap_w'] threshold = self.params['threshold'] top_layer = self.params['top_layer'] nser = self.params['nser'] ndum = self.params['ndum'] in_tr_info = self.params['in_tr_info'] out_tr_info = self.params['out_tr_info'] vdd_tr_info = self.params['vdd_tr_info'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] res_type = self.params['res_type'] res_options = self.params['res_options'] cap_spx = self.params['cap_spx'] cap_spy = self.params['cap_spy'] cap_margin = self.params['cap_margin'] ana_options = self.params['ana_options'] sub_tids = self.params['sub_tids'] show_pins = self.params['show_pins'] tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) sub_tr_w = tr_manager.get_width(top_layer - 1, 'sup') rc_params = dict(w=w, h_unit=h_unit, sub_w=ptap_w, sub_lch=lch, sub_type='ptap', threshold=threshold, top_layer=top_layer, nser=nser, ndum=ndum, in_tr_info=in_tr_info, out_tr_info=out_tr_info, bias_idx=0, vdd_tr_info=vdd_tr_info, res_type=res_type, res_options=res_options, cap_spx=cap_spx, cap_spy=cap_spy, cap_margin=cap_margin, end_mode=12, sub_tr_w=sub_tr_w, sub_tids=sub_tids, show_pins=False) master0 = self.new_template(params=rc_params, temp_cls=HighPassDiff) rc_params['bias_idx'] = 1 master1 = self.new_template(params=rc_params, temp_cls=HighPassDiff) fg_sub = master0.fg_sub # place instances if fg_sub > 0: end_params = dict( lch=lch, fg=fg_sub, sub_type='ptap', threshold=threshold, top_layer=top_layer, end_mode=0b11, guard_ring_nf=0, options=ana_options, ) end_master = self.new_template(params=end_params, temp_cls=AnalogBaseEnd) end_row_box = end_master.array_box bot_inst = self.add_instance(end_master, 'XROWB', loc=(0, 0), unit_mode=True) ycur = end_row_box.top_unit ycur, inst_list = self._place_instances(ycur, master0, master1) ycur += end_row_box.top_unit top_inst = self.add_instance(end_master, 'XROWT', loc=(0, ycur), orient='MX', unit_mode=True) self.fill_box = inst_list[0].bound_box.merge( inst_list[-1].bound_box) bound_box = bot_inst.bound_box.merge(top_inst.bound_box) else: _, inst_list = self._place_instances(0, master0, master1) self.fill_box = bound_box = inst_list[0].bound_box.merge( inst_list[-1].bound_box) vdd_list = [] vss_list = [] vdd_vm_list = [] for idx, inst in enumerate(inst_list): suffix = '<%d>' % idx for name in inst.port_names_iter(): if name == 'VDD': vdd_list.extend(inst.port_pins_iter('VDD')) elif name == 'VSS': vss_list.extend(inst.port_pins_iter('VSS')) elif name == 'VDD_vm': vdd_vm_list.extend(inst.port_pins_iter('VDD_vm')) else: self.reexport(inst.get_port(name), net_name=name + suffix, show=show_pins) vdd_vm_list = self.connect_wires(vdd_vm_list) self.add_pin('VDD', vdd_list, label='VDD', show=show_pins) self.add_pin('VSS', vss_list, label='VSS:', show=show_pins) self.add_pin('VDD_vm', vdd_vm_list, label='VDD', show=show_pins) # set size self.set_size_from_bound_box(top_layer, bound_box) self.array_box = bound_box # set schematic parameters self._sch_params = master0.sch_params.copy()
def draw_layout(self): # Get parameters from the specifications top_layer = self.params['top_layer'] show_pins = self.params['show_pins'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] guard_ring_nf = self.params['guard_ring_nf'] # Set up the sub-instance parameters. Some parametes are shared from the top-hierarchy to the sub-instances params_d2s = self.params['D2S_params'] params_d2s['tr_widths'] = tr_widths params_d2s['tr_spaces'] = tr_spaces params_dtsa = self.params['DTSA_params'] params_dtsa['tr_widths'] = tr_widths params_dtsa['tr_spaces'] = tr_spaces # Define the instance masters master_dtsa = self.new_template(params=params_dtsa, temp_cls=DoubleTailSenseAmplifier) master_d2s = self.new_template(params=params_d2s, temp_cls=DynamicToStaticLatch) # Define horizontal and vertical connection layers. Set up TrackManager horz_conn_layer = self.get_mos_conn_layer() + 1 vert_conn_layer = self.get_mos_conn_layer() + 2 power_grid_layer = self.get_mos_conn_layer() + 3 tr_manager = TrackManager(grid=self.grid, tr_widths=tr_widths, tr_spaces=tr_spaces) # Calculate x placement of the DTSA and D2S based on which is wider. Center the smaller in the x direction. if master_dtsa.bound_box.width_unit > master_d2s.bound_box.width_unit: x_offset_unit_dtsa = 0 x_offset_unit_d2s = (master_dtsa.bound_box.width_unit - master_d2s.bound_box.width_unit) // 2 x_total = master_dtsa.bound_box.width_unit else: x_offset_unit_dtsa = (master_d2s.bound_box.width_unit - master_dtsa.bound_box.width_unit) // 2 x_offset_unit_d2s = 0 x_total = master_d2s.bound_box.width_unit # Place the DTSA: # Find the closest x and y offsets that will result in the instance being placed on-track x_dtsa, y_dtsa = self.place_instance_on_track( master_dtsa, x_offset=x_offset_unit_dtsa, y_offset=0, unit_mode=True, mode=(0, 0), half_track=False) # Instantiate the instance inst_dtsa = self.add_instance(master_dtsa, 'X_DTSA', loc=(x_dtsa, y_dtsa), orient='R0', unit_mode=True) # Place the D2S: Note it will be flipped MX so we to account for that extra y offset x_d2s, y_d2s = self.place_instance_on_track( master_d2s, x_offset=x_offset_unit_d2s, y_offset=inst_dtsa.bound_box.top_unit + master_d2s.bound_box.height_unit, unit_mode=True, mode=(0, 0), half_track=False) inst_d2s = self.add_instance(master_d2s, 'X_D2S', loc=(x_d2s, y_d2s), orient='MX', unit_mode=True) # Set block width of the top level hierarchy [blk_w, blk_h] = self.grid.get_block_size(top_layer, unit_mode=True) right = math.ceil(x_total / blk_w) * blk_w top = math.ceil(inst_d2s.bound_box.top_unit / blk_h) * blk_h bound_box = BBox(left=0, bottom=0, right=right, top=top, resolution=self.grid.resolution, unit_mode=True) self.set_size_from_bound_box(top_layer, bound_box) # Get the output wires of the DTSA and the input wires of the D2S, and connect them warr_dtsa_vop_h = inst_dtsa.get_all_port_pins(name='VOP', layer=horz_conn_layer)[0] warr_dtsa_von_h = inst_dtsa.get_all_port_pins(name='VON', layer=horz_conn_layer)[0] warr_d2s_r_in_v = inst_d2s.get_all_port_pins(name='R', layer=vert_conn_layer) warr_d2s_s_in_v = inst_d2s.get_all_port_pins(name='S', layer=vert_conn_layer) self.connect_differential_wires( pin_warrs=warr_d2s_r_in_v, nin_warrs=warr_d2s_s_in_v, pout_warr=warr_dtsa_von_h, nout_warr=warr_dtsa_vop_h, ) # Reexport the VIP, VIN, DIP DIN, Q, and Q_B pins self.reexport(port=inst_dtsa.get_port('VIP'), show=show_pins) self.reexport(port=inst_dtsa.get_port('VIN'), show=show_pins) self.reexport(port=inst_dtsa.get_port('DIP'), show=show_pins) self.reexport(port=inst_dtsa.get_port('DIN'), show=show_pins) self.reexport(port=inst_d2s.get_port('Q'), show=show_pins) self.reexport(port=inst_d2s.get_port('Q_B'), show=show_pins) self.reexport(port=inst_dtsa.get_port('CLK'), show=show_pins) self.reexport(port=inst_dtsa.get_port('CLK_B'), show=show_pins) # Create list of VSS and VDD wire arrays, from bottom to top of layout warr_vss = [] warr_vdd = [] warr_vss_list = [] warr_vdd_list = [] warr_vss_list.append(inst_dtsa.get_all_port_pins(name='VSS')) warr_vss_list.append(inst_d2s.get_all_port_pins(name='VSS')) warr_vdd_list.append(inst_dtsa.get_all_port_pins(name='VDD')) warr_vdd_list.append(inst_d2s.get_all_port_pins(name='VDD')) for warr_list in warr_vss_list: warr_vss.extend(warr_list) for warr_list in warr_vdd_list: warr_vdd.extend(warr_list) # Extend each horizontal power strap to the width of the block, minus some margin for i, warr in enumerate(warr_vss): warr_vss[i] = self.extend_wires( warr, lower=self.bound_box.left_unit + self.grid.get_track_pitch(vert_conn_layer, unit_mode=True), upper=self.bound_box.right_unit - self.grid.get_track_pitch(vert_conn_layer, unit_mode=True), unit_mode=True)[0] for i, warr in enumerate(warr_vdd): warr_vdd[i] = self.extend_wires( warr, lower=self.bound_box.left_unit + self.grid.get_track_pitch(vert_conn_layer, unit_mode=True), upper=self.bound_box.right_unit - self.grid.get_track_pitch(vert_conn_layer, unit_mode=True), unit_mode=True)[0] # Power grid space / width parameters vert_power_grid_width_tracks = 2 vert_power_grid_space_tracks = 2 horz_power_grid_width_tracks = 2 horz_power_grid_space_tracks = 2 warr_vdd, warr_vss = self.do_power_fill( layer_id=vert_conn_layer, space=2 * self.grid.get_track_pitch(vert_conn_layer, unit_mode=True), space_le=3 * self.grid.get_line_end_space( vert_conn_layer, vert_power_grid_width_tracks, unit_mode=True), vdd_warrs=warr_vdd, vss_warrs=warr_vss, fill_width=vert_power_grid_width_tracks, fill_space=vert_power_grid_space_tracks, x_margin=self.grid.get_track_pitch(vert_conn_layer, unit_mode=True), y_margin=self.grid.get_track_pitch(vert_conn_layer, unit_mode=True), unit_mode=True, min_len=self.grid.get_track_width(power_grid_layer, horz_power_grid_width_tracks, unit_mode=True)) warr_vdd, warr_vss = self.do_power_fill( layer_id=power_grid_layer, space=self.grid.get_track_pitch(power_grid_layer, unit_mode=True), space_le=3 * self.grid.get_line_end_space( power_grid_layer, horz_power_grid_width_tracks, unit_mode=True), vdd_warrs=warr_vdd, vss_warrs=warr_vss, fill_width=horz_power_grid_width_tracks, fill_space=horz_power_grid_space_tracks, x_margin=self.grid.get_track_pitch(power_grid_layer, unit_mode=True), y_margin=self.grid.get_track_pitch(power_grid_layer, unit_mode=True), unit_mode=True, ) self.add_pin('VDD', warr_vdd, show=show_pins) self.add_pin('VSS', warr_vss, show=show_pins) # Assign schematic parameters self._sch_params = dict( DTSA_params=master_dtsa.sch_params, D2S_params=master_d2s.sch_params, )
def draw_layout(self): lch = self.params['lch'] ptap_w = self.params['ptap_w'] ntap_w = self.params['ntap_w'] w_dict = self.params['w_dict'] th_dict = self.params['th_dict'] seg_dict = self.params['seg_dict'] fg_duml = self.params['fg_duml'] fg_dumr = self.params['fg_dumr'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] flip_sign = self.params['flip_sign'] but_sw = self.params['but_sw'] top_layer = self.params['top_layer'] end_mode = self.params['end_mode'] options = self.params['options'] min_height = self.params['min_height'] vss_tid = self.params['vss_tid'] vdd_tid = self.params['vdd_tid'] sch_hp_params = self.params['sch_hp_params'] show_pins = self.params['show_pins'] export_probe = self.params['export_probe'] if options is None: options = {} if but_sw: casc_g = [1, 1] else: casc_g = [1] # get track manager and wire names tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) wire_names = { 'tail': dict(g=['clk'], ds=[1]), 'nen': dict(g=['clk', 'en'], ds=['ntail']), # add a track in input row drain/source for VDD of tail switch 'in': dict(g2=['in', 'in']), 'casc': dict(g=casc_g, ds=['ptail']), 'pen': dict(ds2=['out', 'out'], g=['en', 'en']), 'load': dict(ds=['ptail'], g=['clk', 'clk']), } # get total number of fingers hm_layer = self.mos_conn_layer + 1 if top_layer is None: top_layer = hm_layer fg_amp, fg_sep_hm = self.get_amp_fg_info(self.grid, lch, seg_dict) fg_tot = fg_amp + fg_duml + fg_dumr self.draw_rows(lch, fg_tot, ptap_w, ntap_w, w_dict, th_dict, tr_manager, wire_names, top_layer=top_layer, end_mode=end_mode, min_height=min_height, **options) # draw amplifier ports, _ = self.draw_integ_amp(fg_duml, seg_dict, invert=flip_sign, fg_dum=0, fg_sep_hm=fg_sep_hm) if seg_dict.get('tsw', 0) > 0: clkn_label = 'clkn:' self.add_pin('nclkn', ports['nclkn'], label=clkn_label, show=show_pins) else: clkn_label = 'clkn' w_sup = tr_manager.get_width(hm_layer, 'sup') sup_tids = [None, None] if vss_tid is None: vss_width = w_sup else: sup_tids[0] = vss_tid[0] vss_width = vss_tid[1] if vdd_tid is None: vdd_width = w_sup else: sup_tids[1] = vdd_tid[0] vdd_width = vdd_tid[1] vss_warrs, vdd_warrs = self.fill_dummy(vdd_width=vdd_width, vss_width=vss_width, sup_tids=sup_tids) vss_warr = vss_warrs[0] vdd_warr = vdd_warrs[0] self.add_pin('VSS', vss_warr, show=show_pins) self.add_pin('VDD', vdd_warr, show=show_pins) self._track_info = dict( VSS=(vss_warr.track_id.base_index, vss_warr.track_id.width), VDD=(vdd_warr.track_id.base_index, vdd_warr.track_id.width), ) for name in ('inp', 'inn', 'outp', 'outn', 'biasp'): warr = ports[name] self.add_pin(name, warr, show=show_pins) self._track_info[name] = (warr.track_id.base_index, warr.track_id.width) for name in ('foot', 'tail'): warr = ports[name] self._track_info[name] = (warr.track_id.base_index, warr.track_id.width) if export_probe: self.add_pin(name, warr, show=True) nen3 = ports['nen3'] self.add_pin('nen3', nen3, show=False) self._track_info['nen3'] = (nen3.track_id.base_index, nen3.track_id.width) if 'pen3' in ports: self.add_pin('pen3', ports['pen3'], show=False) self.add_pin('en<3>', nen3, label='en<3>:', show=show_pins) self.add_pin('en<3>', ports['pen3'], label='en<3>:', show=show_pins) else: self.add_pin('en<3>', nen3, show=show_pins) for value in (('pen2', 'en<2>'), ('clkp', 'clkp'), ('clkn', 'clkn', clkn_label), ('casc', 'casc'), ('casc<0>', 'casc<0>'), ('casc<1>', 'casc<1>')): name, port_name = value[:2] if len(value) > 2: label = value[2] else: label = port_name if name in ports: warr = ports[name] self.add_pin(port_name, warr, label=label, show=show_pins) if port_name == 'clkp' or port_name == 'clkn': self._track_info[port_name] = (warr.track_id.base_index, warr.track_id.width) # get intermediate wire intervals lower, upper = None, None for name in ('foot', 'tail', 'pm0p', 'pm0n', 'pm1p', 'pm1n', 'nmp', 'nmn'): if name in ports: warr = ports[name] if lower is None: lower = warr.lower_unit upper = warr.upper_unit else: lower = min(lower, warr.lower_unit) upper = max(upper, warr.upper_unit) self.fill_box = self.bound_box # set schematic parameters and other properties self._sch_params = self._get_sch_params(lch, w_dict, th_dict, seg_dict, sch_hp_params, flip_sign, export_probe) self._fg_tot = fg_tot self._hm_widths = (tr_manager.get_width(hm_layer, 'in'), tr_manager.get_width(hm_layer, 'out')) self._hm_intvs = ((lower, upper), (ports['inp'].lower_unit, ports['inp'].upper_unit), (ports['outp'].lower_unit, ports['outp'].upper_unit))
def draw_layout(self): config = self.params['config'] seg = self.params['seg'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] wp = self.params['wp'] wn = self.params['wn'] row_layout_info = self.params['row_layout_info'] show_pins = self.params['show_pins'] wp_row = config['wp'] wn_row = config['wn'] if wp is None: wp = wp_row if wn is None: wn = wn_row if wp < 0 or wp > wp_row or wn < 0 or wn > wn_row: raise ValueError('Invalid choice of wp and/or wn.') if seg % 2 != 0: raise ValueError('seg = %d must be even.' % seg) vss_tid, vdd_tid = self.setup_floorplan(config, row_layout_info, seg) tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) # get track information hm_layer = self.conn_layer + 1 vm_layer = hm_layer + 1 hm_w_out = tr_manager.get_width(hm_layer, 'out') vm_w_out = tr_manager.get_width(vm_layer, 'out') pg_tid = self.make_track_id(1, 'g', -1, width=hm_w_out) ng_tid = self.make_track_id(0, 'g', -1, width=hm_w_out) pd_tid = self.make_track_id(1, 'gb', 0, width=hm_w_out) nd_tid = self.make_track_id(0, 'gb', 0, width=hm_w_out) mid_tidx = self.grid.get_middle_track(pg_tid.base_index, ng_tid.base_index) mid_tid = TrackID(hm_layer, mid_tidx, width=hm_w_out) vm_coord = self.laygo_info.col_to_coord(seg // 2, unit_mode=True) vm_tid = TrackID(vm_layer, self.grid.coord_to_track(vm_layer, vm_coord, unit_mode=True), width=vm_w_out) # add blocks pmos = self.add_laygo_mos(1, 0, seg, w=wp) nmos = self.add_laygo_mos(0, 0, seg, w=wn) # compute overall block size and fill spaces self.fill_space() # connect wires s_warr = self.connect_to_tracks([pmos['s'], nmos['s']], mid_tid) self.add_pin('s', s_warr, show=show_pins) pd = self.connect_to_tracks(pmos['d'], pd_tid, min_len_mode=0) nd = self.connect_to_tracks(nmos['d'], nd_tid, min_len_mode=0) d_warr = self.connect_to_tracks([pd, nd], vm_tid) self.add_pin('pd', pd, show=False) self.add_pin('nd', nd, show=False) self.add_pin('d', d_warr, show=show_pins) en = self.connect_to_tracks(nmos['g'], ng_tid, min_len_mode=0) enb = self.connect_to_tracks(pmos['g'], pg_tid, min_len_mode=0) self.add_pin('en', en, show=show_pins) self.add_pin('enb', enb, show=show_pins) # draw VDD/VSS sup_w = vss_tid.width xl, xr = self.bound_box.left_unit, self.bound_box.right_unit vss = self.add_wires(hm_layer, vss_tid.base_index, xl, xr, width=sup_w, unit_mode=True) vdd = self.add_wires(hm_layer, vdd_tid.base_index, xl, xr, width=sup_w, unit_mode=True) self.add_pin('VSS', vss, show=show_pins) self.add_pin('VDD', vdd, show=show_pins) # set properties self._sch_params = dict( lch=config['lch'], wp=wp, wn=wn, thp=config['thp'], thn=config['thn'], segp=seg, segn=seg, )
def draw_layout(self): """Draw the layout of a dynamic latch chain. """ # type: () -> None lch = self.params['lch'] ptap_w = self.params['ptap_w'] ntap_w = self.params['ntap_w'] w_dict = self.params['w_dict'] th_dict = self.params['th_dict'] seg_dict = self.params['seg_dict'] ndum = self.params['ndum'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] show_pins = self.params['show_pins'] guard_ring_nf = self.params['guard_ring_nf'] top_layer = self.params['top_layer'] tech_cls_name = self.params['tech_cls_name'] if tech_cls_name is not None: self.set_tech_class(tech_cls_name) ana_info = AnalogBaseInfo(self.grid, lch, guard_ring_nf, top_layer=top_layer, tech_cls_name=tech_cls_name) min_fg_sep = ana_info.min_fg_sep # calculate total number of fingers seg_ptail = seg_dict['ptail'] seg_ntail = seg_dict['ntail'] seg_pin = seg_dict['pin'] seg_nin = seg_dict['nin'] fg_ptail = seg_ptail fg_ntail = seg_ntail fg_pin = seg_pin fg_nin = seg_nin # error checking if fg_pin % 2 != 0 or fg_nin % 2 != 0 or fg_ptail % 2 != 0 or fg_ntail % 2 != 0: raise ValueError('Only even number of fingers are supported.') if (fg_pin - fg_nin) % 4 != 0: raise ValueError('This code now only works if fg_pin and fg_nin differ by ' 'multiples of 4.') fg_single_in = max(fg_pin, fg_nin) fg_single = max(fg_ptail, fg_ntail, fg_pin, fg_nin) in_mid_idx = ndum + fg_single - fg_single_in // 2 fg_tot = 2 * (fg_single + ndum) + min_fg_sep ndum_pin = in_mid_idx - fg_pin // 2 ndum_nin = in_mid_idx - fg_nin // 2 if fg_ntail <= fg_nin: ndum_ntail = ndum_nin + fg_nin - fg_ntail else: ndum_ntail = min(ndum_nin, ndum + fg_single - fg_ntail) if fg_ptail <= fg_pin: ndum_ptail = ndum_pin + fg_pin - fg_ptail else: ndum_ptail = min(ndum_pin, ndum + fg_single - fg_ptail) # get width/threshold/orientation info nw_list = [w_dict['ntail'], w_dict['nin']] nth_list = [th_dict['ntail'], th_dict['nin']] n_orientations = ['MX', 'MX'] pw_list = [w_dict['pin'], w_dict['ptail']] pth_list = [th_dict['pin'], th_dict['ptail']] p_orientations = ['R0', 'R0'] # get tracks information tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) hm_layer = self.mos_conn_layer + 1 # allocate tracks wire_names = dict( nch=[ dict(g=['bias'], ds=['tail']), dict(g=['in', 'out'], ds=[]), ], pch=[ dict(g=['in'], ds=[]), dict(g=['bias'], ds=['tail']), ], ) # draw base self.draw_base(lch, fg_tot, ptap_w, ntap_w, nw_list, nth_list, pw_list, pth_list, tr_manager=tr_manager, wire_names=wire_names, n_orientations=n_orientations, p_orientations=p_orientations, guard_ring_nf=guard_ring_nf, top_layer=top_layer) # draw transistors ntaill = self.draw_mos_conn('nch', 0, ndum_ntail, fg_ntail, 2, 0, s_net='ntail', d_net='') ntailr = self.draw_mos_conn('nch', 0, fg_tot - ndum_ntail - fg_ntail, fg_ntail, 2, 0, s_net='ntail', d_net='') ptaill = self.draw_mos_conn('pch', 1, ndum_ptail, fg_ptail, 0, 2, s_net='ptail', d_net='') ptailr = self.draw_mos_conn('pch', 1, fg_tot - ndum_ptail - fg_ptail, fg_ptail, 0, 2, s_net='ptail', d_net='') if (ndum_nin - ndum_ntail) % 2 == 1: tail_nin_port, out_nin_port = 'd', 's' sdir, ddir = 2, 0 s_netl, s_netr, d_netl, d_netr = 'outn', 'outp', 'ntail', 'ntail' else: tail_nin_port, out_nin_port = 's', 'd' sdir, ddir = 0, 2 s_netl, s_netr, d_netl, d_netr = 'ntail', 'ntail', 'outn', 'outp' ninl = self.draw_mos_conn('nch', 1, ndum_nin, fg_nin, sdir, ddir, s_net=s_netl, d_net=d_netl) ninr = self.draw_mos_conn('nch', 1, fg_tot - ndum_nin - fg_nin, fg_nin, sdir, ddir, s_net=s_netr, d_net=d_netr) if (ndum_pin - ndum_ptail) % 2 == 1: tail_pin_port, out_pin_port = 'd', 's' sdir, ddir = 0, 2 s_netl, s_netr, d_netl, d_netr = 'outn', 'outp', 'ptail', 'ptail' else: tail_pin_port, out_pin_port = 's', 'd' sdir, ddir = 2, 0 s_netl, s_netr, d_netl, d_netr = 'ptail', 'ptail', 'outn', 'outp' pinl = self.draw_mos_conn('pch', 0, ndum_pin, fg_pin, sdir, ddir, s_net=s_netl, d_net=d_netl) pinr = self.draw_mos_conn('pch', 0, fg_tot - ndum_pin - fg_pin, fg_pin, sdir, ddir, s_net=s_netr, d_net=d_netr) # draw connections # VDD/VSS self.connect_to_substrate('ptap', [ntaill['d'], ntailr['d']]) self.connect_to_substrate('ntap', [ptaill['d'], ptailr['d']]) # NMOS/PMOS tail tail_tid = self.get_wire_id('nch', 0, 'ds', wire_name='tail') self.connect_to_tracks([ntaill['s'], ntailr['s'], ninl[tail_nin_port], ninr[tail_nin_port]], tail_tid) tail_tid = self.get_wire_id('pch', 1, 'ds', wire_name='tail') self.connect_to_tracks([ptaill['s'], ptailr['s'], pinl[tail_pin_port], pinr[tail_pin_port]], tail_tid) # NMOS/PMOS tail bias bn_tid = self.get_wire_id('nch', 0, 'g', wire_name='bias') bp_tid = self.get_wire_id('pch', 1, 'g', wire_name='bias') self.connect_to_tracks([ntaill['g'], ntailr['g'], ninl['d'], pinl['d']], bn_tid) self.connect_to_tracks([ptaill['g'], ptailr['g'], ninl['d'], pinl['d']], bp_tid) # input/output inn_tid = self.get_wire_id('nch', 1, 'g', wire_name='in') inp_tid = self.get_wire_id('pch', 0, 'g', wire_name='in') inp_idx = inp_tid.base_index inn_idx = inn_tid.base_index in_sum2 = int(round(2 * (inp_idx + inn_idx))) if in_sum2 % 2 == 1: # move inp_idx down so output is centered wrt inputs inp_idx -= 0.5 in_sum2 -= 1 out_tid = TrackID(hm_layer, in_sum2 / 4, width=tr_manager.get_width(hm_layer, 'out')) # connect input/output inp, inn = self.connect_differential_tracks([ninl['g'], pinl['g']], [ninr['g'], pinr['g']], hm_layer, inp_idx, inn_idx, width=inn_tid.width) out = self.connect_to_tracks([ninr[out_nin_port], pinr[out_pin_port]], out_tid, min_len_mode=0) # fill dummies tr_w = tr_manager.get_width(hm_layer, 'sup') vss_warrs, vdd_warrs = self.fill_dummy(vdd_width=tr_w, vss_width=tr_w) # add pins self.add_pin('inp', inp, show=show_pins) self.add_pin('inn', inn, show=show_pins) self.add_pin('out', out, show=show_pins) self.add_pin('VSS', vss_warrs, show=show_pins) self.add_pin('VDD', vdd_warrs, show=show_pins) # compute schematic parameters self._sch_params = dict( lch=lch, w_dict=w_dict, th_dict=th_dict, seg_dict=seg_dict, dum_info=self.get_sch_dummy_info(), )
def draw_layout(self): """Draw the layout of a dynamic latch chain. """ # type: () -> None lch = self.params['lch'] ptap_w = self.params['ptap_w'] ntap_w = self.params['ntap_w'] w_dict = self.params['w_dict'] th_dict = self.params['th_dict'] seg_dict = self.params['seg_dict'] stack_dict = self.params['stack_dict'] ndum = self.params['ndum'] tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] show_pins = self.params['show_pins'] guard_ring_nf = self.params['guard_ring_nf'] top_layer = self.params['top_layer'] ana_info = AnalogBaseInfo(self.grid, lch, guard_ring_nf, top_layer=top_layer) min_fg_sep = ana_info.min_fg_sep # calculate total number of fingers seg_tail1 = seg_dict['tail1'] seg_tail2 = seg_dict['tail2'] seg_tailcm = seg_dict['tailcm'] seg_in = seg_dict['in'] seg_ref = seg_dict['ref'] seg_diode1 = seg_dict['diode1'] seg_ngm1 = seg_dict['ngm1'] seg_diode2 = seg_dict['diode2'] seg_ngm2 = seg_dict['ngm2'] stack_tail = stack_dict['tail'] stack_in = stack_dict['in'] stack_diode = stack_dict['diode'] stack_ngm = stack_dict['ngm'] fg_tail1 = seg_tail1 * stack_tail fg_tail2 = seg_tail2 * stack_tail fg_tailcm = seg_tailcm * stack_tail fg_in = seg_in * stack_in fg_diode1 = seg_diode1 * stack_diode fg_ngm1 = seg_ngm1 * stack_ngm fg_diode2 = seg_diode2 * stack_diode fg_ngm2 = seg_ngm2 * stack_ngm fg_ref = seg_ref * stack_in fg_load = fg_diode1 + fg_ngm1 fg_in2 = fg_diode2 + fg_ngm2 fg_bias2 = fg_tail2 + fg_tailcm # error checking if fg_tail1 != fg_in: raise ValueError('This template assumes fg_tail = fg_in') if stack_tail % stack_in != 0 and stack_in % stack_tail != 0: raise ValueError('one of stack_tail/stack_in must divide the other.') if stack_ngm % stack_in != 0 and stack_in % stack_ngm != 0: raise ValueError('one of stack_ngm/stack_in must divide the other.') fg_single1 = max(fg_in, fg_load) fg_single2 = max(fg_bias2, fg_in2) fg_tot = 2 * (fg_single1 + fg_single2 + ndum) + 4 * min_fg_sep + fg_ref # get width/threshold/orientation info nw_list = [w_dict['load']] nth_list = [th_dict['load']] n_orientations = ['MX'] pw_list = [w_dict['in'], w_dict['tail']] pth_list = [th_dict['in'], th_dict['tail']] p_orientations = ['MX', 'MX'] # get tracks information tr_manager = TrackManager(self.grid, tr_widths, tr_spaces) hm_layer = self.get_mos_conn_layer(self.grid.tech_info) + 1 vm_layer = hm_layer + 1 # allocate tracks wire_names = dict( nch=[ dict(ds=[], g=['out', 'out']), ], pch=[ dict(ds=['bias', 'tail'], g=['in', 'in', 'in', 'in']), dict(ds=['tail'], g=['bias', 'bias']), ], ) # draw base self.draw_base(lch, fg_tot, ptap_w, ntap_w, nw_list, nth_list, pw_list, pth_list, tr_manager=tr_manager, wire_names=wire_names, n_orientations=n_orientations, p_orientations=p_orientations, guard_ring_nf=guard_ring_nf, top_layer=top_layer) # draw stage1 transistors col_left = ndum + fg_single2 + fg_single1 + min_fg_sep col_right = col_left + 2 * min_fg_sep + fg_ref diode1l = self.draw_mos_conn('nch', 0, col_left - fg_load, fg_diode1, 2, 0, stack=stack_diode, s_net='midn', d_net='') ngm1l = self.draw_mos_conn('nch', 0, col_left - fg_ngm1, fg_ngm1, 2, 0, stack=stack_ngm, s_net='midn', d_net='') ngm1r = self.draw_mos_conn('nch', 0, col_right, fg_ngm1, 2, 0, stack=stack_ngm, s_net='midp', d_net='') diode1r = self.draw_mos_conn('nch', 0, col_right + fg_ngm1, fg_diode1, 2, 0, stack=stack_diode, s_net='midp', d_net='') inl = self.draw_mos_conn('pch', 0, col_left - fg_in, fg_in, 0, 2, stack=stack_in, s_net='midn', d_net='tail') inm = self.draw_mos_conn('pch', 0, col_left + min_fg_sep, fg_ref, 0, 2, stack=stack_in, s_net='bias', d_net='tail_ref') inr = self.draw_mos_conn('pch', 0, col_right, fg_in, 0, 2, stack=stack_in, s_net='midp', d_net='tail') bias1l = self.draw_mos_conn('pch', 1, col_left - fg_tail1, fg_tail1, 2, 0, stack=stack_tail, s_net='', d_net='tail') biasm = self.draw_mos_conn('pch', 1, col_left + min_fg_sep, fg_ref, 2, 0, stack=stack_tail, s_net='', d_net='tail_ref') bias1r = self.draw_mos_conn('pch', 1, col_right, fg_tail1, 2, 0, stack=stack_tail, s_net='', d_net='tail') # draw stage2 transistors col_left = ndum + fg_single2 col_right += fg_single1 + min_fg_sep diode2l = self.draw_mos_conn('nch', 0, col_left - fg_in2, fg_diode2, 0, 2, stack=stack_diode, s_net='', d_net='outp') ngm2l = self.draw_mos_conn('nch', 0, col_left - fg_ngm2, fg_ngm2, 0, 2, stack=stack_ngm, s_net='', d_net='outp') ngm2r = self.draw_mos_conn('nch', 0, col_right, fg_ngm2, 0, 2, stack=stack_ngm, s_net='', d_net='outn') diode2r = self.draw_mos_conn('nch', 0, col_right + fg_ngm2, fg_diode2, 0, 2, stack=stack_diode, s_net='', d_net='outn') cm2l = self.draw_mos_conn('pch', 1, col_left - fg_bias2, fg_tailcm, 2, 0, stack=stack_tail, s_net='', d_net='outp') bias2l = self.draw_mos_conn('pch', 1, col_left - fg_tail2, fg_tail2, 2, 0, stack=stack_tail, s_net='', d_net='outp') bias2r = self.draw_mos_conn('pch', 1, col_right, fg_tail2, 2, 0, stack=stack_tail, s_net='', d_net='outn') cm2r = self.draw_mos_conn('pch', 1, col_right + fg_tail2, fg_tailcm, 2, 0, stack=stack_tail, s_net='', d_net='outn') # get track locations midp_tid = self.get_wire_id('nch', 0, 'g', wire_idx=1) midn_tid = self.get_wire_id('nch', 0, 'g', wire_idx=0) tail2_tid = self.get_wire_id('pch', 0, 'ds', wire_name='tail') bias2_tid = self.get_wire_id('pch', 0, 'ds', wire_name='bias') inc1_tid = self.get_wire_id('pch', 0, 'g', wire_idx=3) inp_tid = self.get_wire_id('pch', 0, 'g', wire_idx=2) inn_tid = self.get_wire_id('pch', 0, 'g', wire_idx=1) inc2_tid = self.get_wire_id('pch', 0, 'g', wire_idx=0) tail1_tid = self.get_wire_id('pch', 1, 'ds', wire_name='tail') cm_tid = self.get_wire_id('pch', 1, 'g', wire_idx=1) bias1_tid = self.get_wire_id('pch', 1, 'g', wire_idx=0) w_bias = cm_tid.width w_tail = tail1_tid.width w_out = midp_tid.width w_in = inp_tid.width # draw connections # connect VDD/VSS self.connect_to_substrate('ntap', [bias1l['s'], biasm['s'], bias1r['s'], cm2l['s'], cm2r['s'], bias2l['s'], bias2r['s']]) self.connect_to_substrate('ptap', [diode1l['d'], ngm1l['d'], ngm1r['d'], diode1r['d'], diode2l['s'], ngm2l['s'], ngm2r['s'], diode2r['s']]) # connect bias/tail wires self.connect_differential_tracks([inl['d'], inr['d'], bias1l['d'], bias1r['d']], [biasm['d'], inm['d']], hm_layer, tail2_tid.base_index, tail1_tid.base_index, width=w_tail) bias2 = self.connect_to_tracks(inm['s'], bias2_tid) bias1_warrs = [bias1l['g'], biasm['g'], bias1r['g'], bias2l['g'], bias2r['g']] cm_warrs = [cm2l['g'], cm2r['g']] bias1, cmbias = self.connect_differential_tracks(bias1_warrs, cm_warrs, hm_layer, bias1_tid.base_index, cm_tid.base_index, width=w_bias) mid_tid = self.grid.coord_to_nearest_track(vm_layer, bias1.middle) bias_vtid = TrackID(vm_layer, mid_tid, width=tr_manager.get_width(vm_layer, 'bias')) bias = self.connect_to_tracks([bias1, bias2], bias_vtid) # connect middle wires midp_warrs = [inr['s'], ngm1r['s'], diode1r['s'], diode1r['g'], ngm1l['g'], diode2r['g'], ngm2r['g']] midn_warrs = [inl['s'], ngm1l['s'], diode1l['s'], diode1l['g'], ngm1r['g'], diode2l['g'], ngm2l['g']] midp, midn = self.connect_differential_tracks(midp_warrs, midn_warrs, hm_layer, midp_tid.base_index, midn_tid.base_index, width=w_out) # connect inputs inc1_warrs = inc2_warrs = inm['g'] inp_warrs = inl['g'] inn_warrs = inr['g'] inp_tidx, inn_tidx = inp_tid.base_index, inn_tid.base_index inc1, inp, inn, inc2 = self.connect_matching_tracks( [inc1_warrs, inp_warrs, inn_warrs, inc2_warrs], hm_layer, [inc1_tid.base_index, inp_tidx, inn_tidx, inc2_tid.base_index], width=w_in) # connect outputs out_tidx = self.grid.get_middle_track(inp_tidx, inn_tidx) outp = self.connect_to_tracks([diode2l['d'], ngm2l['d'], bias2l['d'], cm2l['d']], TrackID(hm_layer, out_tidx, width=w_out)) outn = self.connect_to_tracks([diode2r['d'], ngm2r['d'], bias2r['d'], cm2r['d']], TrackID(hm_layer, out_tidx, width=w_out)) # fill dummies sup_width = tr_manager.get_width(hm_layer, 'sup') vss_warrs, vdd_warrs = self.fill_dummy(vdd_width=sup_width, vss_width=sup_width) # add pins self.add_pin('ref', [inc1, inc2], show=show_pins) self.add_pin('inp', inp, show=show_pins) self.add_pin('inn', inn, show=show_pins) self.add_pin('bias', bias, show=show_pins) self.add_pin('cmbias', cmbias, show=show_pins) self.add_pin('midp', midp, show=show_pins) self.add_pin('midn', midn, show=show_pins) self.add_pin('outp', outp, show=show_pins) self.add_pin('outn', outn, show=show_pins) self.add_pin('VSS', vss_warrs, show=show_pins) self.add_pin('VDD', vdd_warrs, show=show_pins) # compute schematic parameters self._sch_params = dict( lch=lch, w_dict=w_dict, th_dict=th_dict, seg_dict=seg_dict, stack_dict=stack_dict, dum_info=self.get_sch_dummy_info(), )
def draw_layout(self): # get parameters tr_widths = self.params['tr_widths'] tr_spaces = self.params['tr_spaces'] show_pins = self.params['show_pins'] tr_manager = TrackManager(self.grid, tr_widths, tr_spaces, half_space=True) l_master, m_master = self._make_masters(tr_manager) ml_margin, mr_margin = m_master.layout_info.edge_margins m_arr_box = m_master.array_box m_bnd_box = m_master.bound_box # place instances top_layer = m_master.top_layer m_inst = self.add_instance(m_master, 'XMAIN', loc=(0, 0), unit_mode=True) y_lat = m_arr_box.top_unit + l_master.array_box.top_unit x_lat = m_bnd_box.right_unit - mr_margin - l_master.array_box.right_unit l_inst = self.add_instance(l_master, 'XLAT', loc=(x_lat, y_lat), orient='MX', unit_mode=True) self._div_grp_loc = (ml_margin, m_arr_box.top_unit) # set size l_bnd_box = l_inst.bound_box self.array_box = m_arr_box.extend(y=l_inst.array_box.top_unit, unit_mode=True) self.fill_box = bnd_box = m_bnd_box.extend(y=l_bnd_box.top_unit, unit_mode=True) self.set_size_from_bound_box(top_layer, bnd_box) self.add_cell_boundary(bnd_box) # export pins in-place exp_list = [ (m_inst, 'outp', 'outp_m', True), (m_inst, 'outn', 'outn_m', True), (m_inst, 'inp', 'inp', False), (m_inst, 'inn', 'inn', False), (m_inst, 'fbp', 'fbp', False), (m_inst, 'fbn', 'fbn', False), (m_inst, 'en<3>', 'en<3>', True), (m_inst, 'en<2>', 'en<2>', True), (m_inst, 'VDD', 'VDD', True), (m_inst, 'VSS', 'VSS', True), (m_inst, 'clkp', 'clkp', True), (m_inst, 'clkn', 'clkn', True), (m_inst, 'biasp_m', 'biasp_m', False), (m_inst, 'biasp_f', 'biasp_f', False), (l_inst, 'inp', 'outp_m', True), (l_inst, 'inn', 'outn_m', True), (l_inst, 'outp', 'outp_d', False), (l_inst, 'outn', 'outn_d', False), (l_inst, 'en<3>', 'en<2>', True), (l_inst, 'en<2>', 'en<1>', False), (l_inst, 'clkp', 'clkn', True), (l_inst, 'clkn', 'clkp', True), (l_inst, 'VDD', 'VDD', True), (l_inst, 'VSS', 'VSS', True), (l_inst, 'biasp', 'biasn_d', False), ] for inst, port_name, name, vconn in exp_list: port = inst.get_port(port_name) label = name + ':' if vconn else name self.reexport(port, net_name=name, label=label, show=show_pins) if inst is m_inst and (port_name == 'outp' or port_name == 'outn'): self.reexport(port, net_name=port_name + '_main', show=False) self._en_locs = self._get_en_locs(l_inst, tr_manager) for lay_id in range(1, top_layer - 1): self.do_max_space_fill(lay_id, bound_box=l_bnd_box, fill_pitch=1.5) # set schematic parameters l_outp_tid = l_inst.get_pin('outp').track_id self._sch_params = dict( sum_params=m_master.sch_params, lat_params=l_master.sch_params, ) self._fg_tot = m_master.fg_tot self._data_tr_info = (l_outp_tid.base_index, l_inst.get_pin('outn').track_id.base_index, l_outp_tid.width) m_tr_info = l_master.track_info en3_info = m_tr_info['nen3'] tr_info = dict( VDD=m_tr_info['VDD'], VSS=m_tr_info['VSS'], q=m_tr_info['inp'], qb=m_tr_info['inn'], en=(en3_info[0] - 2, en3_info[1]), clkp=m_tr_info['clkp'], clkn=m_tr_info['clkn'], inp=m_tr_info['inp'], inn=m_tr_info['inn'], outp=m_tr_info['outp'], outn=m_tr_info['outn'], foot=m_tr_info['foot'], tail=m_tr_info['tail'], ) self._div_tr_info = tr_info self._sum_row_info = m_master.row_layout_info self._lat_row_info = l_master.row_layout_info self._left_edge_info = l_master.lr_edge_info[0]